@colixsystems/widget-sdk 0.180.0 → 0.181.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 +29 -6
- package/dist/contract.cjs +19 -6
- package/dist/contract.js +19 -6
- package/dist/index.d.ts +20 -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.181.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.181.0 (contract 1.142.0)
|
|
80
|
+
|
|
81
|
+
**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.
|
|
82
|
+
|
|
83
|
+
- **`useInvites()` rows carry `app_user_id`** — that reserved id. The `resend()` result carries it too.
|
|
84
|
+
- **`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.
|
|
85
|
+
- **`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.
|
|
86
|
+
- **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.
|
|
87
|
+
|
|
88
|
+
Additive: no existing field changed shape, and the scope is unchanged (`users.write:*` plus the `users.write` capability for the whole invite surface).
|
|
89
|
+
|
|
90
|
+
### What's new in 0.180.1 (contract 1.141.0 — unchanged)
|
|
91
|
+
|
|
92
|
+
**A widget built in the Studio turns on the device capability it uses.** The `react-native-nfc-manager` notes said the publisher must enable the NFC tags capability in Publish settings, and told the widget to say so from its error state. Nobody reads a widget's error state before building: a workspace whose widget imported the package shipped an Android build without the `NFC` permission, and the library — which touches the adapter on every activity pause — closed the app at each permission prompt, file picker and camera open. Publishing a widget from Mason or the widget agent now turns on every device capability its source uses (NFC tags for this package; step counting and activity data for `Pedometer` from `expo-sensors`, and only for `Pedometer`). A widget installed any other way is unchanged: its publisher still enables the capability in Publish settings. The notes now say both, and the error state says what is true on the phone — this build does not include NFC and needs a new one. No export, type or rule changed.
|
|
78
93
|
|
|
79
94
|
### What's new in 0.180.0 (contract 1.141.0)
|
|
80
95
|
|
|
@@ -644,7 +659,7 @@ Also tightened in this release: the `publicGrant` flags are now type-checked. Th
|
|
|
644
659
|
**NFC tags are vetted, native-only (`react-native-nfc-manager`).** A widget can now read and write NFC tags on the Expo export — NDEF over `NfcManager.requestTechnology(NfcTech.Ndef)`, plus the raw technologies a badge, asset tag or transit card uses: `NfcTech.IsoDep` on both hosts, `NfcTech.MifareClassic` on Android, `NfcTech.MifareIOS` / `FelicaIOS` / `Iso15693IOS` on iOS. (There is no `NfcTech.FeliCa` — check `index.d.ts` before naming one.) This is the last common "tap a physical thing" input the allowlist was missing; barcode and QR already had `useBarcodeScanner()`.
|
|
645
660
|
|
|
646
661
|
- **Native-only, and the browser is why.** Web NFC ships in Chrome on Android alone — Safari, Firefox and every desktop browser omit it — so there is no web build to pair with. Put the import in `widget.native.jsx` and give `widget.web.jsx` a real way in: a typed code field, or the SDK's `useBarcodeScanner()` QR path. The linter's `import-platform-mismatch` enforces the split.
|
|
647
|
-
- **
|
|
662
|
+
- **A per-workspace device capability — and `isSupported()` cannot see it.** The export claims the iOS NFC entitlement and the Android `NFC` permission only while the workspace's **NFC tags** device capability is on. A widget built in the Studio by Mason or the widget agent turns it on when it is published; for any other widget the publisher enables it in Publish settings. Either way it reaches a phone only with the next native build. `isSupported()` probes the **device** (`readingAvailable` on iOS, `hasSystemFeature` on Android), not the gate: on a capable phone running a build made without the capability, it returns `true` and the read then fails — Android throws a `SecurityException` from `registerTagEvent`, iOS cannot open the reader session. So wrap `start()` / `requestTechnology()` in a `try` and render a clear **error state** from the `catch` — say this build of the app does not include NFC and that it needs a new build. That `catch` is an error path, **not** the web build's typed-code substitute: do not give `widget.native.jsx` a permanent manual-entry field beside the tap target, because in a correctly-configured app the radio works and the field is the typing the tag was meant to replace.
|
|
648
663
|
- **Clean up the session.** `cancelTechnologyRequest()` in your effect cleanup; an abandoned request leaves the reader sheet up on iOS.
|
|
649
664
|
- **It is a write capability too.** Read with `getNdefMessage()`; write with `writeNdefMessage(bytes)`, `formatNdef(bytes)` (Android only, for a blank tag) and `transceive(bytes)` on the `IsoDep` / `NfcA` / `NfcV` handlers. One capability covers both — Android expresses read and write through the single `android.permission.NFC` — so the workspace owner who enables it is consenting to every widget in the workspace being able to rewrite the tags their users tap. Raw ISO7816 `transceive` on iOS additionally needs `select-identifiers` on the entitlement, which the export does not declare; NDEF read and write work on both hosts.
|
|
650
665
|
- **`makeReadOnly()` is irreversible.** It permanently locks the tag — no later write will ever succeed, on any device, by any app. Call it only where the feature genuinely wants a one-time-programmable tag, and never as a cleanup step after writing.
|
|
@@ -2110,9 +2125,17 @@ function PendingInvites() {
|
|
|
2110
2125
|
}
|
|
2111
2126
|
```
|
|
2112
2127
|
|
|
2113
|
-
Rows are snake_case: `{ id, email, name, group_ids, status, expires_at,
|
|
2114
|
-
revoked_at, created_at }`. Trust the server-computed `status` rather than
|
|
2115
|
-
`expires_at` against the device clock.
|
|
2128
|
+
Rows are snake_case: `{ id, app_user_id, email, name, group_ids, status, expires_at,
|
|
2129
|
+
accepted_at, revoked_at, created_at }`. Trust the server-computed `status` rather than
|
|
2130
|
+
comparing `expires_at` against the device clock.
|
|
2131
|
+
|
|
2132
|
+
`query` also takes `q` (a case-insensitive substring match on the invite's name or
|
|
2133
|
+
email) and `app_user_id`. Each row's `app_user_id` is the id the invitee's user account
|
|
2134
|
+
will have once they accept. A `USER` column may hold that id before the person has
|
|
2135
|
+
joined — it then reads back as `{ id, name, invited: true }`, with no `email` — and
|
|
2136
|
+
`useInvites({ app_user_id: value.id, status: 'open', limit: 1 })` returns the invite
|
|
2137
|
+
behind it. `status: 'open'` is every invite that is neither accepted nor revoked
|
|
2138
|
+
(`pending` or `expired`); a re-invite keeps the id, so without it earlier invites match too.
|
|
2116
2139
|
|
|
2117
2140
|
**The gate is `users.write`, not `users.read`.** A pending invite exposes the email of
|
|
2118
2141
|
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>",
|
|
@@ -3371,7 +3380,7 @@ const VETTED_IMPORTS = [
|
|
|
3371
3380
|
platforms: ["native"],
|
|
3372
3381
|
category: "system",
|
|
3373
3382
|
description:
|
|
3374
|
-
"Read and write NFC tags on the Expo export: NDEF records via NfcManager.requestTechnology(NfcTech.Ndef), plus the raw technologies a badge, asset tag or transit card uses - NfcTech.IsoDep on both hosts, NfcTech.MifareClassic on Android, NfcTech.MifareIOS / FelicaIOS / Iso15693IOS on iOS (there is no NfcTech.FeliCa; check index.d.ts before naming one). Read with ndefHandler.getNdefMessage(); WRITE with writeNdefMessage(bytes), formatNdef(bytes) (Android only, for a blank tag) and transceive(bytes) on the IsoDep / NfcA / NfcV handlers. makeReadOnly() PERMANENTLY locks a tag - it cannot be undone and no later write will ever succeed, so call it only where the brief actually asks for a one-time-programmable tag, never as a tidy-up step. Raw ISO7816 transceive on iOS additionally needs select-identifiers on the entitlement, which this export does not declare - NDEF read and write work on both hosts. Always cancelTechnologyRequest() in a cleanup - an abandoned session leaves the reader sheet up on iOS. Native-only because no browser hands the Player a reader: Web NFC exists in Chrome on Android alone (Safari, Firefox and every desktop browser omit it), so there is no web build to pair with. Author it in widget.native.jsx and give widget.web.jsx a real way in instead - a typed code field, or the SDK's useBarcodeScanner() QR path. The radio is ALSO
|
|
3383
|
+
"Read and write NFC tags on the Expo export: NDEF records via NfcManager.requestTechnology(NfcTech.Ndef), plus the raw technologies a badge, asset tag or transit card uses - NfcTech.IsoDep on both hosts, NfcTech.MifareClassic on Android, NfcTech.MifareIOS / FelicaIOS / Iso15693IOS on iOS (there is no NfcTech.FeliCa; check index.d.ts before naming one). Read with ndefHandler.getNdefMessage(); WRITE with writeNdefMessage(bytes), formatNdef(bytes) (Android only, for a blank tag) and transceive(bytes) on the IsoDep / NfcA / NfcV handlers. makeReadOnly() PERMANENTLY locks a tag - it cannot be undone and no later write will ever succeed, so call it only where the brief actually asks for a one-time-programmable tag, never as a tidy-up step. Raw ISO7816 transceive on iOS additionally needs select-identifiers on the entitlement, which this export does not declare - NDEF read and write work on both hosts. Always cancelTechnologyRequest() in a cleanup - an abandoned session leaves the reader sheet up on iOS. Native-only because no browser hands the Player a reader: Web NFC exists in Chrome on Android alone (Safari, Firefox and every desktop browser omit it), so there is no web build to pair with. Author it in widget.native.jsx and give widget.web.jsx a real way in instead - a typed code field, or the SDK's useBarcodeScanner() QR path. The radio is ALSO a per-workspace device capability: the export claims the iOS NFC entitlement and the Android NFC permission only while the workspace's 'nfc' device capability is on. A widget built in the Studio by Mason or the widget agent turns it on when it is published; for any other widget the publisher enables it in Publish settings. Either way it reaches a phone only with the next native build. isSupported() does NOT see that gate - it probes the DEVICE (readingAvailable on iOS, hasSystemFeature on Android), so on a capable phone running a build made without the capability it returns true and the read then fails: Android throws a SecurityException from registerTagEvent, iOS cannot open the reader session. So wrap start() / requestTechnology() in a try and render a clear ERROR STATE from the catch - say this build of the app does not include NFC and that it needs a new build. That catch is an ERROR path, NOT the web build's typed-code substitute: do not give widget.native.jsx a permanent manual-entry field beside the tap target, because in a correctly-configured app the radio works and the field is the typing the tag was meant to replace. Pinned at 3.17.2, the current stable release (4.x is still beta); a community package, so Expo bundledNativeModules has no version opinion on it.",
|
|
3375
3384
|
},
|
|
3376
3385
|
{
|
|
3377
3386
|
specifier: "decimal.js",
|
|
@@ -4767,7 +4776,11 @@ 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
|
+
version: "1.142.0",
|
|
4771
4784
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
4772
4785
|
hooks: HOOKS,
|
|
4773
4786
|
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>",
|
|
@@ -3367,7 +3376,7 @@ const VETTED_IMPORTS = [
|
|
|
3367
3376
|
platforms: ["native"],
|
|
3368
3377
|
category: "system",
|
|
3369
3378
|
description:
|
|
3370
|
-
"Read and write NFC tags on the Expo export: NDEF records via NfcManager.requestTechnology(NfcTech.Ndef), plus the raw technologies a badge, asset tag or transit card uses - NfcTech.IsoDep on both hosts, NfcTech.MifareClassic on Android, NfcTech.MifareIOS / FelicaIOS / Iso15693IOS on iOS (there is no NfcTech.FeliCa; check index.d.ts before naming one). Read with ndefHandler.getNdefMessage(); WRITE with writeNdefMessage(bytes), formatNdef(bytes) (Android only, for a blank tag) and transceive(bytes) on the IsoDep / NfcA / NfcV handlers. makeReadOnly() PERMANENTLY locks a tag - it cannot be undone and no later write will ever succeed, so call it only where the brief actually asks for a one-time-programmable tag, never as a tidy-up step. Raw ISO7816 transceive on iOS additionally needs select-identifiers on the entitlement, which this export does not declare - NDEF read and write work on both hosts. Always cancelTechnologyRequest() in a cleanup - an abandoned session leaves the reader sheet up on iOS. Native-only because no browser hands the Player a reader: Web NFC exists in Chrome on Android alone (Safari, Firefox and every desktop browser omit it), so there is no web build to pair with. Author it in widget.native.jsx and give widget.web.jsx a real way in instead - a typed code field, or the SDK's useBarcodeScanner() QR path. The radio is ALSO
|
|
3379
|
+
"Read and write NFC tags on the Expo export: NDEF records via NfcManager.requestTechnology(NfcTech.Ndef), plus the raw technologies a badge, asset tag or transit card uses - NfcTech.IsoDep on both hosts, NfcTech.MifareClassic on Android, NfcTech.MifareIOS / FelicaIOS / Iso15693IOS on iOS (there is no NfcTech.FeliCa; check index.d.ts before naming one). Read with ndefHandler.getNdefMessage(); WRITE with writeNdefMessage(bytes), formatNdef(bytes) (Android only, for a blank tag) and transceive(bytes) on the IsoDep / NfcA / NfcV handlers. makeReadOnly() PERMANENTLY locks a tag - it cannot be undone and no later write will ever succeed, so call it only where the brief actually asks for a one-time-programmable tag, never as a tidy-up step. Raw ISO7816 transceive on iOS additionally needs select-identifiers on the entitlement, which this export does not declare - NDEF read and write work on both hosts. Always cancelTechnologyRequest() in a cleanup - an abandoned session leaves the reader sheet up on iOS. Native-only because no browser hands the Player a reader: Web NFC exists in Chrome on Android alone (Safari, Firefox and every desktop browser omit it), so there is no web build to pair with. Author it in widget.native.jsx and give widget.web.jsx a real way in instead - a typed code field, or the SDK's useBarcodeScanner() QR path. The radio is ALSO a per-workspace device capability: the export claims the iOS NFC entitlement and the Android NFC permission only while the workspace's 'nfc' device capability is on. A widget built in the Studio by Mason or the widget agent turns it on when it is published; for any other widget the publisher enables it in Publish settings. Either way it reaches a phone only with the next native build. isSupported() does NOT see that gate - it probes the DEVICE (readingAvailable on iOS, hasSystemFeature on Android), so on a capable phone running a build made without the capability it returns true and the read then fails: Android throws a SecurityException from registerTagEvent, iOS cannot open the reader session. So wrap start() / requestTechnology() in a try and render a clear ERROR STATE from the catch - say this build of the app does not include NFC and that it needs a new build. That catch is an ERROR path, NOT the web build's typed-code substitute: do not give widget.native.jsx a permanent manual-entry field beside the tap target, because in a correctly-configured app the radio works and the field is the typing the tag was meant to replace. Pinned at 3.17.2, the current stable release (4.x is still beta); a community package, so Expo bundledNativeModules has no version opinion on it.",
|
|
3371
3380
|
},
|
|
3372
3381
|
{
|
|
3373
3382
|
specifier: "decimal.js",
|
|
@@ -4763,7 +4772,11 @@ 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
|
+
version: "1.142.0",
|
|
4767
4780
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
4768
4781
|
hooks: HOOKS,
|
|
4769
4782
|
primitives: PRIMITIVES,
|
package/dist/index.d.ts
CHANGED
|
@@ -2185,6 +2185,12 @@ export type AppUserInviteStatus =
|
|
|
2185
2185
|
|
|
2186
2186
|
export interface AppUserInviteRow {
|
|
2187
2187
|
id: string;
|
|
2188
|
+
/**
|
|
2189
|
+
* The id the invitee's user account will have once they accept. On every
|
|
2190
|
+
* `useInvites()` row and the `resend()` result; `useUsers().invite()` does
|
|
2191
|
+
* not return it.
|
|
2192
|
+
*/
|
|
2193
|
+
app_user_id?: string;
|
|
2188
2194
|
email: string;
|
|
2189
2195
|
/** Server-computed lifecycle value — prefer it over re-deriving expiry. */
|
|
2190
2196
|
status: AppUserInviteStatus;
|
|
@@ -2258,8 +2264,20 @@ export function useGroups(query?: GroupsQuery): GroupsApi;
|
|
|
2258
2264
|
// sc-5097 — pending AppUser invite administration.
|
|
2259
2265
|
|
|
2260
2266
|
export interface InvitesQuery {
|
|
2261
|
-
/**
|
|
2262
|
-
|
|
2267
|
+
/**
|
|
2268
|
+
* Defaults to "all" server-side; pass "pending" for outstanding invites.
|
|
2269
|
+
* "open" is "pending" or "expired": neither accepted nor revoked.
|
|
2270
|
+
*/
|
|
2271
|
+
status?: AppUserInviteStatus | "open" | "all";
|
|
2272
|
+
/** Case-insensitive substring match on the invite's name or email. */
|
|
2273
|
+
q?: string;
|
|
2274
|
+
/**
|
|
2275
|
+
* Only the invites reserving this user id (a row's `app_user_id`) — resolves
|
|
2276
|
+
* the person behind an id a USER column holds before they have joined. A
|
|
2277
|
+
* re-invite keeps the id, so add `status: "open"` for the one that still
|
|
2278
|
+
* reserves it. An id no invite reserves returns no rows.
|
|
2279
|
+
*/
|
|
2280
|
+
app_user_id?: string;
|
|
2263
2281
|
limit?: number;
|
|
2264
2282
|
offset?: number;
|
|
2265
2283
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@colixsystems/widget-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.181.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",
|