@colixsystems/widget-sdk 0.95.0 → 0.97.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/README.md +23 -0
- package/dist/contract.cjs +130 -4
- package/dist/contract.js +130 -4
- package/dist/index.d.ts +13 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -66,6 +66,29 @@ See the design reference for the full architecture: [`docs/architecture/widget-m
|
|
|
66
66
|
|
|
67
67
|
`v0.91.0` — pre-publish. The package surface (types, function names, export paths) is the v1 contract; runtime behaviour for some hooks is stubbed (each hook documents what's wired and what isn't). It is **not yet published to npm**.
|
|
68
68
|
|
|
69
|
+
### What's new in 0.97.0 (contract 1.69.0)
|
|
70
|
+
|
|
71
|
+
**`CONTRACT.themeComponents` gains five scopes: `accent`, `destructive`, `muted`, `popover`, `ring` (sc-5392).** The vocabulary shipped with exactly `button`/`card`/`text` (sc-1497), so a shadcn/Tailwind app import's `--accent`, `--destructive`, `--muted`, `--popover` and `--ring` custom properties had no `themeConfig` home and were reported "no theme home" on every import. Each new scope binds to real `styleSchema` fields on the built-ins that already had a matching surface — `accent` (a highlight/tag surface: `background`/`borderColor`/`radius`) to the Label widget's own fields, `destructive` (a themed danger/delete action: `background`/`textColor`/`borderColor`) to a new "Danger" Button variant, `muted` (a subtle/secondary surface: `background`/`textColor`/`borderColor`/`radius`) to Form Input's and Form Builder's pre-existing input fields, `popover` (a dropdown/menu surface: `background`/`textColor`/`borderColor`) to the same two widgets' choice-field option list, and `ring` (the app-wide focus-visible outline: `color`/`width`) to a new emphasis border on Button. **This is HOST-ONLY plumbing, exactly like the three scopes before it** — `useTheme()`'s documented `components` slice is unchanged, no widget-authoring hook or `propertySchema` type moved, and no scope declares `universalFields`, so a third-party or AI-generated widget's contract is unaffected; the Developer guide and `DEFAULT_SYSTEM_PROMPT` need no update because neither ever documented this internal vocabulary. Fully additive: a theme with no `components` key, or one using only `button`/`card`/`text`, resolves exactly as before.
|
|
72
|
+
- **`CONTRACT.version` → `1.69.0`** (additive: five new `themeComponents` scopes + their target-field bindings). No existing scope, token, or export changed shape.
|
|
73
|
+
|
|
74
|
+
### What's new in 0.96.0 (contract 1.68.0)
|
|
75
|
+
|
|
76
|
+
**A manifest action can declare `manual`, and every action script gains a `request` global (sc-5366).** The backend Action model grew three ways of *reaching* a script to sit beside the ones that *fire* it: `manual` (nothing starts it — the workspace runs it on demand), `app` (a published app's button `onPress`) and `http_post` (an inbound webhook at `POST /api/v1/action-hooks/:actionId`, authenticated with one of the workspace's integration API keys). The same change retired the separate `appInvokable` boolean, so one column now answers "what starts this action?".
|
|
77
|
+
|
|
78
|
+
Only `manual` joins `CONTRACT.actionTriggerTypes`, and that is deliberate. `app` and `http_post` expose a script to a caller **outside** the Studio, which is the installing workspace's decision about running someone else's code — not the author's. A manifest that declares either is rejected by `validateManifest`, the CLI linter and the backend alike; the operator grants them in the Actions admin page after install, on top of whatever triggers your manifest declared. Nothing about an already-published manifest changes.
|
|
79
|
+
|
|
80
|
+
`CONTRACT.actionScriptGlobals` gains **`request`**: `{ body }` — the JSON an inbound webhook caller sent — on an `http_post` run, and `null` on every other trigger. Request *headers* are never passed through, because they carry the caller's API key. `triggerType` now also reports `"http_post"` alongside `"manual"` and `"app"`, so one script can tell a webhook apart from its nightly schedule:
|
|
81
|
+
|
|
82
|
+
```js
|
|
83
|
+
if (triggerType === "http_post") {
|
|
84
|
+
const order = request?.body;
|
|
85
|
+
if (!order?.id) return; // never trust the caller's shape
|
|
86
|
+
await datastore.records("Orders").create({ externalId: order.id });
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
`CONTRACT.version` → `1.68.0`. Additive for every existing manifest.
|
|
91
|
+
|
|
69
92
|
### What's new in 0.95.0 (contract 1.67.0)
|
|
70
93
|
|
|
71
94
|
**An admin can mail a locked-out member a password-reset link — `useUsers().sendPasswordReset(userId)` (sc-5335).** An app user who forgot their password could only recover it themselves, from the app's own login screen. The admin they actually ask — the one already able to invite, deactivate and remove them — had no way to help, and the workaround in the field was to remove and re-invite the account, which discards its group memberships and history.
|
package/dist/contract.cjs
CHANGED
|
@@ -211,6 +211,34 @@ const CARD_UNIVERSAL_FIELDS = Object.freeze({
|
|
|
211
211
|
gradient: "cardGradient",
|
|
212
212
|
});
|
|
213
213
|
|
|
214
|
+
// The Form widgets' text-entry surface, already read by both form widgets
|
|
215
|
+
// (REQ-THEME-13, predating this scope) -- a genuine subtle/secondary surface
|
|
216
|
+
// distinct from the card that hosts it.
|
|
217
|
+
const MUTED_INPUT_FIELDS = Object.freeze({
|
|
218
|
+
background: "inputBackground",
|
|
219
|
+
textColor: "inputTextColor",
|
|
220
|
+
borderColor: "inputBorderColor",
|
|
221
|
+
radius: "inputRadius",
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// The Form widgets' choice-field option list -- the closest thing this
|
|
225
|
+
// catalog has to a menu/dropdown surface (singleChoice/multiChoice render an
|
|
226
|
+
// inline list of options here).
|
|
227
|
+
const POPOVER_FIELDS = Object.freeze({
|
|
228
|
+
background: "popoverBackground",
|
|
229
|
+
textColor: "popoverTextColor",
|
|
230
|
+
borderColor: "popoverBorderColor",
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
// The Button widget's danger-variant fields -- kept separate from its own
|
|
234
|
+
// `background`/`textColor`/`borderColor` (the `button` scope's fields above)
|
|
235
|
+
// so a themed destructive action never collides with the button's normal look.
|
|
236
|
+
const DANGER_BUTTON_FIELDS = Object.freeze({
|
|
237
|
+
background: "dangerBackground",
|
|
238
|
+
textColor: "dangerTextColor",
|
|
239
|
+
borderColor: "dangerBorderColor",
|
|
240
|
+
});
|
|
241
|
+
|
|
214
242
|
const THEME_COMPONENTS = Object.freeze({
|
|
215
243
|
button: Object.freeze({
|
|
216
244
|
label: "Buttons",
|
|
@@ -277,6 +305,82 @@ const THEME_COMPONENTS = Object.freeze({
|
|
|
277
305
|
"appstudio.data-value": TEXT_TYPOGRAPHY_FIELDS,
|
|
278
306
|
}),
|
|
279
307
|
}),
|
|
308
|
+
// sc-5392 -- a highlight/tag surface. Bound to Label's OWN background/
|
|
309
|
+
// borderColor/radius fields (REQ-THEME-13, predating this scope) rather
|
|
310
|
+
// than its `color` field, which the `text` scope already owns -- two
|
|
311
|
+
// scopes writing the same field on the same widget would race.
|
|
312
|
+
accent: Object.freeze({
|
|
313
|
+
label: "Accent",
|
|
314
|
+
tokens: Object.freeze({
|
|
315
|
+
background: Object.freeze({ type: "color", uiDefault: "colors.primarySoft" }),
|
|
316
|
+
borderColor: Object.freeze({ type: "color", uiDefault: "colors.primary" }),
|
|
317
|
+
radius: Object.freeze({ type: "size", min: 0, max: 48, uiDefault: "radii.pill" }),
|
|
318
|
+
}),
|
|
319
|
+
targets: Object.freeze({
|
|
320
|
+
"appstudio.label": Object.freeze({
|
|
321
|
+
background: "background",
|
|
322
|
+
borderColor: "borderColor",
|
|
323
|
+
radius: "radius",
|
|
324
|
+
}),
|
|
325
|
+
}),
|
|
326
|
+
}),
|
|
327
|
+
// sc-5392 -- the Button widget's danger/delete variant. Separate token set
|
|
328
|
+
// from `button` above so "make the buttons coral" never recolours Delete.
|
|
329
|
+
destructive: Object.freeze({
|
|
330
|
+
label: "Destructive",
|
|
331
|
+
tokens: Object.freeze({
|
|
332
|
+
background: Object.freeze({ type: "color", uiDefault: "colors.danger" }),
|
|
333
|
+
textColor: Object.freeze({ type: "color", uiDefault: "colors.onPrimary" }),
|
|
334
|
+
borderColor: Object.freeze({ type: "color", uiDefault: "colors.danger" }),
|
|
335
|
+
}),
|
|
336
|
+
targets: Object.freeze({
|
|
337
|
+
"appstudio.button": DANGER_BUTTON_FIELDS,
|
|
338
|
+
}),
|
|
339
|
+
}),
|
|
340
|
+
// sc-5392 -- a subtle/secondary surface, bound to the form widgets' own
|
|
341
|
+
// text-entry fields (already REQ-THEME-13 style fields, predating this
|
|
342
|
+
// scope) rather than to Text/Label/Data Value, whose `color` field the
|
|
343
|
+
// `text` scope already owns.
|
|
344
|
+
muted: Object.freeze({
|
|
345
|
+
label: "Muted",
|
|
346
|
+
tokens: Object.freeze({
|
|
347
|
+
background: Object.freeze({ type: "color", uiDefault: "colors.surfaceMuted" }),
|
|
348
|
+
textColor: Object.freeze({ type: "color", uiDefault: "colors.onSurfaceMuted" }),
|
|
349
|
+
borderColor: Object.freeze({ type: "color", uiDefault: "colors.border" }),
|
|
350
|
+
radius: Object.freeze({ type: "size", min: 0, max: 48, uiDefault: "radii.sm" }),
|
|
351
|
+
}),
|
|
352
|
+
targets: Object.freeze({
|
|
353
|
+
"appstudio.form-input": MUTED_INPUT_FIELDS,
|
|
354
|
+
"appstudio.form-builder": MUTED_INPUT_FIELDS,
|
|
355
|
+
}),
|
|
356
|
+
}),
|
|
357
|
+
// sc-5392 -- dropdown/menu surfaces. The form widgets' choice-field option
|
|
358
|
+
// list is the closest thing the built-in catalog has to a popover panel.
|
|
359
|
+
popover: Object.freeze({
|
|
360
|
+
label: "Popover",
|
|
361
|
+
tokens: Object.freeze({
|
|
362
|
+
background: Object.freeze({ type: "color", uiDefault: "colors.surface" }),
|
|
363
|
+
textColor: Object.freeze({ type: "color", uiDefault: "colors.onSurface" }),
|
|
364
|
+
borderColor: Object.freeze({ type: "color", uiDefault: "colors.border" }),
|
|
365
|
+
}),
|
|
366
|
+
targets: Object.freeze({
|
|
367
|
+
"appstudio.form-input": POPOVER_FIELDS,
|
|
368
|
+
"appstudio.form-builder": POPOVER_FIELDS,
|
|
369
|
+
}),
|
|
370
|
+
}),
|
|
371
|
+
// sc-5392 -- the app-wide focus-visible outline. A themed emphasis border
|
|
372
|
+
// on the Button widget, applied after its variant colours and beaten by the
|
|
373
|
+
// widget's own per-instance `borderColor` (REQ-THEME-13 stays final).
|
|
374
|
+
ring: Object.freeze({
|
|
375
|
+
label: "Focus ring",
|
|
376
|
+
tokens: Object.freeze({
|
|
377
|
+
color: Object.freeze({ type: "color", uiDefault: "colors.primary" }),
|
|
378
|
+
width: Object.freeze({ type: "size", min: 0, max: 8 }),
|
|
379
|
+
}),
|
|
380
|
+
targets: Object.freeze({
|
|
381
|
+
"appstudio.button": Object.freeze({ color: "ringColor", width: "ringWidth" }),
|
|
382
|
+
}),
|
|
383
|
+
}),
|
|
280
384
|
});
|
|
281
385
|
|
|
282
386
|
// sc-4505 — the value types a widget event payload field may declare. Narrower
|
|
@@ -1459,14 +1563,23 @@ const PLATFORMS = ["web", "native"];
|
|
|
1459
1563
|
|
|
1460
1564
|
// REQ-WIDGET-ACTION — server-side actions a widget may declare in its
|
|
1461
1565
|
// manifest. Each runs in the shared isolated-vm action runner (see backend
|
|
1462
|
-
// action-runner.service.js) on a cron schedule
|
|
1463
|
-
//
|
|
1464
|
-
// export parity.
|
|
1566
|
+
// action-runner.service.js) on a cron schedule, in response to a record CRUD
|
|
1567
|
+
// event, or on demand — NEVER in the rendered app, so they never affect
|
|
1568
|
+
// Player ↔ export parity.
|
|
1569
|
+
//
|
|
1570
|
+
// This is what a MANIFEST may declare. The backend Action model additionally
|
|
1571
|
+
// accepts 'app' and 'http_post' (sc-5366), which expose a script to a
|
|
1572
|
+
// published app's buttons or to a public webhook — those are the installing
|
|
1573
|
+
// workspace's decision about running someone else's code, so an operator
|
|
1574
|
+
// grants them and a manifest may not ask for them. `validateManifest` and the
|
|
1575
|
+
// backend's `_validateWidgetDeclared` both reject them, so the linter and the
|
|
1576
|
+
// server agree about what a manifest declared.
|
|
1465
1577
|
const ACTION_TRIGGER_TYPES = [
|
|
1466
1578
|
"schedule",
|
|
1467
1579
|
"record_created",
|
|
1468
1580
|
"record_updated",
|
|
1469
1581
|
"record_deleted",
|
|
1582
|
+
"manual",
|
|
1470
1583
|
];
|
|
1471
1584
|
// Globals the action script runs against (the runner's surface) — distinct
|
|
1472
1585
|
// from the React/SDK widget surface, so the component import/banned-API
|
|
@@ -1479,6 +1592,9 @@ const ACTION_SCRIPT_GLOBALS = [
|
|
|
1479
1592
|
"notifications",
|
|
1480
1593
|
"console",
|
|
1481
1594
|
"record",
|
|
1595
|
+
// sc-5366 — `{ body }` on a run fired by the inbound `http_post` trigger,
|
|
1596
|
+
// null on every other trigger.
|
|
1597
|
+
"request",
|
|
1482
1598
|
"tenantId",
|
|
1483
1599
|
"triggerType",
|
|
1484
1600
|
"triggerTableId",
|
|
@@ -2939,7 +3055,17 @@ const CONTRACT = deepFreeze({
|
|
|
2939
3055
|
// resolves `{ sent, email_masked }` and never returns the token or the
|
|
2940
3056
|
// link, so it is not an account-takeover primitive; it gates on the same
|
|
2941
3057
|
// `users.write:*` scope as invite / deactivate.
|
|
2942
|
-
|
|
3058
|
+
// 1.68.0: additive (sc-5366) — a manifest action may declare the `manual`
|
|
3059
|
+
// trigger (nothing fires it; the workspace runs it on demand), and every
|
|
3060
|
+
// action script now sees a `request` global — `{ body }` on a run fired
|
|
3061
|
+
// by the workspace-granted `http_post` webhook trigger, null otherwise.
|
|
3062
|
+
// `app` and `http_post` stay operator-granted and are NOT declarable.
|
|
3063
|
+
// 1.69.0: additive (sc-5392) — `themeComponents` gains five scopes:
|
|
3064
|
+
// `accent`, `destructive`, `muted`, `popover` and `ring`, so a
|
|
3065
|
+
// shadcn/Tailwind import's matching custom properties have a themeConfig
|
|
3066
|
+
// home instead of being reported lost. Host-only plumbing: no scope
|
|
3067
|
+
// declares `universalFields`, so a third-party manifest is unaffected.
|
|
3068
|
+
version: "1.69.0",
|
|
2943
3069
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
2944
3070
|
hooks: HOOKS,
|
|
2945
3071
|
primitives: PRIMITIVES,
|
package/dist/contract.js
CHANGED
|
@@ -211,6 +211,34 @@ const CARD_UNIVERSAL_FIELDS = Object.freeze({
|
|
|
211
211
|
gradient: "cardGradient",
|
|
212
212
|
});
|
|
213
213
|
|
|
214
|
+
// The Form widgets' text-entry surface, already read by both form widgets
|
|
215
|
+
// (REQ-THEME-13, predating this scope) -- a genuine subtle/secondary surface
|
|
216
|
+
// distinct from the card that hosts it.
|
|
217
|
+
const MUTED_INPUT_FIELDS = Object.freeze({
|
|
218
|
+
background: "inputBackground",
|
|
219
|
+
textColor: "inputTextColor",
|
|
220
|
+
borderColor: "inputBorderColor",
|
|
221
|
+
radius: "inputRadius",
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// The Form widgets' choice-field option list -- the closest thing this
|
|
225
|
+
// catalog has to a menu/dropdown surface (singleChoice/multiChoice render an
|
|
226
|
+
// inline list of options here).
|
|
227
|
+
const POPOVER_FIELDS = Object.freeze({
|
|
228
|
+
background: "popoverBackground",
|
|
229
|
+
textColor: "popoverTextColor",
|
|
230
|
+
borderColor: "popoverBorderColor",
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
// The Button widget's danger-variant fields -- kept separate from its own
|
|
234
|
+
// `background`/`textColor`/`borderColor` (the `button` scope's fields above)
|
|
235
|
+
// so a themed destructive action never collides with the button's normal look.
|
|
236
|
+
const DANGER_BUTTON_FIELDS = Object.freeze({
|
|
237
|
+
background: "dangerBackground",
|
|
238
|
+
textColor: "dangerTextColor",
|
|
239
|
+
borderColor: "dangerBorderColor",
|
|
240
|
+
});
|
|
241
|
+
|
|
214
242
|
const THEME_COMPONENTS = Object.freeze({
|
|
215
243
|
button: Object.freeze({
|
|
216
244
|
label: "Buttons",
|
|
@@ -277,6 +305,82 @@ const THEME_COMPONENTS = Object.freeze({
|
|
|
277
305
|
"appstudio.data-value": TEXT_TYPOGRAPHY_FIELDS,
|
|
278
306
|
}),
|
|
279
307
|
}),
|
|
308
|
+
// sc-5392 -- a highlight/tag surface. Bound to Label's OWN background/
|
|
309
|
+
// borderColor/radius fields (REQ-THEME-13, predating this scope) rather
|
|
310
|
+
// than its `color` field, which the `text` scope already owns -- two
|
|
311
|
+
// scopes writing the same field on the same widget would race.
|
|
312
|
+
accent: Object.freeze({
|
|
313
|
+
label: "Accent",
|
|
314
|
+
tokens: Object.freeze({
|
|
315
|
+
background: Object.freeze({ type: "color", uiDefault: "colors.primarySoft" }),
|
|
316
|
+
borderColor: Object.freeze({ type: "color", uiDefault: "colors.primary" }),
|
|
317
|
+
radius: Object.freeze({ type: "size", min: 0, max: 48, uiDefault: "radii.pill" }),
|
|
318
|
+
}),
|
|
319
|
+
targets: Object.freeze({
|
|
320
|
+
"appstudio.label": Object.freeze({
|
|
321
|
+
background: "background",
|
|
322
|
+
borderColor: "borderColor",
|
|
323
|
+
radius: "radius",
|
|
324
|
+
}),
|
|
325
|
+
}),
|
|
326
|
+
}),
|
|
327
|
+
// sc-5392 -- the Button widget's danger/delete variant. Separate token set
|
|
328
|
+
// from `button` above so "make the buttons coral" never recolours Delete.
|
|
329
|
+
destructive: Object.freeze({
|
|
330
|
+
label: "Destructive",
|
|
331
|
+
tokens: Object.freeze({
|
|
332
|
+
background: Object.freeze({ type: "color", uiDefault: "colors.danger" }),
|
|
333
|
+
textColor: Object.freeze({ type: "color", uiDefault: "colors.onPrimary" }),
|
|
334
|
+
borderColor: Object.freeze({ type: "color", uiDefault: "colors.danger" }),
|
|
335
|
+
}),
|
|
336
|
+
targets: Object.freeze({
|
|
337
|
+
"appstudio.button": DANGER_BUTTON_FIELDS,
|
|
338
|
+
}),
|
|
339
|
+
}),
|
|
340
|
+
// sc-5392 -- a subtle/secondary surface, bound to the form widgets' own
|
|
341
|
+
// text-entry fields (already REQ-THEME-13 style fields, predating this
|
|
342
|
+
// scope) rather than to Text/Label/Data Value, whose `color` field the
|
|
343
|
+
// `text` scope already owns.
|
|
344
|
+
muted: Object.freeze({
|
|
345
|
+
label: "Muted",
|
|
346
|
+
tokens: Object.freeze({
|
|
347
|
+
background: Object.freeze({ type: "color", uiDefault: "colors.surfaceMuted" }),
|
|
348
|
+
textColor: Object.freeze({ type: "color", uiDefault: "colors.onSurfaceMuted" }),
|
|
349
|
+
borderColor: Object.freeze({ type: "color", uiDefault: "colors.border" }),
|
|
350
|
+
radius: Object.freeze({ type: "size", min: 0, max: 48, uiDefault: "radii.sm" }),
|
|
351
|
+
}),
|
|
352
|
+
targets: Object.freeze({
|
|
353
|
+
"appstudio.form-input": MUTED_INPUT_FIELDS,
|
|
354
|
+
"appstudio.form-builder": MUTED_INPUT_FIELDS,
|
|
355
|
+
}),
|
|
356
|
+
}),
|
|
357
|
+
// sc-5392 -- dropdown/menu surfaces. The form widgets' choice-field option
|
|
358
|
+
// list is the closest thing the built-in catalog has to a popover panel.
|
|
359
|
+
popover: Object.freeze({
|
|
360
|
+
label: "Popover",
|
|
361
|
+
tokens: Object.freeze({
|
|
362
|
+
background: Object.freeze({ type: "color", uiDefault: "colors.surface" }),
|
|
363
|
+
textColor: Object.freeze({ type: "color", uiDefault: "colors.onSurface" }),
|
|
364
|
+
borderColor: Object.freeze({ type: "color", uiDefault: "colors.border" }),
|
|
365
|
+
}),
|
|
366
|
+
targets: Object.freeze({
|
|
367
|
+
"appstudio.form-input": POPOVER_FIELDS,
|
|
368
|
+
"appstudio.form-builder": POPOVER_FIELDS,
|
|
369
|
+
}),
|
|
370
|
+
}),
|
|
371
|
+
// sc-5392 -- the app-wide focus-visible outline. A themed emphasis border
|
|
372
|
+
// on the Button widget, applied after its variant colours and beaten by the
|
|
373
|
+
// widget's own per-instance `borderColor` (REQ-THEME-13 stays final).
|
|
374
|
+
ring: Object.freeze({
|
|
375
|
+
label: "Focus ring",
|
|
376
|
+
tokens: Object.freeze({
|
|
377
|
+
color: Object.freeze({ type: "color", uiDefault: "colors.primary" }),
|
|
378
|
+
width: Object.freeze({ type: "size", min: 0, max: 8 }),
|
|
379
|
+
}),
|
|
380
|
+
targets: Object.freeze({
|
|
381
|
+
"appstudio.button": Object.freeze({ color: "ringColor", width: "ringWidth" }),
|
|
382
|
+
}),
|
|
383
|
+
}),
|
|
280
384
|
});
|
|
281
385
|
|
|
282
386
|
// sc-4505 — the value types a widget event payload field may declare. Narrower
|
|
@@ -1459,14 +1563,23 @@ const PLATFORMS = ["web", "native"];
|
|
|
1459
1563
|
|
|
1460
1564
|
// REQ-WIDGET-ACTION — server-side actions a widget may declare in its
|
|
1461
1565
|
// manifest. Each runs in the shared isolated-vm action runner (see backend
|
|
1462
|
-
// action-runner.service.js) on a cron schedule
|
|
1463
|
-
//
|
|
1464
|
-
// export parity.
|
|
1566
|
+
// action-runner.service.js) on a cron schedule, in response to a record CRUD
|
|
1567
|
+
// event, or on demand — NEVER in the rendered app, so they never affect
|
|
1568
|
+
// Player ↔ export parity.
|
|
1569
|
+
//
|
|
1570
|
+
// This is what a MANIFEST may declare. The backend Action model additionally
|
|
1571
|
+
// accepts 'app' and 'http_post' (sc-5366), which expose a script to a
|
|
1572
|
+
// published app's buttons or to a public webhook — those are the installing
|
|
1573
|
+
// workspace's decision about running someone else's code, so an operator
|
|
1574
|
+
// grants them and a manifest may not ask for them. `validateManifest` and the
|
|
1575
|
+
// backend's `_validateWidgetDeclared` both reject them, so the linter and the
|
|
1576
|
+
// server agree about what a manifest declared.
|
|
1465
1577
|
const ACTION_TRIGGER_TYPES = [
|
|
1466
1578
|
"schedule",
|
|
1467
1579
|
"record_created",
|
|
1468
1580
|
"record_updated",
|
|
1469
1581
|
"record_deleted",
|
|
1582
|
+
"manual",
|
|
1470
1583
|
];
|
|
1471
1584
|
// Globals the action script runs against (the runner's surface) — distinct
|
|
1472
1585
|
// from the React/SDK widget surface, so the component import/banned-API
|
|
@@ -1479,6 +1592,9 @@ const ACTION_SCRIPT_GLOBALS = [
|
|
|
1479
1592
|
"notifications",
|
|
1480
1593
|
"console",
|
|
1481
1594
|
"record",
|
|
1595
|
+
// sc-5366 — `{ body }` on a run fired by the inbound `http_post` trigger,
|
|
1596
|
+
// null on every other trigger.
|
|
1597
|
+
"request",
|
|
1482
1598
|
"tenantId",
|
|
1483
1599
|
"triggerType",
|
|
1484
1600
|
"triggerTableId",
|
|
@@ -2939,7 +3055,17 @@ const CONTRACT = deepFreeze({
|
|
|
2939
3055
|
// resolves `{ sent, email_masked }` and never returns the token or the
|
|
2940
3056
|
// link, so it is not an account-takeover primitive; it gates on the same
|
|
2941
3057
|
// `users.write:*` scope as invite / deactivate.
|
|
2942
|
-
|
|
3058
|
+
// 1.68.0: additive (sc-5366) — a manifest action may declare the `manual`
|
|
3059
|
+
// trigger (nothing fires it; the workspace runs it on demand), and every
|
|
3060
|
+
// action script now sees a `request` global — `{ body }` on a run fired
|
|
3061
|
+
// by the workspace-granted `http_post` webhook trigger, null otherwise.
|
|
3062
|
+
// `app` and `http_post` stay operator-granted and are NOT declarable.
|
|
3063
|
+
// 1.69.0: additive (sc-5392) — `themeComponents` gains five scopes:
|
|
3064
|
+
// `accent`, `destructive`, `muted`, `popover` and `ring`, so a
|
|
3065
|
+
// shadcn/Tailwind import's matching custom properties have a themeConfig
|
|
3066
|
+
// home instead of being reported lost. Host-only plumbing: no scope
|
|
3067
|
+
// declares `universalFields`, so a third-party manifest is unaffected.
|
|
3068
|
+
version: "1.69.0",
|
|
2943
3069
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
2944
3070
|
hooks: HOOKS,
|
|
2945
3071
|
primitives: PRIMITIVES,
|
package/dist/index.d.ts
CHANGED
|
@@ -229,17 +229,25 @@ export interface WidgetManifestAction {
|
|
|
229
229
|
* that actually fired.
|
|
230
230
|
*/
|
|
231
231
|
triggerTypes: Array<
|
|
232
|
-
|
|
232
|
+
| "schedule"
|
|
233
|
+
| "record_created"
|
|
234
|
+
| "record_updated"
|
|
235
|
+
| "record_deleted"
|
|
236
|
+
// sc-5366 — nothing fires it; the workspace runs it on demand. The
|
|
237
|
+
// `"app"` and `"http_post"` triggers are operator-granted, not declarable.
|
|
238
|
+
| "manual"
|
|
233
239
|
>;
|
|
234
240
|
/** Required iff `triggerTypes` contains `"schedule"`. node-cron syntax. */
|
|
235
241
|
scheduleCron?: string;
|
|
236
242
|
/** 100–300000. Defaults to 30000 on materialise. */
|
|
237
243
|
timeoutMs?: number;
|
|
238
244
|
/**
|
|
239
|
-
* Runs against `datastore`, `fetch`, `console`, `record`, `
|
|
240
|
-
* `triggerType`, `triggerTableId` — NOT the React/SDK surface.
|
|
241
|
-
* `triggerType` is the trigger that fired THIS run — one of the
|
|
242
|
-
* `triggerTypes`, or `"manual"` / `"app"` for an
|
|
245
|
+
* Runs against `datastore`, `fetch`, `console`, `record`, `request`,
|
|
246
|
+
* `tenantId`, `triggerType`, `triggerTableId` — NOT the React/SDK surface.
|
|
247
|
+
* ≤ 200 KiB. `triggerType` is the trigger that fired THIS run — one of the
|
|
248
|
+
* declared `triggerTypes`, or `"manual"` / `"app"` / `"http_post"` for an
|
|
249
|
+
* operator, button or webhook run. `request` is `{ body }` on a webhook run
|
|
250
|
+
* and `null` otherwise (sc-5366).
|
|
243
251
|
*/
|
|
244
252
|
scriptSource: string;
|
|
245
253
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@colixsystems/widget-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.97.0",
|
|
4
4
|
"description": "Common widget interface for AppStudio. Implements WidgetManifest, WidgetContext, property schema, and helper hooks.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|