@colixsystems/widget-sdk 0.285.0 → 0.287.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 +11 -2
- package/dist/contract.cjs +28 -2
- package/dist/contract.js +28 -2
- package/dist/hooks.js +98 -58
- package/dist/index.d.ts +27 -0
- package/dist/index.js +1 -0
- package/dist/index.native.js +1 -0
- package/dist/navigation.cjs +1 -4
- package/dist/navigation.js +1 -4
- package/dist/plain-object.cjs +11 -0
- package/dist/plain-object.js +4 -0
- package/dist/property-schema.js +1 -4
- package/dist/signing-state.js +4 -3
- package/dist/theme-appearance.cjs +1 -4
- package/dist/theme-appearance.js +1 -4
- package/dist/theme-components.cjs +1 -4
- package/dist/theme-components.js +1 -4
- package/dist/theme-token-refs.js +1 -4
- package/package.json +1 -1
- package/src/contract.js +28 -2
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ The data layer lives in **four separate domain-client packages**, each instantia
|
|
|
10
10
|
| `ctx.directory` | `@colixsystems/directory-client` | `me()`, `users.{list,get,invite,deactivate,reactivate}`, `groups.{list,create,remove,addMember,removeMember,listMine}`, `invites.{list,revoke,resend}` |
|
|
11
11
|
| `ctx.assets` | `@colixsystems/assets-client` | the Asset Manager: `get(id)`, `list(query)`, `upload(formData)` over `/files` — what `useAsset()` (single asset by id) and `useAssetsByTag()` (every asset carrying a tag) resolve |
|
|
12
12
|
| `ctx.widget.assets` | — (built by the host) | the files the widget package bundles: a map of file name → absolute URL, `{}` when it bundles nothing — what `useBundledAsset()` resolves |
|
|
13
|
-
| `ctx.payments` | `@colixsystems/payments-client` | `requestPayment(body)`, `getPayment(id)
|
|
13
|
+
| `ctx.payments` | `@colixsystems/payments-client` | `requestPayment(body)`, `getPayment(id)`, the access plans and the app subscription |
|
|
14
14
|
|
|
15
15
|
**Wire / casing: snake_case end to end.** The clients send and return snake_case **verbatim** (`created_at`, `group_ids`, `can_read`, `amount_cents`, `data_type`, `is_active`, …). There is **no case transform anywhere** — not on the client and not in the backend; the only casing boundary is Prisma `@map` (snake_case field → camelCase column). Author-defined record column values pass through verbatim. Every `list(...)` returns the `{ data, meta }` envelope; the read hooks unwrap `res.data` for you.
|
|
16
16
|
|
|
@@ -75,6 +75,7 @@ The data layer lives in **four separate domain-client packages**, each instantia
|
|
|
75
75
|
| **DIRECTORY** | `useBankIdLink()` | `{ linked, available, status, qr, message, startLink, refresh, cancel, unlink, refetchStatus, … }` | `directory.bankid.*` — no scope (JWT-gated self-service) |
|
|
76
76
|
| **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. |
|
|
77
77
|
| **PAYMENTS** (`ctx.payments`) | `useAccessPlans()` | `{ plans, loading, error, purchaseAvailable, purchase, cancel, refresh }` | `ctx.payments.{listAccessPlans,purchaseAccessPlan,cancelAccessPlan}` — `payments.charge:appUser`. Plans selling access to restricted pages; `purchase(planId)` names only the plan, the server sets price and group. `purchaseAvailable` is `false` in an iOS / Android build (digital goods need store billing): show a visible not-available state. |
|
|
78
|
+
| **PAYMENTS** (`ctx.payments`) | `useAppSubscription()` | `{ subscription, loading, error, cancel, refresh }` | `ctx.payments.{getAppSubscription,cancelAppSubscription}` — `payments.charge:appUser`. The signed-in user's subscription to a paid app (`{ active, renews, access_ends_at }`); `cancel()` stops it renewing, and access runs to `access_ends_at`. |
|
|
78
79
|
| **PAYMENTS** (`ctx.payments`) | `usePayments()` | `{ requestPayment, getPayment }` | `ctx.payments.*` — `payments.charge:appUser`. `requestPayment` requires `kind` (`"physical"` \| `"service"` \| `"digital"`): what is sold. A `"digital"` charge is refused in an iOS or Android build (`DIGITAL_SALE_NEEDS_STORE_BILLING`). 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). |
|
|
79
80
|
| **NOTIFICATIONS** (`ctx.notifications`) | `useSendNotification()` | `{ send, sending, error }` | `ctx.notifications.send` — `notifications.send:appUser`. `send({ recipient_user_id, title, body, link?, payload? })` notifies one app user in the same workspace; call from an event handler (never render); rejects with `NotificationError`. |
|
|
80
81
|
| **NOTIFICATIONS** (`ctx.notifications`) | `useRecordMutes(tableId)` | `{ isMuted, loading, error, refetch }` | `ctx.notifications.mutes` — no scope. Which records of one table the signed-in user has muted, in one request for a whole list; `isMuted(recordId)` turns false when a timed mute lapses, and a `useRecordMute` write shows at once. |
|
|
@@ -89,7 +90,15 @@ See the design reference for the full architecture: [`docs/architecture/widget-m
|
|
|
89
90
|
|
|
90
91
|
## Status
|
|
91
92
|
|
|
92
|
-
`v0.
|
|
93
|
+
`v0.287.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**.
|
|
94
|
+
|
|
95
|
+
### What's new in 0.287.0 (contract 1.235.0)
|
|
96
|
+
|
|
97
|
+
**`useAppSubscription()` — a paid app's subscriber cancels their own subscription.** When a workspace charges its app users a monthly price for the whole app, `useAppSubscription()` returns `{ subscription, loading, error, cancel, refresh }`, where `subscription` is `{ active, renews, access_ends_at }`: `active` says the signed-in user holds paid access now, `renews` that a subscription will charge again, and `access_ends_at` when access ends without a renewal (null while it has no end). Offer a "Cancel subscription" control whenever `renews` is true: `cancel()` stops the renewal, access continues until `access_ends_at`, and nothing is refunded. Access bought in the App Store or Google Play is cancelled in the store and reads `renews: false`. Needs the `payments.charge:appUser` scope. The injected payments client gains `getAppSubscription()` and `cancelAppSubscription()` (`@colixsystems/payments-client` 0.5.0).
|
|
98
|
+
|
|
99
|
+
### What's new in 0.286.0 (contract 1.234.0)
|
|
100
|
+
|
|
101
|
+
**Action scripts only: `previous` and `changedFields`.** `CONTRACT.actionScriptGlobals` lists the two new runner globals a `record_updated` Action script reads: the row before the edit and the columns whose value changed. Nothing changes for widget authors.
|
|
93
102
|
|
|
94
103
|
### What's new in 0.285.0 (contract 1.233.0)
|
|
95
104
|
|
package/dist/contract.cjs
CHANGED
|
@@ -2407,6 +2407,22 @@ const HOOKS = [
|
|
|
2407
2407
|
],
|
|
2408
2408
|
scopes: ["payments.charge:appUser"],
|
|
2409
2409
|
},
|
|
2410
|
+
// sc-15342 — a paid app's subscriber cancels their own subscription.
|
|
2411
|
+
{
|
|
2412
|
+
name: "useAppSubscription",
|
|
2413
|
+
signature: "useAppSubscription()",
|
|
2414
|
+
returnShape: {
|
|
2415
|
+
subscription:
|
|
2416
|
+
"{ active, renews, access_ends_at } | null // the signed-in user's subscription to the app itself (a paid app): active = they hold paid access now; renews = a subscription will charge again (false once cancelled, or when bought in the App Store / Google Play); access_ends_at = when access ends without a renewal (null while it has no end, or when not held)",
|
|
2417
|
+
loading: "boolean",
|
|
2418
|
+
error: "PaymentError | null",
|
|
2419
|
+
cancel:
|
|
2420
|
+
"() => Promise<{ active, renews: false, access_ends_at }> // stop it renewing; access runs to access_ends_at, nothing is refunded — offer it whenever renews is true",
|
|
2421
|
+
refresh: "() => Promise<void>",
|
|
2422
|
+
},
|
|
2423
|
+
requiredContextSlice: ["payments.getAppSubscription", "payments.cancelAppSubscription"],
|
|
2424
|
+
scopes: ["payments.charge:appUser"],
|
|
2425
|
+
},
|
|
2410
2426
|
// sc-4686 — the workspace's charge currency + the only sanctioned way to
|
|
2411
2427
|
// render money. A widget must NOT name a currency: the owner can change it
|
|
2412
2428
|
// after the widget was authored, so a baked symbol goes stale over a charge
|
|
@@ -3945,6 +3961,9 @@ const ACTION_SCRIPT_GLOBALS = [
|
|
|
3945
3961
|
"directory",
|
|
3946
3962
|
"console",
|
|
3947
3963
|
"record",
|
|
3964
|
+
// sc-15609 — on record_updated, the row before the edit and what changed.
|
|
3965
|
+
"previous",
|
|
3966
|
+
"changedFields",
|
|
3948
3967
|
// sc-5366 — `{ body }` on a run fired by the inbound `http_post` trigger,
|
|
3949
3968
|
// null on every other trigger.
|
|
3950
3969
|
"request",
|
|
@@ -4452,7 +4471,7 @@ const WIDGET_CONTEXT_SHAPE = {
|
|
|
4452
4471
|
},
|
|
4453
4472
|
payments: {
|
|
4454
4473
|
description:
|
|
4455
|
-
"Injected @colixsystems/payments-client instance (REQ-BILL-07-WIDGETPAY). { requestPayment(body) -> Promise<{ id, status }>, getPayment(id) -> Promise<payment>, listAccessPlans() -> Promise<{ data, meta }>, purchaseAccessPlan({ plan_id, return_path? }) -> Promise<{ plan_id, status }>, cancelAccessPlan(planId) -> Promise<{ plan_id, renews, access_ends_at }> }; wire is snake_case (amount_cents, return_path). Backs usePayments() and
|
|
4474
|
+
"Injected @colixsystems/payments-client instance (REQ-BILL-07-WIDGETPAY). { requestPayment(body) -> Promise<{ id, status }>, getPayment(id) -> Promise<payment>, listAccessPlans() -> Promise<{ data, meta }>, purchaseAccessPlan({ plan_id, return_path? }) -> Promise<{ plan_id, status }>, cancelAccessPlan(planId) -> Promise<{ plan_id, renews, access_ends_at }>, getAppSubscription() -> Promise<{ active, renews, access_ends_at }>, cancelAppSubscription() -> Promise<{ active, renews, access_ends_at }> }; wire is snake_case (amount_cents, return_path). Backs usePayments(), useAccessPlans() and useAppSubscription(); requires the payments.charge:appUser scope. The host opens hosted Checkout itself — same-tab on web, in-app browser on native (or auto-confirms under the mock provider); the charge settles to the workspace owner.",
|
|
4456
4475
|
required: true,
|
|
4457
4476
|
fields: {
|
|
4458
4477
|
requestPayment: "function",
|
|
@@ -4460,6 +4479,8 @@ const WIDGET_CONTEXT_SHAPE = {
|
|
|
4460
4479
|
listAccessPlans: "function",
|
|
4461
4480
|
purchaseAccessPlan: "function",
|
|
4462
4481
|
cancelAccessPlan: "function",
|
|
4482
|
+
getAppSubscription: "function",
|
|
4483
|
+
cancelAppSubscription: "function",
|
|
4463
4484
|
},
|
|
4464
4485
|
},
|
|
4465
4486
|
// sc-890 — backs useSendNotification().
|
|
@@ -6719,7 +6740,12 @@ const CONTRACT = deepFreeze({
|
|
|
6719
6740
|
// "Rows per page" property.
|
|
6720
6741
|
// 1.233.0: additive (sc-8115) — a `datastoreTemplate` table may declare
|
|
6721
6742
|
// `deleteWhenLastMemberLeaves: true`.
|
|
6722
|
-
|
|
6743
|
+
// 1.234.0: additive (sc-15609) — ACTION_SCRIPT_GLOBALS gains `previous` and
|
|
6744
|
+
// `changedFields`; no widget hook, primitive or manifest field changed.
|
|
6745
|
+
// 1.235.0: additive (sc-15342) — `useAppSubscription()`, and the payments
|
|
6746
|
+
// slice gains `getAppSubscription` / `cancelAppSubscription`: a paid
|
|
6747
|
+
// app's subscriber cancels their own subscription.
|
|
6748
|
+
version: "1.235.0",
|
|
6723
6749
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
6724
6750
|
hooks: HOOKS,
|
|
6725
6751
|
primitives: PRIMITIVES,
|
package/dist/contract.js
CHANGED
|
@@ -2403,6 +2403,22 @@ const HOOKS = [
|
|
|
2403
2403
|
],
|
|
2404
2404
|
scopes: ["payments.charge:appUser"],
|
|
2405
2405
|
},
|
|
2406
|
+
// sc-15342 — a paid app's subscriber cancels their own subscription.
|
|
2407
|
+
{
|
|
2408
|
+
name: "useAppSubscription",
|
|
2409
|
+
signature: "useAppSubscription()",
|
|
2410
|
+
returnShape: {
|
|
2411
|
+
subscription:
|
|
2412
|
+
"{ active, renews, access_ends_at } | null // the signed-in user's subscription to the app itself (a paid app): active = they hold paid access now; renews = a subscription will charge again (false once cancelled, or when bought in the App Store / Google Play); access_ends_at = when access ends without a renewal (null while it has no end, or when not held)",
|
|
2413
|
+
loading: "boolean",
|
|
2414
|
+
error: "PaymentError | null",
|
|
2415
|
+
cancel:
|
|
2416
|
+
"() => Promise<{ active, renews: false, access_ends_at }> // stop it renewing; access runs to access_ends_at, nothing is refunded — offer it whenever renews is true",
|
|
2417
|
+
refresh: "() => Promise<void>",
|
|
2418
|
+
},
|
|
2419
|
+
requiredContextSlice: ["payments.getAppSubscription", "payments.cancelAppSubscription"],
|
|
2420
|
+
scopes: ["payments.charge:appUser"],
|
|
2421
|
+
},
|
|
2406
2422
|
// sc-4686 — the workspace's charge currency + the only sanctioned way to
|
|
2407
2423
|
// render money. A widget must NOT name a currency: the owner can change it
|
|
2408
2424
|
// after the widget was authored, so a baked symbol goes stale over a charge
|
|
@@ -3941,6 +3957,9 @@ const ACTION_SCRIPT_GLOBALS = [
|
|
|
3941
3957
|
"directory",
|
|
3942
3958
|
"console",
|
|
3943
3959
|
"record",
|
|
3960
|
+
// sc-15609 — on record_updated, the row before the edit and what changed.
|
|
3961
|
+
"previous",
|
|
3962
|
+
"changedFields",
|
|
3944
3963
|
// sc-5366 — `{ body }` on a run fired by the inbound `http_post` trigger,
|
|
3945
3964
|
// null on every other trigger.
|
|
3946
3965
|
"request",
|
|
@@ -4448,7 +4467,7 @@ const WIDGET_CONTEXT_SHAPE = {
|
|
|
4448
4467
|
},
|
|
4449
4468
|
payments: {
|
|
4450
4469
|
description:
|
|
4451
|
-
"Injected @colixsystems/payments-client instance (REQ-BILL-07-WIDGETPAY). { requestPayment(body) -> Promise<{ id, status }>, getPayment(id) -> Promise<payment>, listAccessPlans() -> Promise<{ data, meta }>, purchaseAccessPlan({ plan_id, return_path? }) -> Promise<{ plan_id, status }>, cancelAccessPlan(planId) -> Promise<{ plan_id, renews, access_ends_at }> }; wire is snake_case (amount_cents, return_path). Backs usePayments() and
|
|
4470
|
+
"Injected @colixsystems/payments-client instance (REQ-BILL-07-WIDGETPAY). { requestPayment(body) -> Promise<{ id, status }>, getPayment(id) -> Promise<payment>, listAccessPlans() -> Promise<{ data, meta }>, purchaseAccessPlan({ plan_id, return_path? }) -> Promise<{ plan_id, status }>, cancelAccessPlan(planId) -> Promise<{ plan_id, renews, access_ends_at }>, getAppSubscription() -> Promise<{ active, renews, access_ends_at }>, cancelAppSubscription() -> Promise<{ active, renews, access_ends_at }> }; wire is snake_case (amount_cents, return_path). Backs usePayments(), useAccessPlans() and useAppSubscription(); requires the payments.charge:appUser scope. The host opens hosted Checkout itself — same-tab on web, in-app browser on native (or auto-confirms under the mock provider); the charge settles to the workspace owner.",
|
|
4452
4471
|
required: true,
|
|
4453
4472
|
fields: {
|
|
4454
4473
|
requestPayment: "function",
|
|
@@ -4456,6 +4475,8 @@ const WIDGET_CONTEXT_SHAPE = {
|
|
|
4456
4475
|
listAccessPlans: "function",
|
|
4457
4476
|
purchaseAccessPlan: "function",
|
|
4458
4477
|
cancelAccessPlan: "function",
|
|
4478
|
+
getAppSubscription: "function",
|
|
4479
|
+
cancelAppSubscription: "function",
|
|
4459
4480
|
},
|
|
4460
4481
|
},
|
|
4461
4482
|
// sc-890 — backs useSendNotification().
|
|
@@ -6715,7 +6736,12 @@ const CONTRACT = deepFreeze({
|
|
|
6715
6736
|
// "Rows per page" property.
|
|
6716
6737
|
// 1.233.0: additive (sc-8115) — a `datastoreTemplate` table may declare
|
|
6717
6738
|
// `deleteWhenLastMemberLeaves: true`.
|
|
6718
|
-
|
|
6739
|
+
// 1.234.0: additive (sc-15609) — ACTION_SCRIPT_GLOBALS gains `previous` and
|
|
6740
|
+
// `changedFields`; no widget hook, primitive or manifest field changed.
|
|
6741
|
+
// 1.235.0: additive (sc-15342) — `useAppSubscription()`, and the payments
|
|
6742
|
+
// slice gains `getAppSubscription` / `cancelAppSubscription`: a paid
|
|
6743
|
+
// app's subscriber cancels their own subscription.
|
|
6744
|
+
version: "1.235.0",
|
|
6719
6745
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
6720
6746
|
hooks: HOOKS,
|
|
6721
6747
|
primitives: PRIMITIVES,
|
package/dist/hooks.js
CHANGED
|
@@ -7104,91 +7104,131 @@ export function usePayments() {
|
|
|
7104
7104
|
* when `retryable` is false. Requires the `payments.charge:appUser` scope.
|
|
7105
7105
|
*/
|
|
7106
7106
|
export function useAccessPlans() {
|
|
7107
|
-
const
|
|
7107
|
+
const { state, refresh, call } = usePaymentsResource(
|
|
7108
|
+
"useAccessPlans",
|
|
7109
|
+
"listAccessPlans",
|
|
7110
|
+
"Plans are only available for an installed widget running in the app.",
|
|
7111
|
+
_accessPlansState,
|
|
7112
|
+
);
|
|
7113
|
+
|
|
7114
|
+
const purchase = useCallback(
|
|
7115
|
+
async (planId, opts) => {
|
|
7116
|
+
const res = await call("purchaseAccessPlan", {
|
|
7117
|
+
plan_id: planId,
|
|
7118
|
+
return_path: opts && opts.return_path,
|
|
7119
|
+
});
|
|
7120
|
+
if (res && res.status === "ACTIVE") await refresh();
|
|
7121
|
+
return res;
|
|
7122
|
+
},
|
|
7123
|
+
[call, refresh],
|
|
7124
|
+
);
|
|
7125
|
+
|
|
7126
|
+
const cancel = useCallback(
|
|
7127
|
+
async (planId) => {
|
|
7128
|
+
const res = await call("cancelAccessPlan", planId);
|
|
7129
|
+
await refresh();
|
|
7130
|
+
return res;
|
|
7131
|
+
},
|
|
7132
|
+
[call, refresh],
|
|
7133
|
+
);
|
|
7134
|
+
|
|
7135
|
+
return { ...state, purchase, cancel, refresh };
|
|
7136
|
+
}
|
|
7137
|
+
|
|
7138
|
+
function _accessPlansState(res) {
|
|
7139
|
+
return {
|
|
7140
|
+
plans: Array.isArray(res && res.data) ? res.data : [],
|
|
7141
|
+
purchaseAvailable: Boolean(res && res.meta && res.meta.purchase_available),
|
|
7142
|
+
};
|
|
7143
|
+
}
|
|
7144
|
+
|
|
7145
|
+
/**
|
|
7146
|
+
* sc-15342 — the signed-in app user's own subscription to the app itself (a
|
|
7147
|
+
* paid app), and the control to stop it renewing.
|
|
7148
|
+
*
|
|
7149
|
+
* subscription → { active, renews, access_ends_at }. `active`: they hold paid
|
|
7150
|
+
* access to the app now; `renews`: a subscription will charge again;
|
|
7151
|
+
* `access_ends_at`: when access ends without a renewal (null while it has
|
|
7152
|
+
* no end, or when not held).
|
|
7153
|
+
* cancel() → Promise<{ active, renews: false, access_ends_at }> — stop the
|
|
7154
|
+
* subscription renewing. Access continues until `access_ends_at`, then
|
|
7155
|
+
* ends; nothing is refunded. Offer it whenever `renews` is true. Access
|
|
7156
|
+
* bought in the App Store or Google Play is cancelled in the store, so it
|
|
7157
|
+
* reads `renews: false` here.
|
|
7158
|
+
* refresh() → re-read the subscription.
|
|
7159
|
+
*
|
|
7160
|
+
* `error` and `cancel` rejections are a `PaymentError` — show its `message`
|
|
7161
|
+
* when `retryable` is false. Requires the `payments.charge:appUser` scope.
|
|
7162
|
+
*/
|
|
7163
|
+
export function useAppSubscription() {
|
|
7164
|
+
const { state, setState, refresh, call } = usePaymentsResource(
|
|
7165
|
+
"useAppSubscription",
|
|
7166
|
+
"getAppSubscription",
|
|
7167
|
+
"The subscription is only available for an installed widget running in the app.",
|
|
7168
|
+
_appSubscriptionState,
|
|
7169
|
+
);
|
|
7170
|
+
|
|
7171
|
+
const cancel = useCallback(async () => {
|
|
7172
|
+
const res = await call("cancelAppSubscription");
|
|
7173
|
+
setState({ ..._appSubscriptionState(res), loading: false, error: null });
|
|
7174
|
+
return res;
|
|
7175
|
+
}, [call, setState]);
|
|
7176
|
+
|
|
7177
|
+
return { ...state, cancel, refresh };
|
|
7178
|
+
}
|
|
7179
|
+
|
|
7180
|
+
function _appSubscriptionState(subscription) {
|
|
7181
|
+
return { subscription };
|
|
7182
|
+
}
|
|
7183
|
+
|
|
7184
|
+
/**
|
|
7185
|
+
* The payments read hooks' shared core: `readMethod` loads the state on mount
|
|
7186
|
+
* and on `refresh()`, `toState` maps its result, and `call(method, ...args)`
|
|
7187
|
+
* invokes a payments method with its rejection as a `PaymentError`.
|
|
7188
|
+
*/
|
|
7189
|
+
function usePaymentsResource(hookName, readMethod, unavailableMessage, toState) {
|
|
7190
|
+
const ctx = useWidgetContextOrThrow(hookName);
|
|
7108
7191
|
const paymentsRef = useRef(ctx.payments);
|
|
7109
7192
|
paymentsRef.current = ctx.payments;
|
|
7110
|
-
const supported = Boolean(
|
|
7111
|
-
ctx.payments && typeof ctx.payments.listAccessPlans === "function",
|
|
7112
|
-
);
|
|
7193
|
+
const supported = Boolean(ctx.payments && typeof ctx.payments[readMethod] === "function");
|
|
7113
7194
|
const [state, setState] = useState(() => ({
|
|
7114
|
-
|
|
7195
|
+
...toState(null),
|
|
7115
7196
|
loading: supported,
|
|
7116
|
-
error: supported
|
|
7117
|
-
? null
|
|
7118
|
-
: new PaymentError(
|
|
7119
|
-
"PAYMENTS_UNAVAILABLE",
|
|
7120
|
-
"Plans are only available for an installed widget running in the app.",
|
|
7121
|
-
),
|
|
7122
|
-
purchaseAvailable: false,
|
|
7197
|
+
error: supported ? null : new PaymentError("PAYMENTS_UNAVAILABLE", unavailableMessage),
|
|
7123
7198
|
}));
|
|
7124
7199
|
|
|
7125
7200
|
const refresh = useCallback(async () => {
|
|
7126
7201
|
const payments = paymentsRef.current;
|
|
7127
|
-
if (!payments || typeof payments
|
|
7202
|
+
if (!payments || typeof payments[readMethod] !== "function") return;
|
|
7128
7203
|
setState((prev) => ({ ...prev, loading: true }));
|
|
7129
7204
|
try {
|
|
7130
|
-
const res = await payments
|
|
7131
|
-
setState({
|
|
7132
|
-
plans: Array.isArray(res && res.data) ? res.data : [],
|
|
7133
|
-
loading: false,
|
|
7134
|
-
error: null,
|
|
7135
|
-
purchaseAvailable: Boolean(res && res.meta && res.meta.purchase_available),
|
|
7136
|
-
});
|
|
7205
|
+
const res = await payments[readMethod]();
|
|
7206
|
+
setState({ ...toState(res), loading: false, error: null });
|
|
7137
7207
|
} catch (err) {
|
|
7138
7208
|
setState((prev) => ({ ...prev, loading: false, error: toPaymentError(err) }));
|
|
7139
7209
|
}
|
|
7140
|
-
}, []);
|
|
7210
|
+
}, [readMethod, toState]);
|
|
7141
7211
|
|
|
7142
7212
|
useEffect(() => {
|
|
7143
7213
|
if (supported) refresh();
|
|
7144
7214
|
}, [supported, refresh]);
|
|
7145
7215
|
|
|
7146
|
-
const
|
|
7147
|
-
async (
|
|
7148
|
-
const payments = paymentsRef.current;
|
|
7149
|
-
if (!payments || typeof payments.purchaseAccessPlan !== "function") {
|
|
7150
|
-
throw new PaymentError(
|
|
7151
|
-
"PAYMENTS_UNAVAILABLE",
|
|
7152
|
-
"Plans are only available for an installed widget running in the app.",
|
|
7153
|
-
);
|
|
7154
|
-
}
|
|
7155
|
-
let res;
|
|
7156
|
-
try {
|
|
7157
|
-
res = await payments.purchaseAccessPlan({
|
|
7158
|
-
plan_id: planId,
|
|
7159
|
-
return_path: opts && opts.return_path,
|
|
7160
|
-
});
|
|
7161
|
-
} catch (err) {
|
|
7162
|
-
throw toPaymentError(err);
|
|
7163
|
-
}
|
|
7164
|
-
if (res && res.status === "ACTIVE") await refresh();
|
|
7165
|
-
return res;
|
|
7166
|
-
},
|
|
7167
|
-
[refresh],
|
|
7168
|
-
);
|
|
7169
|
-
|
|
7170
|
-
const cancel = useCallback(
|
|
7171
|
-
async (planId) => {
|
|
7216
|
+
const call = useCallback(
|
|
7217
|
+
async (method, ...args) => {
|
|
7172
7218
|
const payments = paymentsRef.current;
|
|
7173
|
-
if (!payments || typeof payments
|
|
7174
|
-
throw new PaymentError(
|
|
7175
|
-
"PAYMENTS_UNAVAILABLE",
|
|
7176
|
-
"Plans are only available for an installed widget running in the app.",
|
|
7177
|
-
);
|
|
7219
|
+
if (!payments || typeof payments[method] !== "function") {
|
|
7220
|
+
throw new PaymentError("PAYMENTS_UNAVAILABLE", unavailableMessage);
|
|
7178
7221
|
}
|
|
7179
|
-
let res;
|
|
7180
7222
|
try {
|
|
7181
|
-
|
|
7223
|
+
return await payments[method](...args);
|
|
7182
7224
|
} catch (err) {
|
|
7183
7225
|
throw toPaymentError(err);
|
|
7184
7226
|
}
|
|
7185
|
-
await refresh();
|
|
7186
|
-
return res;
|
|
7187
7227
|
},
|
|
7188
|
-
[
|
|
7228
|
+
[unavailableMessage],
|
|
7189
7229
|
);
|
|
7190
7230
|
|
|
7191
|
-
return {
|
|
7231
|
+
return { state, setState, refresh, call };
|
|
7192
7232
|
}
|
|
7193
7233
|
|
|
7194
7234
|
/* ============================================================================
|
package/dist/index.d.ts
CHANGED
|
@@ -713,6 +713,8 @@ export interface PaymentsClient {
|
|
|
713
713
|
cancelAccessPlan(
|
|
714
714
|
planId: string,
|
|
715
715
|
): Promise<{ plan_id: string; renews: false; access_ends_at: string | null }>;
|
|
716
|
+
getAppSubscription(): Promise<AppSubscription>;
|
|
717
|
+
cancelAppSubscription(): Promise<AppSubscription>;
|
|
716
718
|
}
|
|
717
719
|
|
|
718
720
|
/**
|
|
@@ -1610,6 +1612,31 @@ export interface AccessPlansApi {
|
|
|
1610
1612
|
*/
|
|
1611
1613
|
export function useAccessPlans(): AccessPlansApi;
|
|
1612
1614
|
|
|
1615
|
+
/** The signed-in app user's own subscription to the app itself (a paid app). */
|
|
1616
|
+
export interface AppSubscription {
|
|
1617
|
+
/** They hold paid access to the app now. */
|
|
1618
|
+
active: boolean;
|
|
1619
|
+
/** A subscription will charge again; false once cancelled, or bought in a store. */
|
|
1620
|
+
renews: boolean;
|
|
1621
|
+
/** ISO time access ends without a renewal; null while it has no end, or when not held. */
|
|
1622
|
+
access_ends_at: string | null;
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
export interface AppSubscriptionApi {
|
|
1626
|
+
subscription: AppSubscription | null;
|
|
1627
|
+
loading: boolean;
|
|
1628
|
+
error: PaymentError | null;
|
|
1629
|
+
/** Stop it renewing; access runs to `access_ends_at`, nothing is refunded. */
|
|
1630
|
+
cancel(): Promise<AppSubscription>;
|
|
1631
|
+
refresh(): Promise<void>;
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
/**
|
|
1635
|
+
* The signed-in app user's subscription to a paid app, and the control to
|
|
1636
|
+
* cancel it themselves. Requires the `payments.charge:appUser` scope.
|
|
1637
|
+
*/
|
|
1638
|
+
export function useAppSubscription(): AppSubscriptionApi;
|
|
1639
|
+
|
|
1613
1640
|
/**
|
|
1614
1641
|
* Rejection thrown by both `usePayments()` callbacks. `code` is the server's
|
|
1615
1642
|
* own reason when it sent one; `message` is its user-safe sentence. Branch on
|
package/dist/index.js
CHANGED
package/dist/index.native.js
CHANGED
package/dist/navigation.cjs
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
5
|
const { CONTRACT, isHexColor, contrastRatio, mixHex, readableTextColor } = require("./contract.cjs");
|
|
6
|
+
const { isPlainObject } = require("./plain-object.cjs");
|
|
6
7
|
|
|
7
8
|
// REQ-NAV-STRUCTURE — the host side of an app's navigation SHAPE and the
|
|
8
9
|
// tokens its footer strip is painted with.
|
|
@@ -27,10 +28,6 @@ const { CONTRACT, isHexColor, contrastRatio, mixHex, readableTextColor } = requi
|
|
|
27
28
|
// existed renders and compiles byte-identically.
|
|
28
29
|
const DEFAULT_MENU_TYPE = "sidebar";
|
|
29
30
|
|
|
30
|
-
function isPlainObject(value) {
|
|
31
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
31
|
/**
|
|
35
32
|
* Resolve a `theme_config.navigation` block to the shape both hosts read.
|
|
36
33
|
*
|
package/dist/navigation.js
CHANGED
|
@@ -17,15 +17,12 @@
|
|
|
17
17
|
// diverge.
|
|
18
18
|
|
|
19
19
|
import { CONTRACT, isHexColor, contrastRatio, mixHex, readableTextColor } from "./contract.js";
|
|
20
|
+
import { isPlainObject } from "./plain-object.js";
|
|
20
21
|
|
|
21
22
|
// Absent or unknown resolves here, so every app authored before menu types
|
|
22
23
|
// existed renders and compiles byte-identically.
|
|
23
24
|
const DEFAULT_MENU_TYPE = "sidebar";
|
|
24
25
|
|
|
25
|
-
function isPlainObject(value) {
|
|
26
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
26
|
/**
|
|
30
27
|
* Resolve a `theme_config.navigation` block to the shape both hosts read.
|
|
31
28
|
*
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// GENERATED from plain-object.js by scripts/generate-cjs.js — DO NOT EDIT.
|
|
2
|
+
// Change plain-object.js, then run `npm run generate:cjs` in packages/widget-sdk.
|
|
3
|
+
"use strict";
|
|
4
|
+
|
|
5
|
+
/** Any non-null, non-array object — a Date, Map or class instance passes. */
|
|
6
|
+
function isPlainObject(value) {
|
|
7
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
8
|
+
}
|
|
9
|
+
module.exports = {
|
|
10
|
+
isPlainObject,
|
|
11
|
+
};
|
package/dist/property-schema.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
import { CORNER_RADIUS_KEYS } from "./corner-radius.js";
|
|
5
5
|
import { SPACING_KEYS } from "./spacing.js";
|
|
6
|
+
import { isPlainObject } from "./plain-object.js";
|
|
6
7
|
|
|
7
8
|
const VALID_TYPES = new Set([
|
|
8
9
|
"string", "number", "boolean",
|
|
@@ -85,10 +86,6 @@ const VALID_TYPES = new Set([
|
|
|
85
86
|
/** The propertySchema field types, for documentation generated from this list. */
|
|
86
87
|
export const PROPERTY_TYPES = Object.freeze([...VALID_TYPES]);
|
|
87
88
|
|
|
88
|
-
function isPlainObject(v) {
|
|
89
|
-
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
89
|
function validateDef(def, path, errors) {
|
|
93
90
|
if (!isPlainObject(def)) {
|
|
94
91
|
errors.push(`${path}: must be an object`);
|
package/dist/signing-state.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
// a sign order's state, what a refusal says, and how an overview is indexed.
|
|
3
3
|
// Kept apart from hooks.js so it runs under plain `node --test`.
|
|
4
4
|
|
|
5
|
+
import { isPlainObject } from "./plain-object.js";
|
|
6
|
+
|
|
5
7
|
/** The server reads at most this many file ids per overview call. */
|
|
6
8
|
export const OVERVIEW_IDS_PER_CALL = 200;
|
|
7
9
|
|
|
@@ -83,12 +85,11 @@ export function readRefusalDetail(err) {
|
|
|
83
85
|
}
|
|
84
86
|
|
|
85
87
|
const nonEmptyString = (value) => typeof value === "string" && value.trim() !== "";
|
|
86
|
-
const isObject = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
87
88
|
|
|
88
89
|
/** One prepare entry as the filestore client takes it, or null when it is neither form. */
|
|
89
90
|
function signerForPrepare(signer) {
|
|
90
91
|
if (nonEmptyString(signer)) return signer;
|
|
91
|
-
if (!
|
|
92
|
+
if (!isPlainObject(signer)) return null;
|
|
92
93
|
const keys = Object.keys(signer).sort().join(",");
|
|
93
94
|
if (keys === "app_user_id" && nonEmptyString(signer.app_user_id)) return signer.app_user_id;
|
|
94
95
|
if (keys === "email,name" && nonEmptyString(signer.name) && nonEmptyString(signer.email)) {
|
|
@@ -111,7 +112,7 @@ export function signerListForPrepare(signers) {
|
|
|
111
112
|
|
|
112
113
|
/** The body of a new link: a corrected `email` when one is given, otherwise nothing. */
|
|
113
114
|
export function resendLinkOptions(options) {
|
|
114
|
-
const email =
|
|
115
|
+
const email = isPlainObject(options) && typeof options.email === "string" ? options.email.trim() : "";
|
|
115
116
|
return email ? { email } : {};
|
|
116
117
|
}
|
|
117
118
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
5
|
const { isHexColor, APPEARANCE_PREFERENCES } = require("./contract.cjs");
|
|
6
|
+
const { isPlainObject } = require("./plain-object.cjs");
|
|
6
7
|
|
|
7
8
|
// APPEARANCE_PREFERENCES: what an app opens on and a user may choose.
|
|
8
9
|
|
|
@@ -21,10 +22,6 @@ const APPEARANCE_THEME_KEYS = ["dark", "appearance"];
|
|
|
21
22
|
/** The two palettes an app can render. */
|
|
22
23
|
const APPEARANCE_MODES = Object.freeze(["light", "dark"]);
|
|
23
24
|
|
|
24
|
-
function isPlainObject(value) {
|
|
25
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
25
|
// A gradient is one colour value: its angle and type travel with its stops.
|
|
29
26
|
function isGradient(value) {
|
|
30
27
|
return isPlainObject(value) && isHexColor(value.from) && isHexColor(value.to);
|
package/dist/theme-appearance.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// APPEARANCE_PREFERENCES: what an app opens on and a user may choose.
|
|
2
2
|
import { isHexColor, APPEARANCE_PREFERENCES } from "./contract.js";
|
|
3
|
+
import { isPlainObject } from "./plain-object.js";
|
|
3
4
|
|
|
4
5
|
export { APPEARANCE_PREFERENCES };
|
|
5
6
|
|
|
@@ -17,10 +18,6 @@ const APPEARANCE_THEME_KEYS = ["dark", "appearance"];
|
|
|
17
18
|
/** The two palettes an app can render. */
|
|
18
19
|
export const APPEARANCE_MODES = Object.freeze(["light", "dark"]);
|
|
19
20
|
|
|
20
|
-
function isPlainObject(value) {
|
|
21
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
21
|
// A gradient is one colour value: its angle and type travel with its stops.
|
|
25
22
|
function isGradient(value) {
|
|
26
23
|
return isPlainObject(value) && isHexColor(value.from) && isHexColor(value.to);
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
5
|
const { CONTRACT, normaliseComponentGradient } = require("./contract.cjs");
|
|
6
|
+
const { isPlainObject } = require("./plain-object.cjs");
|
|
6
7
|
|
|
7
8
|
// REQ-THEME-15 (sc-1497) — the host side of per-component style tokens.
|
|
8
9
|
//
|
|
@@ -26,10 +27,6 @@ const { CONTRACT, normaliseComponentGradient } = require("./contract.cjs");
|
|
|
26
27
|
// must never drop a colour the runner already accepted.
|
|
27
28
|
const HEX_COLOR = /^#(?:[0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/i;
|
|
28
29
|
|
|
29
|
-
function isPlainObject(value) {
|
|
30
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
30
|
function coerceToken(def, value) {
|
|
34
31
|
if (def.type === "color") {
|
|
35
32
|
if (typeof value !== "string") return undefined;
|
package/dist/theme-components.js
CHANGED
|
@@ -16,15 +16,12 @@
|
|
|
16
16
|
// literal, so what a planner may emit and what a host applies cannot diverge.
|
|
17
17
|
|
|
18
18
|
import { CONTRACT, normaliseComponentGradient } from "./contract.js";
|
|
19
|
+
import { isPlainObject } from "./plain-object.js";
|
|
19
20
|
|
|
20
21
|
// 3-, 6- and 8-digit hex, matching what the build runner persists — the host
|
|
21
22
|
// must never drop a colour the runner already accepted.
|
|
22
23
|
const HEX_COLOR = /^#(?:[0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/i;
|
|
23
24
|
|
|
24
|
-
function isPlainObject(value) {
|
|
25
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
25
|
function coerceToken(def, value) {
|
|
29
26
|
if (def.type === "color") {
|
|
30
27
|
if (typeof value !== "string") return undefined;
|
package/dist/theme-token-refs.js
CHANGED
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
withHexAlpha,
|
|
24
24
|
THEME_TOKEN_REF_PREFIX,
|
|
25
25
|
} from "./contract.js";
|
|
26
|
+
import { isPlainObject } from "./plain-object.js";
|
|
26
27
|
|
|
27
28
|
// A value is a binding, not a colour, when it carries this prefix. No CSS
|
|
28
29
|
// colour can begin with it, so a stored literal can never be misread as one.
|
|
@@ -67,10 +68,6 @@ export function resolveThemeTokenRef(value, theme) {
|
|
|
67
68
|
return ref.alpha === null ? resolved : withHexAlpha(resolved, ref.alpha);
|
|
68
69
|
}
|
|
69
70
|
|
|
70
|
-
function isPlainObject(value) {
|
|
71
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
71
|
// The same guard the neighbouring style validators apply: these props are
|
|
75
72
|
// author-stored JSON, and assigning such a key while rebuilding an object would
|
|
76
73
|
// silently reseat its prototype instead of carrying the value.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@colixsystems/widget-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.287.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",
|
package/src/contract.js
CHANGED
|
@@ -2403,6 +2403,22 @@ const HOOKS = [
|
|
|
2403
2403
|
],
|
|
2404
2404
|
scopes: ["payments.charge:appUser"],
|
|
2405
2405
|
},
|
|
2406
|
+
// sc-15342 — a paid app's subscriber cancels their own subscription.
|
|
2407
|
+
{
|
|
2408
|
+
name: "useAppSubscription",
|
|
2409
|
+
signature: "useAppSubscription()",
|
|
2410
|
+
returnShape: {
|
|
2411
|
+
subscription:
|
|
2412
|
+
"{ active, renews, access_ends_at } | null // the signed-in user's subscription to the app itself (a paid app): active = they hold paid access now; renews = a subscription will charge again (false once cancelled, or when bought in the App Store / Google Play); access_ends_at = when access ends without a renewal (null while it has no end, or when not held)",
|
|
2413
|
+
loading: "boolean",
|
|
2414
|
+
error: "PaymentError | null",
|
|
2415
|
+
cancel:
|
|
2416
|
+
"() => Promise<{ active, renews: false, access_ends_at }> // stop it renewing; access runs to access_ends_at, nothing is refunded — offer it whenever renews is true",
|
|
2417
|
+
refresh: "() => Promise<void>",
|
|
2418
|
+
},
|
|
2419
|
+
requiredContextSlice: ["payments.getAppSubscription", "payments.cancelAppSubscription"],
|
|
2420
|
+
scopes: ["payments.charge:appUser"],
|
|
2421
|
+
},
|
|
2406
2422
|
// sc-4686 — the workspace's charge currency + the only sanctioned way to
|
|
2407
2423
|
// render money. A widget must NOT name a currency: the owner can change it
|
|
2408
2424
|
// after the widget was authored, so a baked symbol goes stale over a charge
|
|
@@ -3941,6 +3957,9 @@ const ACTION_SCRIPT_GLOBALS = [
|
|
|
3941
3957
|
"directory",
|
|
3942
3958
|
"console",
|
|
3943
3959
|
"record",
|
|
3960
|
+
// sc-15609 — on record_updated, the row before the edit and what changed.
|
|
3961
|
+
"previous",
|
|
3962
|
+
"changedFields",
|
|
3944
3963
|
// sc-5366 — `{ body }` on a run fired by the inbound `http_post` trigger,
|
|
3945
3964
|
// null on every other trigger.
|
|
3946
3965
|
"request",
|
|
@@ -4448,7 +4467,7 @@ const WIDGET_CONTEXT_SHAPE = {
|
|
|
4448
4467
|
},
|
|
4449
4468
|
payments: {
|
|
4450
4469
|
description:
|
|
4451
|
-
"Injected @colixsystems/payments-client instance (REQ-BILL-07-WIDGETPAY). { requestPayment(body) -> Promise<{ id, status }>, getPayment(id) -> Promise<payment>, listAccessPlans() -> Promise<{ data, meta }>, purchaseAccessPlan({ plan_id, return_path? }) -> Promise<{ plan_id, status }>, cancelAccessPlan(planId) -> Promise<{ plan_id, renews, access_ends_at }> }; wire is snake_case (amount_cents, return_path). Backs usePayments() and
|
|
4470
|
+
"Injected @colixsystems/payments-client instance (REQ-BILL-07-WIDGETPAY). { requestPayment(body) -> Promise<{ id, status }>, getPayment(id) -> Promise<payment>, listAccessPlans() -> Promise<{ data, meta }>, purchaseAccessPlan({ plan_id, return_path? }) -> Promise<{ plan_id, status }>, cancelAccessPlan(planId) -> Promise<{ plan_id, renews, access_ends_at }>, getAppSubscription() -> Promise<{ active, renews, access_ends_at }>, cancelAppSubscription() -> Promise<{ active, renews, access_ends_at }> }; wire is snake_case (amount_cents, return_path). Backs usePayments(), useAccessPlans() and useAppSubscription(); requires the payments.charge:appUser scope. The host opens hosted Checkout itself — same-tab on web, in-app browser on native (or auto-confirms under the mock provider); the charge settles to the workspace owner.",
|
|
4452
4471
|
required: true,
|
|
4453
4472
|
fields: {
|
|
4454
4473
|
requestPayment: "function",
|
|
@@ -4456,6 +4475,8 @@ const WIDGET_CONTEXT_SHAPE = {
|
|
|
4456
4475
|
listAccessPlans: "function",
|
|
4457
4476
|
purchaseAccessPlan: "function",
|
|
4458
4477
|
cancelAccessPlan: "function",
|
|
4478
|
+
getAppSubscription: "function",
|
|
4479
|
+
cancelAppSubscription: "function",
|
|
4459
4480
|
},
|
|
4460
4481
|
},
|
|
4461
4482
|
// sc-890 — backs useSendNotification().
|
|
@@ -6715,7 +6736,12 @@ const CONTRACT = deepFreeze({
|
|
|
6715
6736
|
// "Rows per page" property.
|
|
6716
6737
|
// 1.233.0: additive (sc-8115) — a `datastoreTemplate` table may declare
|
|
6717
6738
|
// `deleteWhenLastMemberLeaves: true`.
|
|
6718
|
-
|
|
6739
|
+
// 1.234.0: additive (sc-15609) — ACTION_SCRIPT_GLOBALS gains `previous` and
|
|
6740
|
+
// `changedFields`; no widget hook, primitive or manifest field changed.
|
|
6741
|
+
// 1.235.0: additive (sc-15342) — `useAppSubscription()`, and the payments
|
|
6742
|
+
// slice gains `getAppSubscription` / `cancelAppSubscription`: a paid
|
|
6743
|
+
// app's subscriber cancels their own subscription.
|
|
6744
|
+
version: "1.235.0",
|
|
6719
6745
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
6720
6746
|
hooks: HOOKS,
|
|
6721
6747
|
primitives: PRIMITIVES,
|