@colixsystems/widget-sdk 0.180.1 → 0.182.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 +28 -5
- package/dist/contract.cjs +20 -5
- package/dist/contract.js +20 -5
- package/dist/index.d.ts +26 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -59,7 +59,7 @@ The data layer lives in **four separate domain-client packages**, each instantia
|
|
|
59
59
|
| **DIRECTORY** (`ctx.directory`) | `useDirectory(query)` (optional: query) | `{ users, loading, error, refetch }` | `directory.users.list` — `directory.read:users` |
|
|
60
60
|
| **DIRECTORY** | `useUsers(query)` (optional: query) | `{ users, loading, error, refetch, invite, deactivate, reactivate, remove, sendPasswordReset }` | `directory.users.*` — `users.read:*` plus the `users.read` (or `users.write`) capability, else `FORBIDDEN` (edits, incl. `sendPasswordReset()`, also `users.write:*`; `remove()` also `users.delete:*`) |
|
|
61
61
|
| **DIRECTORY** | `useGroups(query)` (optional: query) | `{ groups, loading, error, refetch, create, remove, addMember, removeMember }` | `directory.groups.*` — `groups.read:*` (mutations also `groups.write:*`) |
|
|
62
|
-
| **DIRECTORY** | `useInvites(query)` (optional: 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`). |
|
|
62
|
+
| **DIRECTORY** | `useInvites(query)` (optional: 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?, q?, app_user_id?, limit?, offset? }` with `status` ∈ `pending \| accepted \| revoked \| expired \| open \| all` (endpoint default `all`; `open` = `pending` or `expired`); `q` matches the invite's name or email, `app_user_id` returns the invites reserving that user id. |
|
|
63
63
|
| **DIRECTORY** | `useBankIdLink()` | `{ linked, available, status, qr, message, startLink, refresh, cancel, unlink, refetchStatus, … }` | `directory.bankid.*` — no scope (JWT-gated self-service) |
|
|
64
64
|
| **FILESTORE** (`ctx.filestore`) | `usePdfExport({ spaceType, folderId })` (optional: spaceType, folderId) | `{ openPdf, opening, canOpenPdf, exportToPdf, exporting, error, lastExported }` | `ctx.filestore.files.renderPdf` + `ctx.device.pdfViewer`, `ctx.filestore.files.exportPdf` — `files.write:*`. `openPdf(html, { fileName? })` renders the HTML to a PDF server-side and OPENS it (web: PDF tab; native: print / share sheet) without saving. `exportToPdf(html, { fileName?, folderId? })` renders it and saves it as a file (`application/pdf`). Same server-side renderer on web + native. |
|
|
65
65
|
| **PAYMENTS** (`ctx.payments`) | `usePayments()` | `{ requestPayment, getPayment }` | `ctx.payments.*` — `payments.charge:appUser`. Rejects with `PaymentError { code, message, retryable }`; when `retryable` is `false` show `message` and drop the retry. Charges are accepted ONLY in the currency the workspace sells in — omit `currency` and the platform applies it (a disagreeing literal is a publish-blocking `payment-currency` finding). |
|
|
@@ -74,7 +74,22 @@ See the design reference for the full architecture: [`docs/architecture/widget-m
|
|
|
74
74
|
|
|
75
75
|
## Status
|
|
76
76
|
|
|
77
|
-
`v0.
|
|
77
|
+
`v0.182.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**.
|
|
78
|
+
|
|
79
|
+
### What's new in 0.182.0 (contract 1.143.0)
|
|
80
|
+
|
|
81
|
+
**A `datastoreTemplate` relation decides what happens when the row it points at is deleted.** A `ONE_TO_ONE` / `ONE_TO_MANY` RELATION column may declare `onDelete`. With `"SET_NULL"`, the default, the row is kept and its reference is emptied. With `"CASCADE"`, the row is deleted along with the row it points at, and anything that cascades from it is deleted in turn. A `MANY_TO_MANY` column takes no `onDelete`: deleting a row on either side only removes its links. The platform applies the rule on every delete, so a widget that deletes a parent row never has to delete or clear its children itself. Use `CASCADE` for rows that mean nothing without their parent (order lines, a post's comments). Additive: a template without it gets `SET_NULL`.
|
|
82
|
+
|
|
83
|
+
### What's new in 0.181.0 (contract 1.142.0)
|
|
84
|
+
|
|
85
|
+
**A `USER` column can point at someone who has been invited but has not joined yet.** Every invite now reserves the id its invitee's user account will have, so a record can be assigned to that person straight away and nothing needs rewriting when they accept.
|
|
86
|
+
|
|
87
|
+
- **`useInvites()` rows carry `app_user_id`** — that reserved id. The `resend()` result carries it too.
|
|
88
|
+
- **`useInvites(query)` takes two more optional keys.** `q` is a case-insensitive substring match on the invite's name or email. `app_user_id` returns only the invites reserving that id, which is how a member-admin widget resolves the person behind a `USER` value before they have joined; an id no invite reserves returns no rows.
|
|
89
|
+
- **`status: 'open'`** is every invite that is neither accepted nor revoked — `pending` or `expired`. A re-invite keeps the reserved id, so `useInvites({ app_user_id, status: 'open' })` is the one invite that still reserves it.
|
|
90
|
+
- **A `USER` column has a third read shape.** A user of the workspace still reads as `{ id, name, email }` and an id that names nobody as `{ id, name: null }`. An invited person who has not joined reads as `{ id, name, invited: true }` — **with no `email`**, so render `name` and treat `email` as optional. Once they accept, the same id reads as an ordinary user and `invited` is gone.
|
|
91
|
+
|
|
92
|
+
Additive: no existing field changed shape, and the scope is unchanged (`users.write:*` plus the `users.write` capability for the whole invite surface).
|
|
78
93
|
|
|
79
94
|
### What's new in 0.180.1 (contract 1.141.0 — unchanged)
|
|
80
95
|
|
|
@@ -2114,9 +2129,17 @@ function PendingInvites() {
|
|
|
2114
2129
|
}
|
|
2115
2130
|
```
|
|
2116
2131
|
|
|
2117
|
-
Rows are snake_case: `{ id, email, name, group_ids, status, expires_at,
|
|
2118
|
-
revoked_at, created_at }`. Trust the server-computed `status` rather than
|
|
2119
|
-
`expires_at` against the device clock.
|
|
2132
|
+
Rows are snake_case: `{ id, app_user_id, email, name, group_ids, status, expires_at,
|
|
2133
|
+
accepted_at, revoked_at, created_at }`. Trust the server-computed `status` rather than
|
|
2134
|
+
comparing `expires_at` against the device clock.
|
|
2135
|
+
|
|
2136
|
+
`query` also takes `q` (a case-insensitive substring match on the invite's name or
|
|
2137
|
+
email) and `app_user_id`. Each row's `app_user_id` is the id the invitee's user account
|
|
2138
|
+
will have once they accept. A `USER` column may hold that id before the person has
|
|
2139
|
+
joined — it then reads back as `{ id, name, invited: true }`, with no `email` — and
|
|
2140
|
+
`useInvites({ app_user_id: value.id, status: 'open', limit: 1 })` returns the invite
|
|
2141
|
+
behind it. `status: 'open'` is every invite that is neither accepted nor revoked
|
|
2142
|
+
(`pending` or `expired`); a re-invite keeps the id, so without it earlier invites match too.
|
|
2120
2143
|
|
|
2121
2144
|
**The gate is `users.write`, not `users.read`.** A pending invite exposes the email of
|
|
2122
2145
|
someone who is not a member yet, so the backend gates the entire invite surface —
|
package/dist/contract.cjs
CHANGED
|
@@ -1787,9 +1787,18 @@ const HOOKS = [
|
|
|
1787
1787
|
"returns the { data, meta } envelope verbatim — the hook unwraps " +
|
|
1788
1788
|
"res.data; rows are snake_case (email, status, expires_at, …) and " +
|
|
1789
1789
|
"status is the server-computed pending/accepted/revoked/expired " +
|
|
1790
|
-
"value. query is { status?, limit?, offset? } passed
|
|
1791
|
-
"endpoint defaults to status 'all', so pass
|
|
1792
|
-
"for the usual outstanding-invite list
|
|
1790
|
+
"value. query is { status?, q?, app_user_id?, limit?, offset? } passed " +
|
|
1791
|
+
"verbatim; the endpoint defaults to status 'all', so pass " +
|
|
1792
|
+
"{ status: 'pending' } for the usual outstanding-invite list; " +
|
|
1793
|
+
"status 'open' is every invite neither accepted nor revoked (pending " +
|
|
1794
|
+
"or expired). q is a " +
|
|
1795
|
+
"case-insensitive substring match on the invite's name or email. " +
|
|
1796
|
+
"Each row's app_user_id is the id the invitee's user account will " +
|
|
1797
|
+
"have once they accept; a USER column may already hold it, and " +
|
|
1798
|
+
"{ app_user_id, status: 'open' } returns the invite behind such an id " +
|
|
1799
|
+
"(a re-invite keeps the id, so without the status earlier invites " +
|
|
1800
|
+
"match too; an id no invite reserves returns no rows). The WHOLE " +
|
|
1801
|
+
"invite surface — " +
|
|
1793
1802
|
"list included — is gated on users.write:* plus the users.write " +
|
|
1794
1803
|
"SystemAcl capability, because a pending invite exposes the email of " +
|
|
1795
1804
|
"someone who is not a member yet; a users.read-only caller cannot see " +
|
|
@@ -1799,7 +1808,7 @@ const HOOKS = [
|
|
|
1799
1808
|
"refetch on success.",
|
|
1800
1809
|
returnShape: {
|
|
1801
1810
|
invites:
|
|
1802
|
-
"Array<{ id, email, name, group_ids, status, expires_at, accepted_at, revoked_at, created_at }> // snake_case rows; unwrapped from { data, meta }",
|
|
1811
|
+
"Array<{ id, app_user_id, email, name, group_ids, status, expires_at, accepted_at, revoked_at, created_at }> // snake_case rows; unwrapped from { data, meta }",
|
|
1803
1812
|
loading: "boolean",
|
|
1804
1813
|
error: "DirectoryError | null // { code, message, retryable }",
|
|
1805
1814
|
refetch: "() => Promise<void>",
|
|
@@ -4767,7 +4776,13 @@ const CONTRACT = deepFreeze({
|
|
|
4767
4776
|
// 1.141.0: additive (sc-9346) — inside a section that paints its own fill,
|
|
4768
4777
|
// `theme.colors.backdrop` is that fill, and `<Button>` re-inks a variant
|
|
4769
4778
|
// colour that cannot be read against it by the rule its text follows.
|
|
4770
|
-
|
|
4779
|
+
// 1.142.0: additive (sc-9713) — `useInvites(query)` takes `q`,
|
|
4780
|
+
// `app_user_id` and `status: "open"`, and each invite row carries
|
|
4781
|
+
// `app_user_id`: the id the invitee's user account will have once they
|
|
4782
|
+
// accept.
|
|
4783
|
+
// 1.143.0: additive (sc-9756) — a datastoreTemplate RELATION column may
|
|
4784
|
+
// declare `onDelete` ("CASCADE" | "SET_NULL") for ONE_TO_ONE/ONE_TO_MANY.
|
|
4785
|
+
version: "1.143.0",
|
|
4771
4786
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
4772
4787
|
hooks: HOOKS,
|
|
4773
4788
|
primitives: PRIMITIVES,
|
package/dist/contract.js
CHANGED
|
@@ -1787,9 +1787,18 @@ const HOOKS = [
|
|
|
1787
1787
|
"returns the { data, meta } envelope verbatim — the hook unwraps " +
|
|
1788
1788
|
"res.data; rows are snake_case (email, status, expires_at, …) and " +
|
|
1789
1789
|
"status is the server-computed pending/accepted/revoked/expired " +
|
|
1790
|
-
"value. query is { status?, limit?, offset? } passed
|
|
1791
|
-
"endpoint defaults to status 'all', so pass
|
|
1792
|
-
"for the usual outstanding-invite list
|
|
1790
|
+
"value. query is { status?, q?, app_user_id?, limit?, offset? } passed " +
|
|
1791
|
+
"verbatim; the endpoint defaults to status 'all', so pass " +
|
|
1792
|
+
"{ status: 'pending' } for the usual outstanding-invite list; " +
|
|
1793
|
+
"status 'open' is every invite neither accepted nor revoked (pending " +
|
|
1794
|
+
"or expired). q is a " +
|
|
1795
|
+
"case-insensitive substring match on the invite's name or email. " +
|
|
1796
|
+
"Each row's app_user_id is the id the invitee's user account will " +
|
|
1797
|
+
"have once they accept; a USER column may already hold it, and " +
|
|
1798
|
+
"{ app_user_id, status: 'open' } returns the invite behind such an id " +
|
|
1799
|
+
"(a re-invite keeps the id, so without the status earlier invites " +
|
|
1800
|
+
"match too; an id no invite reserves returns no rows). The WHOLE " +
|
|
1801
|
+
"invite surface — " +
|
|
1793
1802
|
"list included — is gated on users.write:* plus the users.write " +
|
|
1794
1803
|
"SystemAcl capability, because a pending invite exposes the email of " +
|
|
1795
1804
|
"someone who is not a member yet; a users.read-only caller cannot see " +
|
|
@@ -1799,7 +1808,7 @@ const HOOKS = [
|
|
|
1799
1808
|
"refetch on success.",
|
|
1800
1809
|
returnShape: {
|
|
1801
1810
|
invites:
|
|
1802
|
-
"Array<{ id, email, name, group_ids, status, expires_at, accepted_at, revoked_at, created_at }> // snake_case rows; unwrapped from { data, meta }",
|
|
1811
|
+
"Array<{ id, app_user_id, email, name, group_ids, status, expires_at, accepted_at, revoked_at, created_at }> // snake_case rows; unwrapped from { data, meta }",
|
|
1803
1812
|
loading: "boolean",
|
|
1804
1813
|
error: "DirectoryError | null // { code, message, retryable }",
|
|
1805
1814
|
refetch: "() => Promise<void>",
|
|
@@ -4763,7 +4772,13 @@ const CONTRACT = deepFreeze({
|
|
|
4763
4772
|
// 1.141.0: additive (sc-9346) — inside a section that paints its own fill,
|
|
4764
4773
|
// `theme.colors.backdrop` is that fill, and `<Button>` re-inks a variant
|
|
4765
4774
|
// colour that cannot be read against it by the rule its text follows.
|
|
4766
|
-
|
|
4775
|
+
// 1.142.0: additive (sc-9713) — `useInvites(query)` takes `q`,
|
|
4776
|
+
// `app_user_id` and `status: "open"`, and each invite row carries
|
|
4777
|
+
// `app_user_id`: the id the invitee's user account will have once they
|
|
4778
|
+
// accept.
|
|
4779
|
+
// 1.143.0: additive (sc-9756) — a datastoreTemplate RELATION column may
|
|
4780
|
+
// declare `onDelete` ("CASCADE" | "SET_NULL") for ONE_TO_ONE/ONE_TO_MANY.
|
|
4781
|
+
version: "1.143.0",
|
|
4767
4782
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
4768
4783
|
hooks: HOOKS,
|
|
4769
4784
|
primitives: PRIMITIVES,
|
package/dist/index.d.ts
CHANGED
|
@@ -187,6 +187,12 @@ export interface WidgetDatastoreTemplateColumn {
|
|
|
187
187
|
relationType?: "ONE_TO_ONE" | "ONE_TO_MANY" | "MANY_TO_MANY";
|
|
188
188
|
/** REQ-ACL-RELINHERIT: opt this RELATION column into row-level ACL inheritance. */
|
|
189
189
|
inheritAcl?: boolean;
|
|
190
|
+
/**
|
|
191
|
+
* ONE_TO_ONE / ONE_TO_MANY only: what deleting the referenced row does to
|
|
192
|
+
* this table's rows. `CASCADE` deletes them too; `SET_NULL` (the default)
|
|
193
|
+
* empties the reference. A MANY_TO_MANY link is always removed.
|
|
194
|
+
*/
|
|
195
|
+
onDelete?: "CASCADE" | "SET_NULL";
|
|
190
196
|
/**
|
|
191
197
|
* REQ-DDL-ENCRYPT: store this column's values encrypted at rest (AES-256-GCM
|
|
192
198
|
* under a per-workspace subkey). The widget still reads and writes plaintext
|
|
@@ -2185,6 +2191,12 @@ export type AppUserInviteStatus =
|
|
|
2185
2191
|
|
|
2186
2192
|
export interface AppUserInviteRow {
|
|
2187
2193
|
id: string;
|
|
2194
|
+
/**
|
|
2195
|
+
* The id the invitee's user account will have once they accept. On every
|
|
2196
|
+
* `useInvites()` row and the `resend()` result; `useUsers().invite()` does
|
|
2197
|
+
* not return it.
|
|
2198
|
+
*/
|
|
2199
|
+
app_user_id?: string;
|
|
2188
2200
|
email: string;
|
|
2189
2201
|
/** Server-computed lifecycle value — prefer it over re-deriving expiry. */
|
|
2190
2202
|
status: AppUserInviteStatus;
|
|
@@ -2258,8 +2270,20 @@ export function useGroups(query?: GroupsQuery): GroupsApi;
|
|
|
2258
2270
|
// sc-5097 — pending AppUser invite administration.
|
|
2259
2271
|
|
|
2260
2272
|
export interface InvitesQuery {
|
|
2261
|
-
/**
|
|
2262
|
-
|
|
2273
|
+
/**
|
|
2274
|
+
* Defaults to "all" server-side; pass "pending" for outstanding invites.
|
|
2275
|
+
* "open" is "pending" or "expired": neither accepted nor revoked.
|
|
2276
|
+
*/
|
|
2277
|
+
status?: AppUserInviteStatus | "open" | "all";
|
|
2278
|
+
/** Case-insensitive substring match on the invite's name or email. */
|
|
2279
|
+
q?: string;
|
|
2280
|
+
/**
|
|
2281
|
+
* Only the invites reserving this user id (a row's `app_user_id`) — resolves
|
|
2282
|
+
* the person behind an id a USER column holds before they have joined. A
|
|
2283
|
+
* re-invite keeps the id, so add `status: "open"` for the one that still
|
|
2284
|
+
* reserves it. An id no invite reserves returns no rows.
|
|
2285
|
+
*/
|
|
2286
|
+
app_user_id?: string;
|
|
2263
2287
|
limit?: number;
|
|
2264
2288
|
offset?: number;
|
|
2265
2289
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@colixsystems/widget-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.182.0",
|
|
4
4
|
"description": "Common widget interface for AppStudio. Implements WidgetManifest, WidgetContext, property schema, and helper hooks.",
|
|
5
5
|
"homepage": "https://github.com/Colix-AB/AppStudio",
|
|
6
6
|
"type": "module",
|