@colixsystems/widget-sdk 0.18.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/contract.js CHANGED
@@ -168,12 +168,12 @@ const HOOKS = [
168
168
  name: "useDirectory",
169
169
  signature: "useDirectory(query?)",
170
170
  returnShape: {
171
- users: "Array<{ id, name, role }>",
171
+ users: "Array<{ id, name, role }> // snake_case rows; unwrapped from { data, meta }",
172
172
  loading: "boolean",
173
173
  error: "DatastoreError | null",
174
174
  refetch: "() => Promise<void>",
175
175
  },
176
- requiredContextSlice: ["directory.listUsers"],
176
+ requiredContextSlice: ["directory.users"],
177
177
  scopes: ["directory.read:users"],
178
178
  },
179
179
  {
@@ -207,28 +207,26 @@ const HOOKS = [
207
207
  name: "useUsers",
208
208
  signature: "useUsers(query?)",
209
209
  description:
210
- "AppUser administration. Returns { users, loading, error, refetch, invite, " +
211
- "deactivate, reactivate, remove }. Reads need users.read:* scope; mutations " +
212
- "need users.write:*. The `invite` call accepts { email, name, groupIds? } " +
213
- "and returns the resulting AppUserInvite row (the email is sent by the host).",
210
+ "AppUser administration via the injected directory-client at " +
211
+ "ctx.directory.users.{list,get,invite,deactivate,reactivate}. Returns " +
212
+ "{ users, loading, error, refetch, invite, deactivate, reactivate, remove }. " +
213
+ "list returns the { data, meta } envelope verbatim the hook unwraps " +
214
+ "res.data; rows are snake_case (is_active, …). Reads need users.read:* " +
215
+ "scope; mutations need users.write:*. The `invite` call accepts " +
216
+ "{ email, name, group_ids? } and returns the resulting AppUserInvite row " +
217
+ "(the email is sent by the host).",
214
218
  returnShape: {
215
- users: "Array<{ id, name, email?, role, isActive }>",
219
+ users: "Array<{ id, name, email?, role, is_active }> // snake_case rows; unwrapped from { data, meta }",
216
220
  loading: "boolean",
217
221
  error: "DirectoryError | null",
218
222
  refetch: "() => Promise<void>",
219
223
  invite:
220
- "({ email, name, groupIds? }) => Promise<Invite> // rejects with DirectoryError",
224
+ "({ email, name, group_ids? }) => Promise<Invite> // rejects with DirectoryError",
221
225
  deactivate: "(userId) => Promise<User> // rejects with DirectoryError",
222
226
  reactivate: "(userId) => Promise<User> // rejects with DirectoryError",
223
227
  remove: "(userId) => Promise<void> // rejects with DirectoryError",
224
228
  },
225
- requiredContextSlice: [
226
- "users.listUsers",
227
- "users.invite",
228
- "users.deactivate",
229
- "users.reactivate",
230
- "users.remove",
231
- ],
229
+ requiredContextSlice: ["directory.users"],
232
230
  scopes: ["users.read:*"],
233
231
  },
234
232
  // REQ-USERMGMT / REQ-ACL-SYS M3 — AppUserGroup administration. Returns
@@ -240,11 +238,14 @@ const HOOKS = [
240
238
  name: "useGroups",
241
239
  signature: "useGroups(query?)",
242
240
  description:
243
- "AppUserGroup administration. Returns { groups, loading, error, refetch, " +
244
- "create, remove, addMember, removeMember }. Reads need groups.read:*; " +
241
+ "AppUserGroup administration via the injected directory-client at " +
242
+ "ctx.directory.groups.{list,create,remove,addMember,removeMember,listMine}. " +
243
+ "Returns { groups, loading, error, refetch, create, remove, addMember, " +
244
+ "removeMember }. list returns the { data, meta } envelope verbatim — the " +
245
+ "hook unwraps res.data; rows are snake_case. Reads need groups.read:*; " +
245
246
  "mutations need groups.write:*.",
246
247
  returnShape: {
247
- groups: "Array<{ id, name, memberCount }>",
248
+ groups: "Array<{ id, name, member_count }> // snake_case rows; unwrapped from { data, meta }",
248
249
  loading: "boolean",
249
250
  error: "DirectoryError | null",
250
251
  refetch: "() => Promise<void>",
@@ -256,54 +257,48 @@ const HOOKS = [
256
257
  removeMember:
257
258
  "(groupId, userId) => Promise<void> // rejects with DirectoryError",
258
259
  },
259
- requiredContextSlice: [
260
- "groups.listGroups",
261
- "groups.create",
262
- "groups.remove",
263
- "groups.addMember",
264
- "groups.removeMember",
265
- ],
260
+ requiredContextSlice: ["directory.groups"],
266
261
  scopes: ["groups.read:*"],
267
262
  },
268
263
  // REQ-ACL-06 / REQ-ACL-RELINHERIT-05 — per-record VirtualPermission
269
- // management for a single record. Reads `ctx.recordPermissions` (the
270
- // host injects this on web through `widgetHostDatastore.js` and on
271
- // native through the compiled `WidgetHost` shell). The backend gates
272
- // the call on `canGrant` for the target record (Studio owners short-
273
- // circuit; APP_USER actors must hold `canGrant` via REQ-ACL-05 /
274
- // REQ-ACL-06). A widget that declares the scope but whose caller
275
- // lacks the grant receives `PermissionError { code: 'FORBIDDEN' }`.
264
+ // management for a single record. Reads the injected datastore-client at
265
+ // ctx.datastore.records(tableId).permissions(recordId) (the recordPermissions
266
+ // facade was folded into the datastore-client). The backend gates the call
267
+ // on `can_grant` for the target record (Studio owners short-circuit;
268
+ // APP_USER actors must hold `can_grant` via REQ-ACL-05 / REQ-ACL-06). A
269
+ // widget that declares the scope but whose caller lacks the grant receives
270
+ // `PermissionError { code: 'FORBIDDEN' }`.
276
271
  {
277
272
  name: "useRecordPermissions",
278
273
  signature: "useRecordPermissions(tableId, recordId)",
279
274
  description:
280
- "Manage per-record VirtualPermission grants on a single record. Returns " +
281
- "{ permissions, loading, error, grant, revoke, update, refetch } where " +
282
- "permissions is Array<{ id, principalType: 'USER' | 'GROUP' | 'PUBLIC', " +
283
- "principalId, canRead, canWrite, canDelete, canGrant }>. Mutating " +
284
- "requires acl.write:records scope AND canGrant on the target record " +
285
- "(REQ-ACL-RELINHERIT-05: APP_USER actors with canGrant are accepted, " +
286
- "not only Studio owners). When tableId or recordId is null/empty the " +
287
- "hook collapses to an empty no-op result without a network round-trip.",
275
+ "Manage per-record VirtualPermission grants on a single record via the " +
276
+ "injected datastore-client at " +
277
+ "ctx.datastore.records(tableId).permissions(recordId).{list,grant,update,revoke}. " +
278
+ "Returns { permissions, loading, error, grant, revoke, update, refetch } " +
279
+ "where permissions is Array<{ id, user_id, group_id, can_read, can_write, " +
280
+ "can_delete, can_grant }> (snake_case rows verbatim; list() returns the " +
281
+ "{ data, meta } envelope and the hook unwraps res.data). grant/update " +
282
+ "bodies are snake_case verbatim ({ user_id | group_id, can_read, " +
283
+ "can_write, can_delete, can_grant }). Mutating requires acl.write:records " +
284
+ "scope AND can_grant on the target record (REQ-ACL-RELINHERIT-05: " +
285
+ "APP_USER actors with can_grant are accepted, not only Studio owners). " +
286
+ "When tableId or recordId is null/empty the hook collapses to an empty " +
287
+ "no-op result without a network round-trip.",
288
288
  returnShape: {
289
289
  permissions:
290
- "Array<{ id, principalType: 'USER' | 'GROUP' | 'PUBLIC', principalId, canRead, canWrite, canDelete, canGrant }>",
290
+ "Array<{ id, user_id, group_id, can_read, can_write, can_delete, can_grant }> // snake_case rows; unwrapped from { data, meta }",
291
291
  loading: "boolean",
292
292
  error: "PermissionError | null",
293
293
  grant:
294
- "({ principalType, principalId?, canRead?, canWrite?, canDelete?, canGrant? }) => Promise<RecordPermission> // rejects with PermissionError",
294
+ "({ user_id?, group_id?, can_read?, can_write?, can_delete?, can_grant? }) => Promise<RecordPermission> // rejects with PermissionError",
295
295
  revoke:
296
296
  "(permissionId) => Promise<void> // rejects with PermissionError",
297
297
  update:
298
- "(permissionId, { canRead?, canWrite?, canDelete?, canGrant? }) => Promise<RecordPermission> // rejects with PermissionError",
298
+ "(permissionId, { can_read?, can_write?, can_delete?, can_grant? }) => Promise<RecordPermission> // rejects with PermissionError",
299
299
  refetch: "() => Promise<void>",
300
300
  },
301
- requiredContextSlice: [
302
- "recordPermissions.list",
303
- "recordPermissions.grant",
304
- "recordPermissions.revoke",
305
- "recordPermissions.update",
306
- ],
301
+ requiredContextSlice: ["datastore.records"],
307
302
  scopes: ["acl.write:records"],
308
303
  },
309
304
  // REQ-WSDK-PLATFORM §6 — Tier A SDK hooks.
@@ -455,10 +450,40 @@ const CATEGORIES = [
455
450
  "DATA",
456
451
  "MEDIA",
457
452
  "COMMUNICATION",
453
+ // REQ-USERMGMT-06: app-administration widgets (User Management, …) the
454
+ // published app embeds for its own member management — its own palette
455
+ // section, distinct from COMMUNICATION.
456
+ "ADMINISTRATION",
458
457
  "CUSTOM",
459
458
  ];
460
459
  const PLATFORMS = ["web", "native"];
461
460
 
461
+ // REQ-WIDGET-ACTION — server-side actions a widget may declare in its
462
+ // manifest. Each runs in the shared isolated-vm action runner (see backend
463
+ // action-runner.service.js) on a cron schedule or in response to a record
464
+ // CRUD event — NEVER in the rendered app, so they never affect Player ↔
465
+ // export parity. The trigger vocabulary mirrors the backend Action model.
466
+ const ACTION_TRIGGER_TYPES = [
467
+ "schedule",
468
+ "record_created",
469
+ "record_updated",
470
+ "record_deleted",
471
+ ];
472
+ // Globals the action script runs against (the runner's surface) — distinct
473
+ // from the React/SDK widget surface, so the component import/banned-API
474
+ // linter does NOT scan action scripts.
475
+ const ACTION_SCRIPT_GLOBALS = [
476
+ "datastore",
477
+ "fetch",
478
+ "console",
479
+ "record",
480
+ "tenantId",
481
+ "triggerType",
482
+ "triggerTableId",
483
+ ];
484
+ // Mirrors action.service.js SCRIPT_MAX_BYTES.
485
+ const ACTION_SCRIPT_MAX_BYTES = 200 * 1024;
486
+
462
487
  // Reverse-DNS-ish manifest id, e.g. "com.acme.charts.barchart". Two or
463
488
  // more labels, lowercase alnum + hyphen, label starts with a letter. The
464
489
  // analyzer + the SDK validator both read this from the contract so a
@@ -544,6 +569,17 @@ const MANIFEST_SCHEMA = {
544
569
  description:
545
570
  "Optional. Tables the widget needs, seeded into the workspace at install time. Authors wire them into the widget's `tableRef` properties via the Properties Panel — the SDK does not auto-bind. Limits: 8 tables, 24 columns per table. RELATION columns address siblings by `targetSuffix` (must be declared earlier in the array). Tables persist across uninstalls.",
546
571
  },
572
+ actions: {
573
+ type: "object[]",
574
+ required: false,
575
+ description:
576
+ "Optional. Server-side actions the widget declares. Each runs in the shared isolated-vm action runner (cron- or record-triggered) — NEVER in the rendered app. Operators enable them per tenant from the Properties Panel; the action materialises DISABLED until they bind an integration API key (and, for record_* triggers, a target table) in the Actions admin page. Each entry: { key (stable, unique within the manifest), name, description?, triggerType (one of " +
577
+ ACTION_TRIGGER_TYPES.join(", ") +
578
+ "), scheduleCron? (required iff triggerType=='schedule'; node-cron syntax), timeoutMs? (100–300000), scriptSource (≤200 KiB; runs against " +
579
+ ACTION_SCRIPT_GLOBALS.join(", ") +
580
+ " — NOT the React surface, so SDK imports/hooks are unavailable) }. Do NOT include triggerTableId or apiKeyId — those are tenant-local and bound after install.",
581
+ default: [],
582
+ },
547
583
  };
548
584
 
549
585
  const WIDGET_CONTEXT_SHAPE = {
@@ -559,9 +595,10 @@ const WIDGET_CONTEXT_SHAPE = {
559
595
  fields: { id: "manifest.id", version: "manifest.version" },
560
596
  },
561
597
  user: {
562
- description: "Signed-in user ({ id, email, displayName }).",
598
+ description:
599
+ "Signed-in user, host-provided VERBATIM (snake_case: { id, email, display_name, roles, group_ids }). Not a data-client.",
563
600
  required: true,
564
- fields: { id: "string", email: "string", displayName: "string" },
601
+ fields: { id: "string", email: "string", display_name: "string" },
565
602
  },
566
603
  workspace: {
567
604
  description:
@@ -581,19 +618,30 @@ const WIDGET_CONTEXT_SHAPE = {
581
618
  },
582
619
  datastore: {
583
620
  description:
584
- "{ records(table) -> { list(query?), get(id), create(values), update(id, values), delete(id) }, schema(table) -> Promise<{ id, name, columns: [...] }> }. `records` backs the query/record/mutation hooks; `schema` backs useDatastoreSchema() and resolves a table's column structure (no row data).",
621
+ "Injected @colixsystems/datastore-client instance. " +
622
+ "{ tables: { list(), get(idOrName) }, schema(tableId) -> Promise<{ id, name, columns: [...] }>, " +
623
+ "records(tableId) -> { list(query) -> Promise<{ data, meta }>, get(id), create(values), update(id, values), delete(id), aggregate(spec), " +
624
+ "permissions(recordId) -> { list() -> Promise<{ data, meta }>, grant(body), update(permId, patch), revoke(permId) } } }. " +
625
+ "`records` backs the query/record/mutation hooks; `records(t).permissions(r)` backs useRecordPermissions(); `schema` backs useDatastoreSchema(). " +
626
+ "List methods return the { data, meta } envelope verbatim (hooks unwrap res.data); rows/bodies are snake_case (author column values keep their author-given names).",
585
627
  required: true,
586
- fields: { records: "function", schema: "function" },
628
+ fields: { records: "function", schema: "function", tables: "object" },
587
629
  },
588
630
  directory: {
589
631
  description:
590
- "Read-only user directory. { listUsers(query?) -> Promise<Array<{ id, name, role }>> }. Backs useDirectory(); for chat people-lists / @-mention pickers / author-id resolution. Requires the directory.read:users scope.",
632
+ "Injected @colixsystems/directory-client instance. " +
633
+ "{ me(), users: { list(query?) -> Promise<{ data, meta }>, get(id), invite(body), deactivate(id), reactivate(id) }, " +
634
+ "groups: { list(query?) -> Promise<{ data, meta }>, create(body), remove(id), addMember(groupId, userId), removeMember(groupId, userId), listMine() }, " +
635
+ "invites: { list(), revoke(id), resend(id) } }. " +
636
+ "users backs useDirectory() + useUsers(); groups backs useGroups(). List methods return the { data, meta } envelope verbatim (hooks unwrap res.data); rows/bodies are snake_case. Reads gated by directory.read:users / users.read:* / groups.read:*; mutations by users.write:* / groups.write:*.",
591
637
  required: true,
592
- fields: { listUsers: "function" },
638
+ fields: { users: "object", groups: "object" },
593
639
  },
594
640
  files: {
595
641
  description:
596
- "Read-only asset resolver. { get(fileId) -> Promise<{ id, url, storedFilename, mimeType, sizeBytes, ... }> }. Backs useFile(); resolves an asset id to an absolute URL the widget can drop into an <Image source>. The url field is always an absolute URL composed against the host's API base.",
642
+ "Injected @colixsystems/files-client instance, FLATTENED so file ops are top-level. " +
643
+ "{ get(id) -> Promise<{ id, url, ... }>, list(query) -> Promise<{ data, meta }>, upload(formData), folders: { list, create }, shares: { list, create, remove } }. " +
644
+ "Backs useFile(); the returned file already carries an absolute url the widget can drop into an <Image source>.",
597
645
  required: true,
598
646
  fields: { get: "function" },
599
647
  },
@@ -610,64 +658,17 @@ const WIDGET_CONTEXT_SHAPE = {
610
658
  },
611
659
  payments: {
612
660
  description:
613
- "Incoming app-user payments (REQ-BILL-07-WIDGETPAY). { requestPayment({ amountCents, currency?, description, metadata? }) -> Promise<{ id, status, checkoutUrl? }>, getPayment(id) -> Promise<payment> }. Backs usePayments(); requires the payments.charge:appUser scope. The host opens hosted Checkout (or auto-confirms under the mock provider); the charge settles to the workspace owner.",
661
+ "Injected @colixsystems/payments-client instance (REQ-BILL-07-WIDGETPAY). { requestPayment(body) -> Promise<{ id, status, checkoutUrl? }>, getPayment(id) -> Promise<payment> }. Backs usePayments(); requires the payments.charge:appUser scope. The host opens hosted Checkout (or auto-confirms under the mock provider); the charge settles to the workspace owner.",
614
662
  required: true,
615
663
  fields: { requestPayment: "function", getPayment: "function" },
616
664
  },
617
- // REQ-USERMGMT / REQ-ACL-SYS M3 — AppUser administration facade backing
618
- // useUsers(). Reads gated by `users.read:*`; mutations by `users.write:*`.
619
- // The host signs an `X-Widget-Scopes` header against JWT_SECRET so an
620
- // APP_USER cannot forge scope claims, and the request is additionally
621
- // gated by a SystemAcl `users.read` / `users.write` capability grant.
622
- users: {
623
- description:
624
- "AppUser administration. { listUsers(query?) -> Promise<User[]>, invite({ email, name, groupIds? }) -> Promise<Invite>, deactivate(userId) -> Promise<User>, reactivate(userId) -> Promise<User>, remove(userId) -> Promise<void> }. Backs useUsers(); reads require users.read:*, mutations require users.write:*.",
625
- required: true,
626
- fields: {
627
- listUsers: "function",
628
- invite: "function",
629
- deactivate: "function",
630
- reactivate: "function",
631
- remove: "function",
632
- },
633
- },
634
- // REQ-ACL-06 / REQ-ACL-RELINHERIT-05 — per-record VirtualPermission
635
- // management facade backing useRecordPermissions(). Hits the existing
636
- // /api/v1/tables/:tableId/records/:recordId/permissions REST surface;
637
- // the host normalises the wire shape (userId / groupId / both-null =
638
- // public) into the principalType+principalId pair widgets read.
639
- recordPermissions: {
640
- description:
641
- "Per-record VirtualPermission management. " +
642
- "{ list(tableId, recordId) -> Promise<RecordPermission[]>, " +
643
- "grant(tableId, recordId, body) -> Promise<RecordPermission>, " +
644
- "revoke(tableId, recordId, permissionId) -> Promise<void>, " +
645
- "update(tableId, recordId, permissionId, body) -> Promise<RecordPermission> }. " +
646
- "Backs useRecordPermissions(); requires acl.write:records scope AND " +
647
- "canGrant on the target record.",
648
- required: true,
649
- fields: {
650
- list: "function",
651
- grant: "function",
652
- revoke: "function",
653
- update: "function",
654
- },
655
- },
656
- // REQ-USERMGMT / REQ-ACL-SYS M3 — AppUserGroup administration facade
657
- // backing useGroups(). Reads gated by `groups.read:*`; mutations by
658
- // `groups.write:*`. Same X-Widget-Scopes + SystemAcl gating as users.
659
- groups: {
660
- description:
661
- "AppUserGroup administration. { listGroups(query?) -> Promise<Group[]>, create({ name }) -> Promise<Group>, remove(groupId) -> Promise<void>, addMember(groupId, userId) -> Promise<void>, removeMember(groupId, userId) -> Promise<void> }. Backs useGroups(); reads require groups.read:*, mutations require groups.write:*.",
662
- required: true,
663
- fields: {
664
- listGroups: "function",
665
- create: "function",
666
- remove: "function",
667
- addMember: "function",
668
- removeMember: "function",
669
- },
670
- },
665
+ // REQ-WSDK-DOMAIN-CLIENTSthe AppUser administration, AppUserGroup
666
+ // administration, and per-record VirtualPermission facades that used to
667
+ // live here (`users`, `groups`, `recordPermissions`) were folded into the
668
+ // injected domain-client instances: useUsers()/useGroups() read
669
+ // ctx.directory.{users,groups}; useRecordPermissions() reads
670
+ // ctx.datastore.records(table).permissions(record). See the `directory`
671
+ // and `datastore` slices above.
671
672
  i18n: {
672
673
  description: "{ t(key, fallback?), locale }.",
673
674
  required: true,
@@ -873,19 +874,46 @@ function deepFreeze(value) {
873
874
  }
874
875
 
875
876
  const CONTRACT = deepFreeze({
876
- // 1.8.0: additive — new useRecordPermissions(tableId, recordId) hook +
877
- // the recordPermissions host-context slice it reads + the
878
- // acl.write:records scope it gates on. Hits the existing REQ-ACL-06
879
- // /api/v1/tables/:tableId/records/:recordId/permissions REST surface;
880
- // the host normalises the wire shape into a single principalType+
881
- // principalId pair widgets branch on. Caller still needs canGrant on
882
- // the target record (REQ-ACL-RELINHERIT-05).
883
- version: "1.8.0",
877
+ // 1.7.0: additive — new useDatastoreSchema(tableId) hook + the
878
+ // datastore.schema host-context slice it reads (resolves a table's column
879
+ // structure at runtime via the existing ACL-gated GET /tables/:id).
880
+ //
881
+ // 1.8.0: two additive features.
882
+ // - REQ-WIDGET-ACTION manifests may declare an optional `actions` array:
883
+ // server-side cron/record-triggered scripts the operator enables per
884
+ // tenant, run in the existing isolated-vm action runner (never in the
885
+ // rendered app). New fields actionTriggerTypes / actionScriptGlobals /
886
+ // actionScriptMaxBytes feed the docs + agent prompt. New ADMINISTRATION
887
+ // manifest category (REQ-USERMGMT-06).
888
+ // - REQ-ACL-06 — new useRecordPermissions(tableId, recordId) hook + the
889
+ // recordPermissions host-context slice it reads + the acl.write:records
890
+ // scope it gates on. Hits the existing REQ-ACL-06
891
+ // /api/v1/tables/:tableId/records/:recordId/permissions REST surface;
892
+ // the host normalises the wire shape into a single principalType+
893
+ // principalId pair widgets branch on. Caller still needs canGrant on
894
+ // the target record (REQ-ACL-RELINHERIT-05).
895
+ //
896
+ // 1.9.0: host-contract change (REQ-WSDK-DOMAIN-CLIENTS) — the host now
897
+ // injects domain-client INSTANCES on the WidgetContext rather than
898
+ // bespoke per-hook facades. ctx.datastore is a @colixsystems/datastore-client
899
+ // (records(t).list now returns the { data, meta } envelope verbatim;
900
+ // records(t).permissions(r) replaces the deleted ctx.recordPermissions),
901
+ // ctx.directory is a @colixsystems/directory-client (users/groups
902
+ // namespaces replace the deleted ctx.users / ctx.groups; list returns
903
+ // envelopes), ctx.files is a flattened @colixsystems/files-client,
904
+ // ctx.payments is a @colixsystems/payments-client. All rows/bodies are
905
+ // snake_case. Hooks now unwrap the list envelope (res.data ?? []). The
906
+ // SDK stays duck-typed — it imports none of the four data SDKs. Minor
907
+ // bump on the contract's pre-1.0 versioning (the breaking channel).
908
+ version: "1.9.0",
884
909
  hooks: HOOKS,
885
910
  primitives: PRIMITIVES,
886
911
  manifestSchema: MANIFEST_SCHEMA,
887
912
  manifestCategories: CATEGORIES,
888
913
  manifestPlatforms: PLATFORMS,
914
+ actionTriggerTypes: ACTION_TRIGGER_TYPES,
915
+ actionScriptGlobals: ACTION_SCRIPT_GLOBALS,
916
+ actionScriptMaxBytes: ACTION_SCRIPT_MAX_BYTES,
889
917
  themeTokens: DEFAULT_THEME_TOKENS,
890
918
  widgetContextShape: WIDGET_CONTEXT_SHAPE,
891
919
  bundleExportContract: BUNDLE_EXPORT_CONTRACT,