Skip to content

Commit a7ffdcf

Browse files
committed
fix(superdoc): preserve public Config.user.color typing via AwarenessUser
Pre-PR, the SuperDoc-local public User had color?: string so consumers could pass { user: { ..., color } } via Config and runtime would honor it (SuperDoc#assignUserColor() skips hash-assignment when user.color is already set). After unifying User to the super-editor symbol (which has no color), Config.user?: User silently narrowed: consumers passing an explicit color now fail TS typecheck even though runtime still accepts it. Fix by typing Config.user against AwarenessUser (which extends User with the optional color), and adding AwarenessUser to the public facade + classification snapshot so it's a first-class supported-root export. addSharedUser / removeSharedUser still accept the narrower User parameter; only Config.user is widened. Empirically verified: a consumer fixture passing { user: { name, email, color: '#ff0000' } } typechecks against the packed tarball post-fix; same fixture fails on the previous commit.
1 parent fa52f5d commit a7ffdcf

7 files changed

Lines changed: 56 additions & 30 deletions

File tree

packages/superdoc/src/core/types/index.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1397,8 +1397,14 @@ export interface Config {
13971397
password?: string;
13981398
/** The documents to load → soon to be deprecated. */
13991399
documents?: Document[];
1400-
/** The current user of this SuperDoc. */
1401-
user?: User;
1400+
/**
1401+
* The current user of this SuperDoc. Typed as `AwarenessUser` (an
1402+
* extension of `User` with the optional `color` field) so consumers
1403+
* can pass an explicit awareness color and have the runtime honor it
1404+
* as an override - `SuperDoc#assignUserColor()` skips its hash-based
1405+
* assignment when `user.color` is already set.
1406+
*/
1407+
user?: AwarenessUser;
14021408
/** All users of this SuperDoc (can be used for "@"-mentions). */
14031409
users?: User[];
14041410
/** Colors to use for user awareness. */

packages/superdoc/src/public/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export { createTheme } from '../core/theme/create-theme.js';
5858

5959
// Type source: ./core/types/index.js
6060
export type { AwarenessState } from '../core/types/index.js';
61+
export type { AwarenessUser } from '../core/types/index.js';
6162
export type { BlockNavigationAddress } from '../core/types/index.js';
6263
export type { BookmarkAddress } from '../core/types/index.js';
6364
export type { CollaborationConfig } from '../core/types/index.js';

tests/consumer-typecheck/snapshots/superdoc-root-classification.json

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
{
22
"generatedAt": "2026-05-19T11:33:50.546Z",
33
"summary": {
4-
"total": 204,
4+
"total": 205,
55
"byBucket": {
66
"legacy-root": 60,
77
"internal-candidate": 8,
8-
"supported-root": 136
8+
"supported-root": 137
99
},
1010
"byConfidence": {
1111
"high": 102,
12-
"medium": 100,
12+
"medium": 101,
1313
"low": 2
1414
}
1515
},
@@ -47,6 +47,17 @@
4747
"inEsm": false,
4848
"inCjs": false
4949
},
50+
{
51+
"name": "AwarenessUser",
52+
"bucket": "supported-root",
53+
"rationale": "Collaboration/awareness type defined in core/types/index.ts. Extends User with an optional `color` field for consumer-supplied awareness color; typed on Config.user so the runtime override in SuperDoc#assignUserColor() is consumer-typable.",
54+
"confidence": "medium",
55+
"source": "collab",
56+
"inDts": true,
57+
"inDcts": true,
58+
"inEsm": false,
59+
"inCjs": false
60+
},
5061
{
5162
"name": "BinaryData",
5263
"bucket": "supported-root",
@@ -754,7 +765,7 @@
754765
{
755766
"name": "FlowBlock",
756767
"bucket": "legacy-root",
757-
"rationale": "In LayoutState.blocks. Layout-engine raw type that must not appear in public .d.ts per package-boundaries.md:64 already leaks via PE closure.",
768+
"rationale": "In LayoutState.blocks. Layout-engine raw type that must not appear in public .d.ts per package-boundaries.md:64 \u2014 already leaks via PE closure.",
758769
"confidence": "high",
759770
"source": "locked",
760771
"inDts": true,
@@ -842,7 +853,7 @@
842853
{
843854
"name": "Layout",
844855
"bucket": "legacy-root",
845-
"rationale": "In PE.onLayoutUpdated payload (LayoutState & { layout: Layout; ... }). Layout-engine raw type that must not appear in public .d.ts per package-boundaries.md:64 already leaks via PE legacy API.",
856+
"rationale": "In PE.onLayoutUpdated payload (LayoutState & { layout: Layout; ... }). Layout-engine raw type that must not appear in public .d.ts per package-boundaries.md:64 \u2014 already leaks via PE legacy API.",
846857
"confidence": "high",
847858
"source": "locked",
848859
"inDts": true,

tests/consumer-typecheck/snapshots/superdoc-root-classification.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,27 @@
11
# SD-3212 A1 — root classification
22

3-
Generated: 2026-05-19T11:33:50.546Z
4-
Input: tests/consumer-typecheck/snapshots/superdoc-root-exports.json (200 names, locked baseline)
3+
Generated: 2026-05-25T00:00:00.000Z
4+
Input: tests/consumer-typecheck/snapshots/superdoc-root-exports.json (205 names, locked baseline)
55

66
## Summary
77

88
| Bucket | Count |
99
|---|---|
10-
| supported-root | 132 |
10+
| supported-root | 137 |
1111
| legacy-root | 60 |
1212
| move-to-subpath | 0 |
1313
| internal-candidate | 8 |
1414
| NEEDS-REVIEW | 0 |
15-
| **total** | **200** |
15+
| **total** | **205** |
1616

17-
Confidence: high=98, medium=100, needs-review=0.
17+
Confidence: high=102, medium=101, needs-review=0.
1818

19-
## supported-root (132)
19+
## supported-root (137)
2020

2121
| Name | Confidence | Source | Rationale |
2222
|---|---|---|---|
2323
| `AwarenessState` | medium | collab | Collaboration/awareness type defined in core/types/index.ts. Customer-facing for collab-provider integrations (e.g., AwarenessState types the documented onAwarenessUpdate callback). |
24+
| `AwarenessUser` | medium | collab | Collaboration/awareness type defined in core/types/index.ts. Extends User with an optional `color` field for consumer-supplied awareness color; typed on Config.user so the runtime override in SuperDoc#assignUserColor() is consumer-typable. |
2425
| `BinaryData` | high | locked | Shape of binary content used in documented import/export/open/save paths. Type-reachable through documented APIs. |
2526
| `BlockNavigationAddress` | high | doc-api | Document API navigation/address/selection type. Promoted into the root facade by SD-3185. |
2627
| `BlocksListResult` | high | doc-api | Document API navigation/address/selection type. Promoted into the root facade by SD-3185. |

tests/consumer-typecheck/snapshots/superdoc-root-exports.json

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"generatedAt": "2026-05-23T12:40:30.787Z",
2+
"generatedAt": "2026-05-25T11:25:51.224Z",
33
"ticket": "SD-3212 PR A0",
44
"package": "superdoc",
55
"rootExport": {
@@ -17,6 +17,7 @@
1717
"AIWriter",
1818
"AnnotatorHelpers",
1919
"AwarenessState",
20+
"AwarenessUser",
2021
"BinaryData",
2122
"BlankDOCX",
2223
"BlockNavigationAddress",
@@ -227,6 +228,7 @@
227228
"AIWriter",
228229
"AnnotatorHelpers",
229230
"AwarenessState",
231+
"AwarenessUser",
230232
"BinaryData",
231233
"BlankDOCX",
232234
"BlockNavigationAddress",
@@ -527,11 +529,11 @@
527529
}
528530
},
529531
"counts": {
530-
"types.import": 204,
531-
"types.require": 204,
532+
"types.import": 205,
533+
"types.require": 205,
532534
"import": 41,
533535
"require": 41,
534-
"union": 204
536+
"union": 205
535537
},
536538
"divergences": {
537539
"typesImportVsRequire": {
@@ -545,6 +547,7 @@
545547
"typesVsRuntime": {
546548
"typedOnly": [
547549
"AwarenessState",
550+
"AwarenessUser",
548551
"BinaryData",
549552
"BlockNavigationAddress",
550553
"BlocksListResult",

tests/consumer-typecheck/snapshots/superdoc-root-exports.md

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,31 @@
11
# superdoc root export inventory (SD-3212 PR A0)
22

3-
Generated: 2026-05-23T12:40:30.787Z
3+
Generated: 2026-05-25T11:25:51.224Z
44
Source: packed and installed `tests/consumer-typecheck/node_modules/superdoc`
55

66
## Counts
77

88
| Source | Path | Count |
99
|---|---|---|
10-
| types.import | `./dist/superdoc/src/public/index.d.ts` | 204 |
11-
| types.require | `./dist/superdoc/src/public/index.d.cts` | 204 |
10+
| types.import | `./dist/superdoc/src/public/index.d.ts` | 205 |
11+
| types.require | `./dist/superdoc/src/public/index.d.cts` | 205 |
1212
| import | `./dist/superdoc.es.js` | 41 |
1313
| require | `./dist/superdoc.cjs` | 41 |
14-
| **union** | | **204** |
14+
| **union** | | **205** |
1515

1616
## Divergences
1717

1818
- types.import only (not in types.require): 0
1919
- types.require only (not in types.import): 0
2020
- ESM only (not in CJS): 0
2121
- CJS only (not in ESM): 0
22-
- typed but no runtime export (phantom risk): 163
22+
- typed but no runtime export (phantom risk): 164
2323
- runtime export but not typed (silent shadow on root): 0
2424

2525
### Type-only names (no runtime)
2626

2727
- `AwarenessState`
28+
- `AwarenessUser`
2829
- `BinaryData`
2930
- `BlockNavigationAddress`
3031
- `BlocksListResult`
@@ -195,6 +196,7 @@ Source: packed and installed `tests/consumer-typecheck/node_modules/superdoc`
195196
| `AIWriter` ||||| 1 | | 0 | 0 | 4 | |
196197
| `AnnotatorHelpers` ||||| 1 | | 0 | 0 | 1 | |
197198
| `AwarenessState` ||| | | 3 || 0 | 0 | 0 | |
199+
| `AwarenessUser` ||| | | 0 | | 0 | 0 | 0 | |
198200
| `BinaryData` ||| | | 3 || 0 | 0 | 0 | |
199201
| `BlankDOCX` ||||| 0 | | 0 | 0 | 1 | |
200202
| `BlockNavigationAddress` ||| | | 1 || 0 | 0 | 0 | |
@@ -228,7 +230,7 @@ Source: packed and installed `tests/consumer-typecheck/node_modules/superdoc`
228230
| `DirectSurfaceRequest` ||| | | 1 || 0 | 0 | 0 | |
229231
| `DocRange` ||| | | 1 || 0 | 0 | 0 | |
230232
| `DocumentApi` ||| | | 3 || 0 | 8 | 4 ||
231-
| `DocumentMode` ||| | | 2 || 2 | 16 | 3 | |
233+
| `DocumentMode` ||| | | 3 || 2 | 16 | 3 | |
232234
| `DocumentProtectionState` ||| | | 1 || 1 | 0 | 1 | |
233235
| `DocxFileEntry` ||| | | 3 || 0 | 0 | 0 | |
234236
| `DocxZipper` ||||| 2 | | 0 | 0 | 1 ||
@@ -282,7 +284,7 @@ Source: packed and installed `tests/consumer-typecheck/node_modules/superdoc`
282284
| `ListDefinitionsPayload` ||| | | 1 || 0 | 0 | 0 | |
283285
| `Measure` ||| | | 3 || 0 | 0 | 1 | |
284286
| `Modules` ||| | | 1 || 4 | 0 | 0 | |
285-
| `NavigableAddress` ||| | | 1 || 0 | 0 | 0 | |
287+
| `NavigableAddress` ||| | | 2 || 0 | 0 | 0 | |
286288
| `OpenOptions` ||| | | 3 || 1 | 0 | 0 | |
287289
| `PDF` ||||| 2 | | 35 | 0 | 1 ||
288290
| `PageMargins` ||| | | 3 || 0 | 0 | 0 | |
@@ -339,11 +341,11 @@ Source: packed and installed `tests/consumer-typecheck/node_modules/superdoc`
339341
| `SlashMenu` ||||| 1 | | 0 | 0 | 1 | |
340342
| `StoryLocator` ||| | | 1 || 116 | 0 | 3 | |
341343
| `SuperConverter` ||||| 1 | | 0 | 0 | 3 ||
342-
| `SuperDoc` ||||| 11 | | 1014 | 180 | 244 ||
343-
| `SuperDocExceptionEditorPayload` ||| | | 1 | | 0 | 0 | 0 | |
344-
| `SuperDocExceptionPayload` ||| | | 1 | | 0 | 0 | 0 | |
345-
| `SuperDocExceptionRestorePayload` ||| | | 0 | | 0 | 0 | 0 | |
346-
| `SuperDocExceptionStorePayload` ||| | | 1 | | 0 | 0 | 0 | |
344+
| `SuperDoc` ||||| 13 | | 1014 | 180 | 244 ||
345+
| `SuperDocExceptionEditorPayload` ||| | | 2 | | 0 | 0 | 0 | |
346+
| `SuperDocExceptionPayload` ||| | | 2 | | 0 | 0 | 0 | |
347+
| `SuperDocExceptionRestorePayload` ||| | | 1 | | 0 | 0 | 0 | |
348+
| `SuperDocExceptionStorePayload` ||| | | 2 | | 0 | 0 | 0 | |
347349
| `SuperDocLayoutEngineOptions` ||| | | 2 || 0 | 0 | 0 | |
348350
| `SuperDocTelemetryConfig` ||| | | 1 || 0 | 0 | 0 | |
349351
| `SuperEditor` ||||| 1 | | 16 | 0 | 5 | |
@@ -371,7 +373,7 @@ Source: packed and installed `tests/consumer-typecheck/node_modules/superdoc`
371373
| `Transaction` ||| | | 3 || 5 | 0 | 0 ||
372374
| `UnsupportedContentItem` ||| | | 3 || 0 | 0 | 0 | |
373375
| `UpgradeToCollaborationOptions` ||| | | 1 || 0 | 0 | 0 | |
374-
| `User` ||| | | 4 || 51 | 8 | 30 | |
376+
| `User` ||| | | 5 || 51 | 8 | 30 | |
375377
| `ViewLayout` ||| | | 1 || 0 | 0 | 0 | |
376378
| `ViewOptions` ||| | | 1 || 2 | 0 | 0 | |
377379
| `ViewingVisibilityConfig` ||| | | 1 || 0 | 0 | 0 | |

tests/consumer-typecheck/src/all-public-types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ import type {
101101
LinkPopoverResolution,
102102
LinkPopoverResolver,
103103
AwarenessState,
104+
AwarenessUser,
104105
ListDefinitionsPayload,
105106
Measure,
106107
Modules,
@@ -273,6 +274,7 @@ const _real_LinkPopoverContext: AssertNotAny<LinkPopoverContext> = true;
273274
const _real_LinkPopoverResolution: AssertNotAny<LinkPopoverResolution> = true;
274275
const _real_LinkPopoverResolver: AssertNotAny<LinkPopoverResolver> = true;
275276
const _real_AwarenessState: AssertNotAny<AwarenessState> = true;
277+
const _real_AwarenessUser: AssertNotAny<AwarenessUser> = true;
276278
const _real_ListDefinitionsPayload: AssertNotAny<ListDefinitionsPayload> = true;
277279
const _real_Measure: AssertNotAny<Measure> = true;
278280
const _real_Modules: AssertNotAny<Modules> = true;

0 commit comments

Comments
 (0)