@colixsystems/widget-sdk 0.94.0 → 0.96.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 +31 -1
- package/dist/contract.cjs +45 -11
- package/dist/contract.js +45 -11
- package/dist/hooks.js +30 -1
- package/dist/index.d.ts +13 -5
- package/dist/linter.cjs +8 -1
- package/dist/linter.js +10 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -47,7 +47,7 @@ The data layer lives in **four separate domain-client packages**, each instantia
|
|
|
47
47
|
| **FILES** (`ctx.assets`) | `useAsset(id)` | `{ url, file, loading, error, refetch }` | `ctx.assets.get` — no scope |
|
|
48
48
|
| **FILES** | `useAssetsByTag(tag, { type? })` | `{ assets, loading, error, refetch }` | `ctx.assets.list` (unwraps `{ data, meta }` to `assets`) — no scope. `type` defaults to `"image"`; pass `"all"` / `"audio"` / `"video"` / `"document"` to widen. Falsy `tag` collapses to `assets: []` without a round-trip. |
|
|
49
49
|
| **DIRECTORY** (`ctx.directory`) | `useDirectory(query?)` | `{ users, loading, error, refetch }` | `directory.users.list` — `directory.read:users` |
|
|
50
|
-
| **DIRECTORY** | `useUsers(query?)` | `{ users, loading, error, refetch, invite, deactivate, reactivate, remove }` | `directory.users.*` — `users.read:*` (edits also `users.write:*`; `remove()` also `users.delete:*`) |
|
|
50
|
+
| **DIRECTORY** | `useUsers(query?)` | `{ users, loading, error, refetch, invite, deactivate, reactivate, remove, sendPasswordReset }` | `directory.users.*` — `users.read:*` (edits, incl. `sendPasswordReset()`, also `users.write:*`; `remove()` also `users.delete:*`) |
|
|
51
51
|
| **DIRECTORY** | `useGroups(query?)` | `{ groups, loading, error, refetch, create, remove, addMember, removeMember }` | `directory.groups.*` — `groups.read:*` (mutations also `groups.write:*`) |
|
|
52
52
|
| **DIRECTORY** | `useInvites(query?)` | `{ invites, loading, error, refetch, resend, revoke }` | `directory.invites.*` — `users.write:*` + the SystemAcl `users.write` capability (the whole invite surface, list included). `query` is `{ status?, limit?, offset? }` with `status` ∈ `pending \| accepted \| revoked \| expired \| all` (endpoint default `all`). |
|
|
53
53
|
| **DIRECTORY** | `useBankIdLink()` | `{ linked, available, status, qr, message, startLink, refresh, cancel, unlink, refetchStatus, … }` | `directory.bankid.*` — no scope (JWT-gated self-service) |
|
|
@@ -66,6 +66,36 @@ 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.96.0 (contract 1.68.0)
|
|
70
|
+
|
|
71
|
+
**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?".
|
|
72
|
+
|
|
73
|
+
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.
|
|
74
|
+
|
|
75
|
+
`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:
|
|
76
|
+
|
|
77
|
+
```js
|
|
78
|
+
if (triggerType === "http_post") {
|
|
79
|
+
const order = request?.body;
|
|
80
|
+
if (!order?.id) return; // never trust the caller's shape
|
|
81
|
+
await datastore.records("Orders").create({ externalId: order.id });
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`CONTRACT.version` → `1.68.0`. Additive for every existing manifest.
|
|
86
|
+
|
|
87
|
+
### What's new in 0.95.0 (contract 1.67.0)
|
|
88
|
+
|
|
89
|
+
**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.
|
|
90
|
+
|
|
91
|
+
`sendPasswordReset(userId)` mails the **same** self-serve link `POST /auth/app/forgot-password` sends, to that user's **own registered address**, and resolves `{ sent, email_masked }`. It deliberately returns neither the token nor the link, so it is not an account-takeover primitive: an admin can start the recovery, only the user can finish it. `email_masked` (`ad**********@example.com`) is there because a widget caller reads the roster through the privacy-reduced directory projection, which omits email — the confirmation says where the mail went without becoming a new way to read addresses.
|
|
92
|
+
|
|
93
|
+
- **Scope**: `users.write:*`, alongside `invite` / `deactivate` / `reactivate` — the same grant, since mailing someone a link they must act on is strictly less powerful than deactivating them. The `scope-required-for-user-mutation` linter rule covers the new method, so calling it without the scope fails the lint.
|
|
94
|
+
- **Refusals are typed, not silent.** Rejects with a `DirectoryError` coded `USER_INACTIVE` (deactivated — reactivate first) or `NO_PASSWORD_CREDENTIAL` (an INTEGRATION service account authenticates by API key and holds no password). Branch on `code` and render `err.message`; don't offer the action on those rows at all.
|
|
95
|
+
- Issuing a link supersedes any outstanding one for that user, and the send is rate-limited per acting admin.
|
|
96
|
+
- The built-in **User Management** widget gains the row action and a new `onPasswordResetSent` event.
|
|
97
|
+
- **`CONTRACT.version` → `1.67.0`** (additive: one hook method). No existing signature changed, and both hosts get it from the same injected `@colixsystems/directory-client`.
|
|
98
|
+
|
|
69
99
|
### What's new in 0.93.0 (contract 1.66.0)
|
|
70
100
|
|
|
71
101
|
**BREAKING: a widget no longer declares server-side actions.** `manifest.actions` is removed from the contract and **refused** by `validateManifest` — an author who declares it now fails the publish with a message naming the replacement, rather than shipping a widget that quietly carries no automation.
|
package/dist/contract.cjs
CHANGED
|
@@ -957,7 +957,8 @@ const HOOKS = [
|
|
|
957
957
|
// REQ-USERMGMT / REQ-ACL-SYS M3 — AppUser administration. Returns
|
|
958
958
|
// `{ users, loading, error, refetch, invite, deactivate, reactivate, remove }`.
|
|
959
959
|
// Reads need `users.read:*` scope; edit-style mutations (invite /
|
|
960
|
-
// deactivate / reactivate) additionally need
|
|
960
|
+
// deactivate / reactivate / sendPasswordReset) additionally need
|
|
961
|
+
// `users.write:*`, and the
|
|
961
962
|
// destructive `remove` needs `users.delete:*` (SC-902). The `invite`
|
|
962
963
|
// call accepts `{ email, name, groupIds? }` and returns the resulting
|
|
963
964
|
// AppUserInvite row (the email is sent by the host). Mutating users from
|
|
@@ -969,15 +970,22 @@ const HOOKS = [
|
|
|
969
970
|
signature: "useUsers(query?)",
|
|
970
971
|
description:
|
|
971
972
|
"AppUser administration via the injected directory-client at " +
|
|
972
|
-
"ctx.directory.users.{list,get,invite,deactivate,reactivate}.
|
|
973
|
-
"{ users, loading, error, refetch, invite, deactivate,
|
|
973
|
+
"ctx.directory.users.{list,get,invite,deactivate,reactivate,sendPasswordReset}. " +
|
|
974
|
+
"Returns { users, loading, error, refetch, invite, deactivate, " +
|
|
975
|
+
"reactivate, remove, sendPasswordReset }. " +
|
|
974
976
|
"list returns the { data, meta } envelope verbatim — the hook unwraps " +
|
|
975
977
|
"res.data; rows are snake_case (is_active, …). Reads need users.read:* " +
|
|
976
|
-
"scope; edit-style mutations (invite/deactivate/reactivate
|
|
977
|
-
"users.write:*, and the destructive remove()
|
|
978
|
-
"users.delete:* (SC-902). The `invite` call accepts " +
|
|
978
|
+
"scope; edit-style mutations (invite/deactivate/reactivate/" +
|
|
979
|
+
"sendPasswordReset) need users.write:*, and the destructive remove() " +
|
|
980
|
+
"additionally needs users.delete:* (SC-902). The `invite` call accepts " +
|
|
979
981
|
"{ email, name, group_ids? } and returns the resulting AppUserInvite row " +
|
|
980
|
-
"(the email is sent by the host)."
|
|
982
|
+
"(the email is sent by the host). sendPasswordReset(userId) mails the " +
|
|
983
|
+
"standard self-serve reset link to that user's own registered address " +
|
|
984
|
+
"and resolves { sent, email_masked } — never the token or the link, so " +
|
|
985
|
+
"it cannot be used to sign in as them. It rejects with a DirectoryError " +
|
|
986
|
+
"coded USER_INACTIVE (deactivated) or NO_PASSWORD_CREDENTIAL (an " +
|
|
987
|
+
"INTEGRATION service account); surface those as an explanation rather " +
|
|
988
|
+
"than a generic failure.",
|
|
981
989
|
returnShape: {
|
|
982
990
|
users: "Array<{ id, name, email?, role, is_active }> // snake_case rows; unwrapped from { data, meta }",
|
|
983
991
|
loading: "boolean",
|
|
@@ -988,6 +996,8 @@ const HOOKS = [
|
|
|
988
996
|
deactivate: "(userId) => Promise<User> // rejects with DirectoryError",
|
|
989
997
|
reactivate: "(userId) => Promise<User> // rejects with DirectoryError",
|
|
990
998
|
remove: "(userId) => Promise<void> // rejects with DirectoryError",
|
|
999
|
+
sendPasswordReset:
|
|
1000
|
+
"(userId) => Promise<{ sent, email_masked }> // rejects with DirectoryError",
|
|
991
1001
|
},
|
|
992
1002
|
requiredContextSlice: ["directory.users"],
|
|
993
1003
|
scopes: ["users.read:*"],
|
|
@@ -1449,14 +1459,23 @@ const PLATFORMS = ["web", "native"];
|
|
|
1449
1459
|
|
|
1450
1460
|
// REQ-WIDGET-ACTION — server-side actions a widget may declare in its
|
|
1451
1461
|
// manifest. Each runs in the shared isolated-vm action runner (see backend
|
|
1452
|
-
// action-runner.service.js) on a cron schedule
|
|
1453
|
-
//
|
|
1454
|
-
// export parity.
|
|
1462
|
+
// action-runner.service.js) on a cron schedule, in response to a record CRUD
|
|
1463
|
+
// event, or on demand — NEVER in the rendered app, so they never affect
|
|
1464
|
+
// Player ↔ export parity.
|
|
1465
|
+
//
|
|
1466
|
+
// This is what a MANIFEST may declare. The backend Action model additionally
|
|
1467
|
+
// accepts 'app' and 'http_post' (sc-5366), which expose a script to a
|
|
1468
|
+
// published app's buttons or to a public webhook — those are the installing
|
|
1469
|
+
// workspace's decision about running someone else's code, so an operator
|
|
1470
|
+
// grants them and a manifest may not ask for them. `validateManifest` and the
|
|
1471
|
+
// backend's `_validateWidgetDeclared` both reject them, so the linter and the
|
|
1472
|
+
// server agree about what a manifest declared.
|
|
1455
1473
|
const ACTION_TRIGGER_TYPES = [
|
|
1456
1474
|
"schedule",
|
|
1457
1475
|
"record_created",
|
|
1458
1476
|
"record_updated",
|
|
1459
1477
|
"record_deleted",
|
|
1478
|
+
"manual",
|
|
1460
1479
|
];
|
|
1461
1480
|
// Globals the action script runs against (the runner's surface) — distinct
|
|
1462
1481
|
// from the React/SDK widget surface, so the component import/banned-API
|
|
@@ -1469,6 +1488,9 @@ const ACTION_SCRIPT_GLOBALS = [
|
|
|
1469
1488
|
"notifications",
|
|
1470
1489
|
"console",
|
|
1471
1490
|
"record",
|
|
1491
|
+
// sc-5366 — `{ body }` on a run fired by the inbound `http_post` trigger,
|
|
1492
|
+
// null on every other trigger.
|
|
1493
|
+
"request",
|
|
1472
1494
|
"tenantId",
|
|
1473
1495
|
"triggerType",
|
|
1474
1496
|
"triggerTableId",
|
|
@@ -2922,7 +2944,19 @@ const CONTRACT = deepFreeze({
|
|
|
2922
2944
|
// REFUSED by the validator (sc-4879). Server-side automation ships as its own
|
|
2923
2945
|
// marketplace deliverable (`@colixsystems/action-sdk`), which a workspace
|
|
2924
2946
|
// installs and configures separately. A widget renders; it does not automate.
|
|
2925
|
-
|
|
2947
|
+
// 1.67.0: additive (sc-5335) — `useUsers()` gains
|
|
2948
|
+
// `sendPasswordReset(userId)`, which mails the standard self-serve
|
|
2949
|
+
// password-reset link to that user's own registered address so an
|
|
2950
|
+
// app-admin can unblock a locked-out member from inside the app. It
|
|
2951
|
+
// resolves `{ sent, email_masked }` and never returns the token or the
|
|
2952
|
+
// link, so it is not an account-takeover primitive; it gates on the same
|
|
2953
|
+
// `users.write:*` scope as invite / deactivate.
|
|
2954
|
+
// 1.68.0: additive (sc-5366) — a manifest action may declare the `manual`
|
|
2955
|
+
// trigger (nothing fires it; the workspace runs it on demand), and every
|
|
2956
|
+
// action script now sees a `request` global — `{ body }` on a run fired
|
|
2957
|
+
// by the workspace-granted `http_post` webhook trigger, null otherwise.
|
|
2958
|
+
// `app` and `http_post` stay operator-granted and are NOT declarable.
|
|
2959
|
+
version: "1.68.0",
|
|
2926
2960
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
2927
2961
|
hooks: HOOKS,
|
|
2928
2962
|
primitives: PRIMITIVES,
|
package/dist/contract.js
CHANGED
|
@@ -957,7 +957,8 @@ const HOOKS = [
|
|
|
957
957
|
// REQ-USERMGMT / REQ-ACL-SYS M3 — AppUser administration. Returns
|
|
958
958
|
// `{ users, loading, error, refetch, invite, deactivate, reactivate, remove }`.
|
|
959
959
|
// Reads need `users.read:*` scope; edit-style mutations (invite /
|
|
960
|
-
// deactivate / reactivate) additionally need
|
|
960
|
+
// deactivate / reactivate / sendPasswordReset) additionally need
|
|
961
|
+
// `users.write:*`, and the
|
|
961
962
|
// destructive `remove` needs `users.delete:*` (SC-902). The `invite`
|
|
962
963
|
// call accepts `{ email, name, groupIds? }` and returns the resulting
|
|
963
964
|
// AppUserInvite row (the email is sent by the host). Mutating users from
|
|
@@ -969,15 +970,22 @@ const HOOKS = [
|
|
|
969
970
|
signature: "useUsers(query?)",
|
|
970
971
|
description:
|
|
971
972
|
"AppUser administration via the injected directory-client at " +
|
|
972
|
-
"ctx.directory.users.{list,get,invite,deactivate,reactivate}.
|
|
973
|
-
"{ users, loading, error, refetch, invite, deactivate,
|
|
973
|
+
"ctx.directory.users.{list,get,invite,deactivate,reactivate,sendPasswordReset}. " +
|
|
974
|
+
"Returns { users, loading, error, refetch, invite, deactivate, " +
|
|
975
|
+
"reactivate, remove, sendPasswordReset }. " +
|
|
974
976
|
"list returns the { data, meta } envelope verbatim — the hook unwraps " +
|
|
975
977
|
"res.data; rows are snake_case (is_active, …). Reads need users.read:* " +
|
|
976
|
-
"scope; edit-style mutations (invite/deactivate/reactivate
|
|
977
|
-
"users.write:*, and the destructive remove()
|
|
978
|
-
"users.delete:* (SC-902). The `invite` call accepts " +
|
|
978
|
+
"scope; edit-style mutations (invite/deactivate/reactivate/" +
|
|
979
|
+
"sendPasswordReset) need users.write:*, and the destructive remove() " +
|
|
980
|
+
"additionally needs users.delete:* (SC-902). The `invite` call accepts " +
|
|
979
981
|
"{ email, name, group_ids? } and returns the resulting AppUserInvite row " +
|
|
980
|
-
"(the email is sent by the host)."
|
|
982
|
+
"(the email is sent by the host). sendPasswordReset(userId) mails the " +
|
|
983
|
+
"standard self-serve reset link to that user's own registered address " +
|
|
984
|
+
"and resolves { sent, email_masked } — never the token or the link, so " +
|
|
985
|
+
"it cannot be used to sign in as them. It rejects with a DirectoryError " +
|
|
986
|
+
"coded USER_INACTIVE (deactivated) or NO_PASSWORD_CREDENTIAL (an " +
|
|
987
|
+
"INTEGRATION service account); surface those as an explanation rather " +
|
|
988
|
+
"than a generic failure.",
|
|
981
989
|
returnShape: {
|
|
982
990
|
users: "Array<{ id, name, email?, role, is_active }> // snake_case rows; unwrapped from { data, meta }",
|
|
983
991
|
loading: "boolean",
|
|
@@ -988,6 +996,8 @@ const HOOKS = [
|
|
|
988
996
|
deactivate: "(userId) => Promise<User> // rejects with DirectoryError",
|
|
989
997
|
reactivate: "(userId) => Promise<User> // rejects with DirectoryError",
|
|
990
998
|
remove: "(userId) => Promise<void> // rejects with DirectoryError",
|
|
999
|
+
sendPasswordReset:
|
|
1000
|
+
"(userId) => Promise<{ sent, email_masked }> // rejects with DirectoryError",
|
|
991
1001
|
},
|
|
992
1002
|
requiredContextSlice: ["directory.users"],
|
|
993
1003
|
scopes: ["users.read:*"],
|
|
@@ -1449,14 +1459,23 @@ const PLATFORMS = ["web", "native"];
|
|
|
1449
1459
|
|
|
1450
1460
|
// REQ-WIDGET-ACTION — server-side actions a widget may declare in its
|
|
1451
1461
|
// manifest. Each runs in the shared isolated-vm action runner (see backend
|
|
1452
|
-
// action-runner.service.js) on a cron schedule
|
|
1453
|
-
//
|
|
1454
|
-
// export parity.
|
|
1462
|
+
// action-runner.service.js) on a cron schedule, in response to a record CRUD
|
|
1463
|
+
// event, or on demand — NEVER in the rendered app, so they never affect
|
|
1464
|
+
// Player ↔ export parity.
|
|
1465
|
+
//
|
|
1466
|
+
// This is what a MANIFEST may declare. The backend Action model additionally
|
|
1467
|
+
// accepts 'app' and 'http_post' (sc-5366), which expose a script to a
|
|
1468
|
+
// published app's buttons or to a public webhook — those are the installing
|
|
1469
|
+
// workspace's decision about running someone else's code, so an operator
|
|
1470
|
+
// grants them and a manifest may not ask for them. `validateManifest` and the
|
|
1471
|
+
// backend's `_validateWidgetDeclared` both reject them, so the linter and the
|
|
1472
|
+
// server agree about what a manifest declared.
|
|
1455
1473
|
const ACTION_TRIGGER_TYPES = [
|
|
1456
1474
|
"schedule",
|
|
1457
1475
|
"record_created",
|
|
1458
1476
|
"record_updated",
|
|
1459
1477
|
"record_deleted",
|
|
1478
|
+
"manual",
|
|
1460
1479
|
];
|
|
1461
1480
|
// Globals the action script runs against (the runner's surface) — distinct
|
|
1462
1481
|
// from the React/SDK widget surface, so the component import/banned-API
|
|
@@ -1469,6 +1488,9 @@ const ACTION_SCRIPT_GLOBALS = [
|
|
|
1469
1488
|
"notifications",
|
|
1470
1489
|
"console",
|
|
1471
1490
|
"record",
|
|
1491
|
+
// sc-5366 — `{ body }` on a run fired by the inbound `http_post` trigger,
|
|
1492
|
+
// null on every other trigger.
|
|
1493
|
+
"request",
|
|
1472
1494
|
"tenantId",
|
|
1473
1495
|
"triggerType",
|
|
1474
1496
|
"triggerTableId",
|
|
@@ -2922,7 +2944,19 @@ const CONTRACT = deepFreeze({
|
|
|
2922
2944
|
// REFUSED by the validator (sc-4879). Server-side automation ships as its own
|
|
2923
2945
|
// marketplace deliverable (`@colixsystems/action-sdk`), which a workspace
|
|
2924
2946
|
// installs and configures separately. A widget renders; it does not automate.
|
|
2925
|
-
|
|
2947
|
+
// 1.67.0: additive (sc-5335) — `useUsers()` gains
|
|
2948
|
+
// `sendPasswordReset(userId)`, which mails the standard self-serve
|
|
2949
|
+
// password-reset link to that user's own registered address so an
|
|
2950
|
+
// app-admin can unblock a locked-out member from inside the app. It
|
|
2951
|
+
// resolves `{ sent, email_masked }` and never returns the token or the
|
|
2952
|
+
// link, so it is not an account-takeover primitive; it gates on the same
|
|
2953
|
+
// `users.write:*` scope as invite / deactivate.
|
|
2954
|
+
// 1.68.0: additive (sc-5366) — a manifest action may declare the `manual`
|
|
2955
|
+
// trigger (nothing fires it; the workspace runs it on demand), and every
|
|
2956
|
+
// action script now sees a `request` global — `{ body }` on a run fired
|
|
2957
|
+
// by the workspace-granted `http_post` webhook trigger, null otherwise.
|
|
2958
|
+
// `app` and `http_post` stay operator-granted and are NOT declarable.
|
|
2959
|
+
version: "1.68.0",
|
|
2926
2960
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
2927
2961
|
hooks: HOOKS,
|
|
2928
2962
|
primitives: PRIMITIVES,
|
package/dist/hooks.js
CHANGED
|
@@ -3363,8 +3363,37 @@ export function useUsers(query) {
|
|
|
3363
3363
|
throw toDirectoryError(err);
|
|
3364
3364
|
}
|
|
3365
3365
|
}, []);
|
|
3366
|
+
// sc-5335: mail the self-serve reset link to a user who has locked
|
|
3367
|
+
// themselves out. Resolves `{ sent, email_masked }`; the host never returns
|
|
3368
|
+
// the token, so this cannot be used to sign in as that user. Rejects with a
|
|
3369
|
+
// DirectoryError carrying `code` USER_INACTIVE or NO_PASSWORD_CREDENTIAL
|
|
3370
|
+
// when the target cannot receive one.
|
|
3371
|
+
const sendPasswordReset = useCallback(async (userId) => {
|
|
3372
|
+
if (typeof usersRef.current.sendPasswordReset !== "function") {
|
|
3373
|
+
throw toDirectoryError(
|
|
3374
|
+
new Error(
|
|
3375
|
+
"useUsers: this host's directory client predates sendPasswordReset",
|
|
3376
|
+
),
|
|
3377
|
+
);
|
|
3378
|
+
}
|
|
3379
|
+
try {
|
|
3380
|
+
return await usersRef.current.sendPasswordReset(userId);
|
|
3381
|
+
} catch (err) {
|
|
3382
|
+
throw toDirectoryError(err);
|
|
3383
|
+
}
|
|
3384
|
+
}, []);
|
|
3366
3385
|
|
|
3367
|
-
return {
|
|
3386
|
+
return {
|
|
3387
|
+
users,
|
|
3388
|
+
loading,
|
|
3389
|
+
error,
|
|
3390
|
+
refetch,
|
|
3391
|
+
invite,
|
|
3392
|
+
deactivate,
|
|
3393
|
+
reactivate,
|
|
3394
|
+
remove,
|
|
3395
|
+
sendPasswordReset,
|
|
3396
|
+
};
|
|
3368
3397
|
}
|
|
3369
3398
|
|
|
3370
3399
|
/**
|
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/dist/linter.cjs
CHANGED
|
@@ -428,7 +428,14 @@ function _translationApiRules(source) {
|
|
|
428
428
|
// REQ-USERMGMT / REQ-ACL-SYS M3 — scope-required-for-user-mutation. See
|
|
429
429
|
// linter.js for the rationale comment. The two files must stay in
|
|
430
430
|
// lockstep (the contract test asserts behaviour-equivalence).
|
|
431
|
-
const USER_MUTATION_METHODS = [
|
|
431
|
+
const USER_MUTATION_METHODS = [
|
|
432
|
+
"invite",
|
|
433
|
+
"deactivate",
|
|
434
|
+
"reactivate",
|
|
435
|
+
// sc-5335 — mailing a reset link is edit-style, so it rides `users.write`
|
|
436
|
+
// alongside deactivate rather than the destructive `users.delete`.
|
|
437
|
+
"sendPasswordReset",
|
|
438
|
+
];
|
|
432
439
|
// SC-902 — destructive user removal is gated on the dedicated
|
|
433
440
|
// `users.delete` capability, NOT `users.write`. A widget that calls
|
|
434
441
|
// useUsers().remove() must declare `users.delete:*` so the static contract
|
package/dist/linter.js
CHANGED
|
@@ -485,7 +485,8 @@ function _translationApiRules(source) {
|
|
|
485
485
|
// REQ-USERMGMT / REQ-ACL-SYS M3 — scope-required-for-user-mutation.
|
|
486
486
|
//
|
|
487
487
|
// A widget that calls `useUsers().invite()` / `.deactivate()` /
|
|
488
|
-
// `.reactivate()` / `.remove()` MUST declare
|
|
488
|
+
// `.reactivate()` / `.sendPasswordReset()` / `.remove()` MUST declare
|
|
489
|
+
// `users.write:*` in its
|
|
489
490
|
// manifest's `requestedScopes`; similarly `useGroups()` mutation methods
|
|
490
491
|
// require `groups.write:*`. The rule is enforced statically so a manifest
|
|
491
492
|
// that drifts from the source (e.g. an author forgot to add the scope
|
|
@@ -498,7 +499,14 @@ function _translationApiRules(source) {
|
|
|
498
499
|
// AST-free; an author whose code happened to spell `.invite(` for an
|
|
499
500
|
// unrelated reason can opt out with a `// @appstudio-skip-scope-check`
|
|
500
501
|
// trailing comment on the offending line.
|
|
501
|
-
const USER_MUTATION_METHODS = [
|
|
502
|
+
const USER_MUTATION_METHODS = [
|
|
503
|
+
"invite",
|
|
504
|
+
"deactivate",
|
|
505
|
+
"reactivate",
|
|
506
|
+
// sc-5335 — mailing a reset link is edit-style, so it rides `users.write`
|
|
507
|
+
// alongside deactivate rather than the destructive `users.delete`.
|
|
508
|
+
"sendPasswordReset",
|
|
509
|
+
];
|
|
502
510
|
// SC-902 — destructive user removal is gated on the dedicated
|
|
503
511
|
// `users.delete` capability, NOT `users.write`. A widget that calls
|
|
504
512
|
// useUsers().remove() must declare `users.delete:*` so the static contract
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@colixsystems/widget-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.96.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",
|