@colixsystems/widget-sdk 0.267.1 → 0.268.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 CHANGED
@@ -86,7 +86,11 @@ See the design reference for the full architecture: [`docs/architecture/widget-m
86
86
 
87
87
  ## Status
88
88
 
89
- `v0.267.1` — 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**.
89
+ `v0.268.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**.
90
+
91
+ ### What's new in 0.268.0 (contract 1.218.0)
92
+
93
+ **`useAction().available` tells the truth about the Action.** It used to be true whenever an Action was bound, so a widget offered a run that could never happen. It now turns false once the server says a run would not reach the script: the Action is switched off or paused, not allowed from the app, gone, or the viewer is not a signed-in app user (signed-out visitors and anonymous accounts can't run Actions). It is asked again when the binding or the signed-in user changes, and stays true while no answer has come. The actions client (`@colixsystems/actions-client` 0.2.0) gains `status(actionId)` for it.
90
94
 
91
95
  ### What's new in 0.267.1 (contract 1.217.0)
92
96
 
package/dist/contract.cjs CHANGED
@@ -2184,7 +2184,12 @@ const HOOKS = [
2184
2184
  "`actionRef` is an Action id an author bound through an `actionRef` " +
2185
2185
  "property — when it is missing, or the host brokers no actions client, " +
2186
2186
  "`available` is false and run() rejects UNAVAILABLE; render a visible " +
2187
- "'pick an action' state, NEVER a blank box. A changed binding clears " +
2187
+ "'pick an action' state, NEVER a blank box. sc-14224: `available` also " +
2188
+ "turns false once the server says a run would not reach the script — " +
2189
+ "the Action is switched off or paused, not allowed from the app, gone, " +
2190
+ "or the viewer is not a signed-in app user (signed-out visitors and " +
2191
+ "anonymous accounts can't run Actions) — so render an 'isn't available' " +
2192
+ "state then too. It stays true while no answer has come. A changed binding clears " +
2188
2193
  "`result` and `error`, and a run that settles after the binding changed " +
2189
2194
  "(or after unmount) writes nothing. ActionError.code: " +
2190
2195
  "INVALID_ACTION_INPUT (400) — the input was refused; " +
@@ -2195,7 +2200,7 @@ const HOOKS = [
2195
2200
  "MARKDOWN, so render it with <RichText value={result} />, not <Text>. " +
2196
2201
  "Reads ctx.actions.",
2197
2202
  returnShape: {
2198
- available: "boolean // false when no Action is bound, or the host brokers none",
2203
+ available: "boolean // false when no Action is bound, the host brokers none, or the server says a run would not reach the script",
2199
2204
  run: "(input) => Promise<unknown> // input is optional; resolves with the Action's respond() value, or null; rejects with ActionError",
2200
2205
  running: "boolean // true while any run is in flight",
2201
2206
  result: "unknown // the last successful run's result; null before the first run",
@@ -4150,13 +4155,14 @@ const WIDGET_CONTEXT_SHAPE = {
4150
4155
  actions: {
4151
4156
  description:
4152
4157
  "sc-13843 — injected @colixsystems/actions-client instance. " +
4153
- "{ run(actionId, input?) -> Promise<{ ok, result }> }. Backs useAction(). " +
4158
+ "{ run(actionId, input?) -> Promise<{ ok, result }>, status(actionId) -> " +
4159
+ "Promise<{ available, reason }> }. Backs useAction(). " +
4154
4160
  "Runs one Action as the signed-in caller; the server refuses an Action " +
4155
4161
  "without the app trigger. Errors carry the server's `code` and `status`. " +
4156
4162
  "Absent on a host that brokers no actions client, where useAction() " +
4157
4163
  "degrades to available: false rather than throwing.",
4158
4164
  required: false,
4159
- fields: { run: "function" },
4165
+ fields: { run: "function", status: "function" },
4160
4166
  },
4161
4167
  linkPreviews: {
4162
4168
  description:
@@ -6472,7 +6478,10 @@ const CONTRACT = deepFreeze({
6472
6478
  // 1.217.0: additive (sc-4747) — `TextInput` documents `keyboardType`,
6473
6479
  // `autoCapitalize`, `autoCorrect`, `autoComplete`, `secureTextEntry`;
6474
6480
  // `useDatastoreSchema` columns document `text_format` (snake_case wire).
6475
- version: "1.217.0",
6481
+ // 1.218.0: additive (sc-14224) — the actions client's `status(actionId)`;
6482
+ // `useAction().available` turns false when the server says a run would
6483
+ // not reach the script.
6484
+ version: "1.218.0",
6476
6485
  sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
6477
6486
  hooks: HOOKS,
6478
6487
  primitives: PRIMITIVES,
package/dist/contract.js CHANGED
@@ -2181,7 +2181,12 @@ const HOOKS = [
2181
2181
  "`actionRef` is an Action id an author bound through an `actionRef` " +
2182
2182
  "property — when it is missing, or the host brokers no actions client, " +
2183
2183
  "`available` is false and run() rejects UNAVAILABLE; render a visible " +
2184
- "'pick an action' state, NEVER a blank box. A changed binding clears " +
2184
+ "'pick an action' state, NEVER a blank box. sc-14224: `available` also " +
2185
+ "turns false once the server says a run would not reach the script — " +
2186
+ "the Action is switched off or paused, not allowed from the app, gone, " +
2187
+ "or the viewer is not a signed-in app user (signed-out visitors and " +
2188
+ "anonymous accounts can't run Actions) — so render an 'isn't available' " +
2189
+ "state then too. It stays true while no answer has come. A changed binding clears " +
2185
2190
  "`result` and `error`, and a run that settles after the binding changed " +
2186
2191
  "(or after unmount) writes nothing. ActionError.code: " +
2187
2192
  "INVALID_ACTION_INPUT (400) — the input was refused; " +
@@ -2192,7 +2197,7 @@ const HOOKS = [
2192
2197
  "MARKDOWN, so render it with <RichText value={result} />, not <Text>. " +
2193
2198
  "Reads ctx.actions.",
2194
2199
  returnShape: {
2195
- available: "boolean // false when no Action is bound, or the host brokers none",
2200
+ available: "boolean // false when no Action is bound, the host brokers none, or the server says a run would not reach the script",
2196
2201
  run: "(input) => Promise<unknown> // input is optional; resolves with the Action's respond() value, or null; rejects with ActionError",
2197
2202
  running: "boolean // true while any run is in flight",
2198
2203
  result: "unknown // the last successful run's result; null before the first run",
@@ -4147,13 +4152,14 @@ const WIDGET_CONTEXT_SHAPE = {
4147
4152
  actions: {
4148
4153
  description:
4149
4154
  "sc-13843 — injected @colixsystems/actions-client instance. " +
4150
- "{ run(actionId, input?) -> Promise<{ ok, result }> }. Backs useAction(). " +
4155
+ "{ run(actionId, input?) -> Promise<{ ok, result }>, status(actionId) -> " +
4156
+ "Promise<{ available, reason }> }. Backs useAction(). " +
4151
4157
  "Runs one Action as the signed-in caller; the server refuses an Action " +
4152
4158
  "without the app trigger. Errors carry the server's `code` and `status`. " +
4153
4159
  "Absent on a host that brokers no actions client, where useAction() " +
4154
4160
  "degrades to available: false rather than throwing.",
4155
4161
  required: false,
4156
- fields: { run: "function" },
4162
+ fields: { run: "function", status: "function" },
4157
4163
  },
4158
4164
  linkPreviews: {
4159
4165
  description:
@@ -6469,7 +6475,10 @@ const CONTRACT = deepFreeze({
6469
6475
  // 1.217.0: additive (sc-4747) — `TextInput` documents `keyboardType`,
6470
6476
  // `autoCapitalize`, `autoCorrect`, `autoComplete`, `secureTextEntry`;
6471
6477
  // `useDatastoreSchema` columns document `text_format` (snake_case wire).
6472
- version: "1.217.0",
6478
+ // 1.218.0: additive (sc-14224) — the actions client's `status(actionId)`;
6479
+ // `useAction().available` turns false when the server says a run would
6480
+ // not reach the script.
6481
+ version: "1.218.0",
6473
6482
  sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
6474
6483
  hooks: HOOKS,
6475
6484
  primitives: PRIMITIVES,
package/dist/hooks.js CHANGED
@@ -3893,18 +3893,46 @@ function toActionError(err) {
3893
3893
  * `available` is false and `run` rejects UNAVAILABLE rather than throwing at
3894
3894
  * render — the widget must render a visible unbound state (CLAUDE.md §8).
3895
3895
  *
3896
+ * sc-14224 — `available` also turns false once the server says a run would
3897
+ * not reach the script: the Action is switched off, paused, not allowed from
3898
+ * the app, gone, or the viewer is not a signed-in app user. It is asked again
3899
+ * when the binding or the signed-in user changes. With no answer (offline, an
3900
+ * older host) it stays true and `run` reports the refusal.
3901
+ *
3896
3902
  * Routes through the injected `@colixsystems/actions-client` at `ctx.actions`,
3897
3903
  * so the Player and the Expo export resolve against the identical client.
3898
3904
  */
3899
3905
  export function useAction(actionRef) {
3900
3906
  const ctx = useWidgetContextOrThrow("useAction");
3901
- const available =
3907
+ const bound =
3902
3908
  Boolean(actionRef) &&
3903
3909
  Boolean(ctx.actions) &&
3904
3910
  typeof ctx.actions.run === "function";
3911
+ const canAsk = bound && typeof ctx.actions.status === "function";
3912
+ const askedAs = `${actionRef}|${(ctx.user && ctx.user.id) || ""}`;
3905
3913
 
3906
3914
  const clientRef = useRef(null);
3907
- clientRef.current = available ? ctx.actions : null;
3915
+ clientRef.current = bound ? ctx.actions : null;
3916
+
3917
+ // The binding and viewer the server last refused, so a rebind starts fresh.
3918
+ const [refusedAs, setRefusedAs] = useState(null);
3919
+ useEffect(() => {
3920
+ const client = clientRef.current;
3921
+ if (!canAsk || !client) return undefined;
3922
+ let live = true;
3923
+ client.status(actionRef).then(
3924
+ (answer) => {
3925
+ if (live) setRefusedAs(answer && answer.available === false ? askedAs : null);
3926
+ },
3927
+ // No answer is not a refusal: keep offering the run.
3928
+ () => {},
3929
+ );
3930
+ return () => {
3931
+ live = false;
3932
+ };
3933
+ // eslint-disable-next-line react-hooks/exhaustive-deps
3934
+ }, [askedAs, canAsk]);
3935
+ const available = bound && refusedAs !== askedAs;
3908
3936
  const actionIdRef = useRef(actionRef);
3909
3937
  actionIdRef.current = actionRef;
3910
3938
 
package/dist/index.d.ts CHANGED
@@ -890,6 +890,8 @@ export interface WidgetContext<TProps = unknown> {
890
890
  /** sc-13843 — injected @colixsystems/actions-client backing useAction(). */
891
891
  actions?: {
892
892
  run(actionId: string, input?: Record<string, unknown> | null): Promise<{ ok: boolean; result: unknown }>;
893
+ /** sc-14224 — whether a run would reach the script; optional on older hosts. */
894
+ status?(actionId: string): Promise<{ available: boolean; reason: string | null }>;
893
895
  };
894
896
  /** Injected @colixsystems/link-preview-client backing useLinkPreview(). */
895
897
  linkPreviews?: { get(url: string): Promise<LinkPreview> };
@@ -1893,7 +1895,11 @@ export interface AgentApi {
1893
1895
  export function useAgent(agentRef: string | null | undefined): AgentApi;
1894
1896
 
1895
1897
  export interface ActionApi {
1896
- /** False when no Action is bound, or the host brokers no actions client. */
1898
+ /**
1899
+ * False when no Action is bound, the host brokers no actions client, or the
1900
+ * server says a run would not reach the script (switched off, not allowed
1901
+ * from the app, or the viewer is not a signed-in app user).
1902
+ */
1897
1903
  available: boolean;
1898
1904
  /**
1899
1905
  * Imperative — call from an event handler, never on mount. Resolves with the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@colixsystems/widget-sdk",
3
- "version": "0.267.1",
3
+ "version": "0.268.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
@@ -2181,7 +2181,12 @@ const HOOKS = [
2181
2181
  "`actionRef` is an Action id an author bound through an `actionRef` " +
2182
2182
  "property — when it is missing, or the host brokers no actions client, " +
2183
2183
  "`available` is false and run() rejects UNAVAILABLE; render a visible " +
2184
- "'pick an action' state, NEVER a blank box. A changed binding clears " +
2184
+ "'pick an action' state, NEVER a blank box. sc-14224: `available` also " +
2185
+ "turns false once the server says a run would not reach the script — " +
2186
+ "the Action is switched off or paused, not allowed from the app, gone, " +
2187
+ "or the viewer is not a signed-in app user (signed-out visitors and " +
2188
+ "anonymous accounts can't run Actions) — so render an 'isn't available' " +
2189
+ "state then too. It stays true while no answer has come. A changed binding clears " +
2185
2190
  "`result` and `error`, and a run that settles after the binding changed " +
2186
2191
  "(or after unmount) writes nothing. ActionError.code: " +
2187
2192
  "INVALID_ACTION_INPUT (400) — the input was refused; " +
@@ -2192,7 +2197,7 @@ const HOOKS = [
2192
2197
  "MARKDOWN, so render it with <RichText value={result} />, not <Text>. " +
2193
2198
  "Reads ctx.actions.",
2194
2199
  returnShape: {
2195
- available: "boolean // false when no Action is bound, or the host brokers none",
2200
+ available: "boolean // false when no Action is bound, the host brokers none, or the server says a run would not reach the script",
2196
2201
  run: "(input) => Promise<unknown> // input is optional; resolves with the Action's respond() value, or null; rejects with ActionError",
2197
2202
  running: "boolean // true while any run is in flight",
2198
2203
  result: "unknown // the last successful run's result; null before the first run",
@@ -4147,13 +4152,14 @@ const WIDGET_CONTEXT_SHAPE = {
4147
4152
  actions: {
4148
4153
  description:
4149
4154
  "sc-13843 — injected @colixsystems/actions-client instance. " +
4150
- "{ run(actionId, input?) -> Promise<{ ok, result }> }. Backs useAction(). " +
4155
+ "{ run(actionId, input?) -> Promise<{ ok, result }>, status(actionId) -> " +
4156
+ "Promise<{ available, reason }> }. Backs useAction(). " +
4151
4157
  "Runs one Action as the signed-in caller; the server refuses an Action " +
4152
4158
  "without the app trigger. Errors carry the server's `code` and `status`. " +
4153
4159
  "Absent on a host that brokers no actions client, where useAction() " +
4154
4160
  "degrades to available: false rather than throwing.",
4155
4161
  required: false,
4156
- fields: { run: "function" },
4162
+ fields: { run: "function", status: "function" },
4157
4163
  },
4158
4164
  linkPreviews: {
4159
4165
  description:
@@ -6469,7 +6475,10 @@ const CONTRACT = deepFreeze({
6469
6475
  // 1.217.0: additive (sc-4747) — `TextInput` documents `keyboardType`,
6470
6476
  // `autoCapitalize`, `autoCorrect`, `autoComplete`, `secureTextEntry`;
6471
6477
  // `useDatastoreSchema` columns document `text_format` (snake_case wire).
6472
- version: "1.217.0",
6478
+ // 1.218.0: additive (sc-14224) — the actions client's `status(actionId)`;
6479
+ // `useAction().available` turns false when the server says a run would
6480
+ // not reach the script.
6481
+ version: "1.218.0",
6473
6482
  sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
6474
6483
  hooks: HOOKS,
6475
6484
  primitives: PRIMITIVES,