@fuzdev/fuz_app 0.46.0 → 0.48.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.
@@ -77,14 +77,68 @@ the system matures.
77
77
  `action_codegen.ts` provides gen helpers (used by consumer `*.gen.ts` files,
78
78
  not the runtime):
79
79
 
80
+ ### Primitives
81
+
80
82
  - `ImportBuilder` — tracks value / type / namespace imports; emits `import type` when every entry on a module is a type (tree-shaking). Namespace (`* as specs`) entries are emitted verbatim. Public surface: `add`, `add_type`, `add_many`, `add_types`, `build`, `preview`, `has_imports`, `import_count`, `clear`.
81
83
  - `get_executor_phases(spec, executor)` — phases a given executor (`'frontend' | 'backend'`) participates in for the spec. Deduplicates via `Set` (handles `initiator: 'both'` overlap).
82
- - `get_handler_return_type(spec, phase, imports, path_prefix)` — the TS type a phase handler must return; triggers the `ActionOutputs` import as a side effect.
83
- - `generate_phase_handlers(spec, executor, imports, {action_event_type?})` — emits the typed handler-map fragment for one action; consumers compose these into `ActionHandlers` types.
84
- - `generate_actions_api_method_signature(spec, {sync_returns_value?})` — single source of truth for the typed `ActionsApi` method shape. Threads `options?: RpcClientCallOptions` (`{signal?, transport_name?, queue?}`) onto every async method — `request_response`, `remote_notification`, and async `local_call` — and wraps the return in `Promise<Result<...>>`. Notifications were previously emitted as `=> void`, mismatching the runtime (`create_remote_notification_method` returns a Promise that resolves to `Result<{value: void}>`); regenerate consumer typed clients to pick up the corrected shape. Older inline templates using `get_innermost_type_name` directly drop the options arg — also regenerate those.
84
+ - `get_handler_return_type(spec, phase, imports, collections_path?)` — the TS type a phase handler must return; triggers the `ActionOutputs` import (sourced from `collections_path`, default `'./action_collections.js'`) as a side effect.
85
+ - `generate_phase_handlers(spec, executor, imports, {action_event_type?, collections_path?})` — emits the typed handler-map fragment for one action; consumers compose these into `ActionHandlers` types.
86
+ - `generate_actions_api_method_signature(spec, {sync_returns_value?})` — single source of truth for the typed `ActionsApi` method shape. Threads `options?: RpcClientCallOptions` (`{signal?, transport_name?, queue?}`) onto every async method — `request_response`, `remote_notification`, and async `local_call` — and wraps the return in `Promise<Result<...>>`. Notifications were previously emitted as `=> void`, mismatching the runtime (`create_remote_notification_method` returns a Promise that resolves to `Result<{value: void}>`); regenerate consumer typed clients to pick up the corrected shape.
85
87
  - `create_banner(origin_path)` — gen banner comment.
86
88
  - `to_action_spec_identifier(method)` / `to_action_spec_input_identifier` / `to_action_spec_output_identifier` — naming convention helpers (emit `foo_action_spec` / `foo_action_spec.input` / `foo_action_spec.output`).
87
- - `get_innermost_type(schema)` / `get_innermost_type_name(schema)` — unwrap Zod `ZodOptional` / `ZodNullable` / `ZodDefault` / transforms / pipes / prefaults to reach the base schema.
89
+ - `COMPOSABLE_ACTION_METHODS` (+ `ComposableActionMethod` type) readonly tuple `['heartbeat', 'cancel']`. Consumers spread when filtering backend `request_response` methods so dispatcher-owned composables don't leak into `BackendRequestResponseMethod` / handler maps.
90
+ - `is_composable_action_method(method)` — type predicate paired with `COMPOSABLE_ACTION_METHODS`; use this in `method_filter` callbacks instead of `COMPOSABLE_ACTION_METHODS.includes(s.method as never)`.
91
+ - `DEFAULT_COLLECTIONS_PATH = './action_collections.js'` — shared default for every helper that takes a `collections_path?`.
92
+ - `DEFAULT_SPECS_MODULE = './action_specs.js'` — shared default for helpers that emit `specs.{method}_action_spec` and need a `* as specs` namespace import.
93
+ - `DEFAULT_METATYPES_PATH = './action_metatypes.js'` — shared default for the sibling module carrying the generated `ActionMethod` enum.
94
+
95
+ ### High-level helpers (Step 1 of codegen orchestration upstream — 2026-04-26)
96
+
97
+ Each accepts `(specs, imports, options?)` and returns one block of declarations.
98
+ Composed by consumer `*.gen.ts` producers; outputs do not include the banner or
99
+ surrounding `imports.build()`.
100
+
101
+ **Composables are filtered by default.** Every spec-iterating helper accepts
102
+ `{include_composables?: boolean}` (default `false`) and drops `heartbeat` /
103
+ `cancel` from the emitted output. Composables ship from fuz_app and are
104
+ spread into each consumer's `actions` array at registration time — they
105
+ should not appear in consumer-owned typed surfaces (`ActionMethod`,
106
+ `ActionsApi`, `ActionInputs`, `FrontendActionHandlers`, etc.). Pass
107
+ `include_composables: true` only if a consumer genuinely owns composables
108
+ in their typed API.
109
+
110
+ **Consumer tiers and namespace handling.** Single-source consumers (zzz,
111
+ undying — every spec lives in one local `action_specs.ts`) drop straight
112
+ into the helpers and accept the default `* as specs from specs_module`
113
+ namespace import. Multi-source consumers (tx, visiones — which stitch
114
+ local specs together with `all_admin_action_specs` /
115
+ `all_permit_offer_action_specs` / `all_account_action_specs` /
116
+ `all_self_service_role_action_specs` from fuz_app) pass
117
+ `qualify_spec?: (spec) => string` to the three multi-source helpers
118
+ (`generate_action_specs_record`, `generate_action_inputs_outputs`,
119
+ `generate_backend_actions_api`). When `qualify_spec` is set, the helper
120
+ emits the callback's return value (e.g.
121
+ `admin_specs.account_list_action_spec`) and skips the default `* as specs`
122
+ import — the consumer manages its own multi-namespace imports. The helper
123
+ appends `.input` / `.output` to the qualified identifier in
124
+ `generate_action_inputs_outputs` automatically; the callback returns the
125
+ bare spec identifier.
126
+
127
+ Tier 1 (HTTP-only, e.g. tx/visiones) emits a smaller surface — typically just
128
+ `ActionMethod` + `ActionsApi` + `ActionInputs` / `ActionOutputs` interfaces —
129
+ and never calls `generate_typed_action_event_alias` or
130
+ `generate_frontend_action_handlers`. Tier 2 (`TypedActionEvent`-aware, e.g.
131
+ zzz) emits the full set including `ActionEventDatas`, `TypedActionEvent`, and
132
+ `FrontendActionHandlers`.
133
+
134
+ - `generate_action_method_enums(specs, imports, {emit?})` — up to six `z.enum` + `z.infer` pairs (`ActionMethod`, `RequestResponseActionMethod`, `RemoteNotificationActionMethod`, `LocalCallActionMethod`, `FrontendActionMethod`, `BackendActionMethod`). `emit: ReadonlySet<ActionMethodEnumKind>` restricts to a subset (Tier 1 HTTP-only consumers don't need all six). Skips kinds whose method list is empty (`z.enum([])` is invalid) and skips the `zod` import when no blocks are emitted. Adds `import {z} from 'zod'` only when at least one block is produced.
135
+ - `generate_typed_action_event_alias(imports, {collections_path?, metatypes_path?})` — fixed-shape `TypedActionEvent<TMethod, TPhase, TStep>` alias narrowing `ActionEvent.data` against `ActionEventDatas`. Adds the three fuz_app type imports + `ActionEventDatas` (from `collections_path`) + `ActionMethod` (from `metatypes_path`).
136
+ - `generate_action_specs_record(specs, imports, {specs_module?, qualify_spec?})` — `ActionSpecs` runtime const + interface + `action_specs: Array<ActionSpecUnion>` value. Adds `* as specs` from `specs_module` unless `qualify_spec` is set (then `specs_module` is ignored and the consumer owns namespace imports).
137
+ - `generate_action_inputs_outputs(specs, imports, {specs_module?, qualify_spec?})` — `ActionInputs` and `ActionOutputs` runtime consts + interfaces. Same `qualify_spec` semantics as `generate_action_specs_record`; the helper appends `.input` / `.output` to the qualified identifier.
138
+ - `generate_action_event_datas(specs, imports, {same_file?, collections_path?})` — `ActionEventDatas` interface; per-spec variant by kind (`ActionEventRequestResponseData` / `ActionEventRemoteNotificationData` / `ActionEventLocalCallData`). `same_file` (default `true`) is the file-layout switch: when `true`, assumes `ActionInputs` / `ActionOutputs` are in the same module and adds no import (the zzz pattern); when `false`, adds the type imports from `collections_path` (default `'./action_collections.js'`). `collections_path` alone is a no-op — the surprising omit-vs-default behavior of earlier versions has been replaced.
139
+ - `generate_actions_api(specs, imports, {method_filter?, collections_path?, sync_returns_value?})` — `ActionsApi` interface. Composables filtered by default; `method_filter: (spec) => boolean` runs after the composable filter for tx-style additional subsets.
140
+ - `generate_frontend_action_handlers(specs, imports, {collections_path?})` — `FrontendActionHandlers` interface (Tier 2 only — wraps `generate_phase_handlers` with `action_event_type: 'TypedActionEvent'`). Pair with `generate_typed_action_event_alias`.
141
+ - `generate_backend_actions_api(specs, imports, {specs_module?, collections_path?, qualify_spec?})` — `BackendActionsApi` interface AND `broadcast_action_specs: ReadonlyArray<ActionSpecUnion>` array. Filter: `kind === 'remote_notification' && initiator !== 'frontend'`. Adds `ActionInputs` (from `collections_path`) + `ActionSpecUnion`, plus `* as specs` from `specs_module` unless `qualify_spec` is set.
88
142
 
89
143
  ## HTTP bridge (`action_bridge.ts`)
90
144
 
@@ -1,4 +1,24 @@
1
1
  import type { ActionSpecUnion, ActionEventPhase } from './action_spec.js';
2
+ /**
3
+ * Method names of composable actions exported from fuz_app — `heartbeat` (auth-aware
4
+ * client liveness probe) and `cancel` (request-scoped abort signal). Consumers spread
5
+ * this list when filtering backend request_response methods so the dispatcher-owned
6
+ * composables don't show up in `BackendRequestResponseMethod` / handler maps.
7
+ */
8
+ export declare const COMPOSABLE_ACTION_METHODS: readonly ["heartbeat", "cancel"];
9
+ /** Methods that ship from fuz_app, kept out of consumer-owned method enums + handler maps. */
10
+ export type ComposableActionMethod = (typeof COMPOSABLE_ACTION_METHODS)[number];
11
+ /**
12
+ * Type predicate for filtering composable methods out of a typed `ActionsApi`
13
+ * `method_filter`. Avoids the `(... as never)` cast required to call
14
+ * `Array.prototype.includes` on the readonly tuple at narrow string types.
15
+ *
16
+ * @example
17
+ * generate_actions_api(specs, imports, {
18
+ * method_filter: (s) => !is_composable_action_method(s.method),
19
+ * });
20
+ */
21
+ export declare const is_composable_action_method: (method: string) => method is ComposableActionMethod;
2
22
  /**
3
23
  * Represents an import item with its kind (type, value, or namespace).
4
24
  */
@@ -79,20 +99,30 @@ export declare class ImportBuilder {
79
99
  * Determines which phases an executor can handle based on the action spec.
80
100
  */
81
101
  export declare const get_executor_phases: (spec: ActionSpecUnion, executor: "frontend" | "backend") => Array<ActionEventPhase>;
102
+ /** Default `collections_path` — every consumer's gen producers point at the sibling `action_collections.js`. */
103
+ export declare const DEFAULT_COLLECTIONS_PATH = "./action_collections.js";
104
+ /** Default `specs_module` — sibling `action_specs.js` namespace bundled by the consumer. */
105
+ export declare const DEFAULT_SPECS_MODULE = "./action_specs.js";
106
+ /** Default `metatypes_path` — sibling `action_metatypes.js` carrying the generated `ActionMethod`. */
107
+ export declare const DEFAULT_METATYPES_PATH = "./action_metatypes.js";
82
108
  /**
83
- * Gets the handler return type for a specific phase and spec.
84
- * Also adds necessary imports to the `ImportBuilder`.
109
+ * Gets the handler return type for a specific phase and spec. Adds an
110
+ * `ActionOutputs` import (from `collections_path`) when the phase carries an
111
+ * output (request_response `receive_request`, local_call `execute`).
85
112
  */
86
- export declare const get_handler_return_type: (spec: ActionSpecUnion, phase: ActionEventPhase, imports: ImportBuilder, path_prefix: string) => string;
113
+ export declare const get_handler_return_type: (spec: ActionSpecUnion, phase: ActionEventPhase, imports: ImportBuilder, collections_path?: string) => string;
87
114
  /**
88
115
  * Generates the phase handlers for an action spec using the unified ActionEvent type
89
116
  * with the new phase/step type parameters.
90
117
  *
91
118
  * @param options.action_event_type - custom type name to use instead of `ActionEvent`
92
119
  * (consumers can define a narrowed type that carries typed input/output via their codegen maps)
120
+ * @param options.collections_path - import path the side-effect `ActionOutputs` import
121
+ * resolves to. Defaults to `'./action_collections.js'`.
93
122
  */
94
123
  export declare const generate_phase_handlers: (spec: ActionSpecUnion, executor: "frontend" | "backend", imports: ImportBuilder, options?: {
95
124
  action_event_type?: string;
125
+ collections_path?: string;
96
126
  }) => string;
97
127
  /**
98
128
  * Creates a file banner comment.
@@ -132,5 +162,157 @@ export declare const to_action_spec_output_identifier: (method: string) => strin
132
162
  export declare const generate_actions_api_method_signature: (spec: ActionSpecUnion, options?: {
133
163
  sync_returns_value?: boolean;
134
164
  }) => string;
165
+ /** Discriminator for `generate_action_method_enums` — which method-set enums to emit. */
166
+ export type ActionMethodEnumKind = 'all' | 'request_response' | 'remote_notification' | 'local_call' | 'frontend' | 'backend';
167
+ /** Default emit set — every enum kind. */
168
+ export declare const ACTION_METHOD_ENUM_KINDS_ALL: ReadonlySet<ActionMethodEnumKind>;
169
+ /**
170
+ * Emit one or more `z.enum([...])` declarations for action method names —
171
+ * `ActionMethod`, `RequestResponseActionMethod`, `RemoteNotificationActionMethod`,
172
+ * `LocalCallActionMethod`, `FrontendActionMethod`, `BackendActionMethod`. Pairs
173
+ * each runtime const with a `z.infer` type alias under the same identifier.
174
+ *
175
+ * Composable methods (`heartbeat`, `cancel`) are filtered out by default —
176
+ * pass `include_composables: true` if a consumer genuinely wants them on
177
+ * their typed surface. Empty kinds are skipped so the helper never emits
178
+ * `z.enum([])` (zod runtime-throws on that).
179
+ *
180
+ * Adds `import {z} from 'zod';` to `imports` only when at least one block
181
+ * is emitted (idempotent).
182
+ *
183
+ * @param options.emit - subset of enums to emit; defaults to all six.
184
+ * @param options.include_composables - when true, retains `heartbeat` /
185
+ * `cancel` in the emitted enums. Default `false`.
186
+ */
187
+ export declare const generate_action_method_enums: (specs: ReadonlyArray<ActionSpecUnion>, imports: ImportBuilder, options?: {
188
+ emit?: ReadonlySet<ActionMethodEnumKind>;
189
+ include_composables?: boolean;
190
+ }) => string;
191
+ /**
192
+ * Emit the fixed-shape `TypedActionEvent` alias used by `FrontendActionHandlers`
193
+ * to narrow `ActionEvent.data` against the consumer's generated `ActionEventDatas`
194
+ * map. Registers the four fuz_app type imports it needs (`ActionEvent`,
195
+ * `ActionEventPhase`, `ActionEventStep`, `ActionEventDatas`) plus the
196
+ * `ActionMethod` type import — sourced from `collections_path` and
197
+ * `metatypes_path` respectively.
198
+ *
199
+ * Pair with `generate_action_method_enums` (emits `ActionMethod` into
200
+ * `metatypes_path`) and `generate_action_event_datas` (emits
201
+ * `ActionEventDatas` into `collections_path`).
202
+ */
203
+ export declare const generate_typed_action_event_alias: (imports: ImportBuilder, options?: {
204
+ collections_path?: string;
205
+ metatypes_path?: string;
206
+ }) => string;
207
+ /**
208
+ * Emit the `ActionSpecs` runtime const + interface + the `action_specs:
209
+ * Array<ActionSpecUnion>` value bundling every spec. Adds the `* as specs`
210
+ * namespace import + the `ActionSpecUnion` type import.
211
+ *
212
+ * @param options.qualify_spec - per-spec qualified identifier callback for
213
+ * multi-source consumers (e.g. ``(s) => `admin_specs.${s.method}_action_spec` ``).
214
+ * When set, the helper emits the callback's return value instead of
215
+ * ``specs.${method}_action_spec`` and skips the default `* as specs`
216
+ * import — the consumer manages its own namespace imports. `specs_module`
217
+ * is ignored when `qualify_spec` is set. Single-source consumers omit it.
218
+ */
219
+ export declare const generate_action_specs_record: (specs: ReadonlyArray<ActionSpecUnion>, imports: ImportBuilder, options?: {
220
+ specs_module?: string;
221
+ qualify_spec?: (spec: ActionSpecUnion) => string;
222
+ include_composables?: boolean;
223
+ }) => string;
224
+ /**
225
+ * Emit `ActionInputs` + `ActionOutputs` runtime consts and matching interfaces.
226
+ * The runtime consts reference `specs.{method}_action_spec.input` /
227
+ * `.output`; the interfaces use `z.infer`.
228
+ *
229
+ * Adds `import {z} from 'zod';` and the `* as specs` namespace import.
230
+ *
231
+ * @param options.qualify_spec - per-spec qualified identifier callback for
232
+ * multi-source consumers. The helper appends `.input` / `.output` to the
233
+ * callback's return value. When set, the helper skips the default
234
+ * `* as specs` import — the consumer manages its own namespace imports —
235
+ * and `specs_module` is ignored. Single-source consumers omit it.
236
+ */
237
+ export declare const generate_action_inputs_outputs: (specs: ReadonlyArray<ActionSpecUnion>, imports: ImportBuilder, options?: {
238
+ specs_module?: string;
239
+ qualify_spec?: (spec: ActionSpecUnion) => string;
240
+ include_composables?: boolean;
241
+ }) => string;
242
+ /**
243
+ * Emit the `ActionEventDatas` interface — one `ActionEvent*Data` variant per
244
+ * method, parameterized by the spec's kind:
245
+ * - `request_response` → `ActionEventRequestResponseData<method, input, output>`
246
+ * - `remote_notification` → `ActionEventRemoteNotificationData<method, input>`
247
+ * - `local_call` → `ActionEventLocalCallData<method, input, output>`
248
+ *
249
+ * Adds the per-kind data type imports (only the kinds that appear in `specs`).
250
+ *
251
+ * @param options.same_file - when `true` (default), assumes `ActionInputs` /
252
+ * `ActionOutputs` are in the same module as the emitted `ActionEventDatas`
253
+ * and adds no import (the zzz pattern, where `generate_action_inputs_outputs`
254
+ * and this helper feed the same `action_collections.ts` output). When
255
+ * `false`, adds `ActionInputs` / `ActionOutputs` type imports from
256
+ * `collections_path`.
257
+ * @param options.collections_path - import path used when `same_file: false`.
258
+ * Defaults to `'./action_collections.js'`. Ignored when `same_file: true`
259
+ * — `same_file` is the file-layout switch; `collections_path` is just the
260
+ * path the import resolves to.
261
+ */
262
+ export declare const generate_action_event_datas: (specs: ReadonlyArray<ActionSpecUnion>, imports: ImportBuilder, options?: {
263
+ same_file?: boolean;
264
+ collections_path?: string;
265
+ include_composables?: boolean;
266
+ }) => string;
267
+ /**
268
+ * Emit the `ActionsApi` interface — one method signature per spec via
269
+ * `generate_actions_api_method_signature`. Optionally filter the spec set
270
+ * (e.g. omit composable methods) via `method_filter`.
271
+ *
272
+ * Adds the `Result`, `JsonrpcErrorObject`, and `RpcClientCallOptions` type
273
+ * imports plus `ActionInputs` / `ActionOutputs` (sourced from `collections_path`).
274
+ */
275
+ export declare const generate_actions_api: (specs: ReadonlyArray<ActionSpecUnion>, imports: ImportBuilder, options?: {
276
+ method_filter?: (spec: ActionSpecUnion) => boolean;
277
+ collections_path?: string;
278
+ sync_returns_value?: boolean;
279
+ include_composables?: boolean;
280
+ }) => string;
281
+ /**
282
+ * Emit the `FrontendActionHandlers` interface — wraps `generate_phase_handlers`
283
+ * with the `TypedActionEvent` action-event type and standard 1-tab per-method
284
+ * indentation. Pairs with `generate_typed_action_event_alias` (emits the
285
+ * matching `TypedActionEvent` alias) — call both in the same gen producer.
286
+ */
287
+ export declare const generate_frontend_action_handlers: (specs: ReadonlyArray<ActionSpecUnion>, imports: ImportBuilder, options?: {
288
+ collections_path?: string;
289
+ include_composables?: boolean;
290
+ }) => string;
291
+ /**
292
+ * Emit BOTH the typed `BackendActionsApi` interface AND the
293
+ * `broadcast_action_specs` runtime array. The interface is shaped for
294
+ * `create_broadcast_api`: backend-initiated `remote_notification` methods,
295
+ * each `(input) => Promise<void>`. The array bundles the matching specs as a
296
+ * `ReadonlyArray<ActionSpecUnion>`.
297
+ *
298
+ * Filter: `kind === 'remote_notification' && initiator !== 'frontend'`.
299
+ *
300
+ * Adds the `* as specs` namespace import (from `specs_module`), the
301
+ * `ActionInputs` type import (from `collections_path`), and the
302
+ * `ActionSpecUnion` type import.
303
+ *
304
+ * @param options.qualify_spec - per-spec qualified identifier callback for
305
+ * multi-source consumers. When set, the helper emits the callback's return
306
+ * value instead of ``specs.${method}_action_spec`` in the broadcast array
307
+ * and skips the default `* as specs` import — the consumer manages its own
308
+ * namespace imports. `specs_module` is ignored when `qualify_spec` is set.
309
+ * Single-source consumers omit it.
310
+ */
311
+ export declare const generate_backend_actions_api: (specs: ReadonlyArray<ActionSpecUnion>, imports: ImportBuilder, options?: {
312
+ specs_module?: string;
313
+ collections_path?: string;
314
+ qualify_spec?: (spec: ActionSpecUnion) => string;
315
+ include_composables?: boolean;
316
+ }) => string;
135
317
  export {};
136
318
  //# sourceMappingURL=action_codegen.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"action_codegen.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/action_codegen.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,eAAe,EAAE,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAOxE;;GAEG;AACH,UAAU,UAAU;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,WAAW,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,aAAa;;IACzB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAa;IAE1D;;;;OAIG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAQrC;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAI1C;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,IAAI;IAOrD;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,IAAI;IAgCtD;;;OAGG;IACH,KAAK,IAAI,MAAM;IAIf;;OAEG;IACH,WAAW,IAAI,OAAO;IAItB;;OAEG;IACH,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED;;;OAGG;IACH,OAAO,IAAI,KAAK,CAAC,MAAM,CAAC;IAIxB;;OAEG;IACH,KAAK,IAAI,IAAI;CAqDb;AAED;;GAEG;AACH,eAAO,MAAM,mBAAmB,GAC/B,MAAM,eAAe,EACrB,UAAU,UAAU,GAAG,SAAS,KAC9B,KAAK,CAAC,gBAAgB,CA4DxB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,uBAAuB,GACnC,MAAM,eAAe,EACrB,OAAO,gBAAgB,EACvB,SAAS,aAAa,EACtB,aAAa,MAAM,KACjB,MAkBF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,GACnC,MAAM,eAAe,EACrB,UAAU,UAAU,GAAG,SAAS,EAChC,SAAS,aAAa,EACtB,UAAU;IAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAAC,KACpC,MA4BF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,GAAI,aAAa,MAAM,KAAG,MACU,CAAC;AAG/D,eAAO,MAAM,yBAAyB,GAAI,QAAQ,MAAM,KAAG,MAAiC,CAAC;AAC7F,eAAO,MAAM,+BAA+B,GAAI,QAAQ,MAAM,KAAG,MACpB,CAAC;AAC9C,eAAO,MAAM,gCAAgC,GAAI,QAAQ,MAAM,KAAG,MACpB,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,qCAAqC,GACjD,MAAM,eAAe,EACrB,UAAU;IAAC,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAAC,KACtC,MAoBF,CAAC"}
1
+ {"version":3,"file":"action_codegen.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/action_codegen.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,25 @@
1
1
  import { UnreachableError } from '@fuzdev/fuz_util/error.js';
2
2
  import { zod_get_base_type } from '@fuzdev/fuz_util/zod.js';
3
+ import { ActionRegistry } from './action_registry.js';
4
+ /**
5
+ * Method names of composable actions exported from fuz_app — `heartbeat` (auth-aware
6
+ * client liveness probe) and `cancel` (request-scoped abort signal). Consumers spread
7
+ * this list when filtering backend request_response methods so the dispatcher-owned
8
+ * composables don't show up in `BackendRequestResponseMethod` / handler maps.
9
+ */
10
+ export const COMPOSABLE_ACTION_METHODS = ['heartbeat', 'cancel'];
11
+ const COMPOSABLE_METHOD_SET = new Set(COMPOSABLE_ACTION_METHODS);
12
+ /**
13
+ * Type predicate for filtering composable methods out of a typed `ActionsApi`
14
+ * `method_filter`. Avoids the `(... as never)` cast required to call
15
+ * `Array.prototype.includes` on the readonly tuple at narrow string types.
16
+ *
17
+ * @example
18
+ * generate_actions_api(specs, imports, {
19
+ * method_filter: (s) => !is_composable_action_method(s.method),
20
+ * });
21
+ */
22
+ export const is_composable_action_method = (method) => COMPOSABLE_METHOD_SET.has(method);
3
23
  /**
4
24
  * Manages imports for generated code, building them on demand.
5
25
  * Automatically optimizes type-only imports to use `import type` syntax.
@@ -223,21 +243,28 @@ export const get_executor_phases = (spec, executor) => {
223
243
  // Deduplicate phases (e.g., send_error added twice for initiator:'both' backend actions)
224
244
  return Array.from(new Set(phases));
225
245
  };
246
+ /** Default `collections_path` — every consumer's gen producers point at the sibling `action_collections.js`. */
247
+ export const DEFAULT_COLLECTIONS_PATH = './action_collections.js';
248
+ /** Default `specs_module` — sibling `action_specs.js` namespace bundled by the consumer. */
249
+ export const DEFAULT_SPECS_MODULE = './action_specs.js';
250
+ /** Default `metatypes_path` — sibling `action_metatypes.js` carrying the generated `ActionMethod`. */
251
+ export const DEFAULT_METATYPES_PATH = './action_metatypes.js';
226
252
  /**
227
- * Gets the handler return type for a specific phase and spec.
228
- * Also adds necessary imports to the `ImportBuilder`.
253
+ * Gets the handler return type for a specific phase and spec. Adds an
254
+ * `ActionOutputs` import (from `collections_path`) when the phase carries an
255
+ * output (request_response `receive_request`, local_call `execute`).
229
256
  */
230
- export const get_handler_return_type = (spec, phase, imports, path_prefix) => {
257
+ export const get_handler_return_type = (spec, phase, imports, collections_path = DEFAULT_COLLECTIONS_PATH) => {
231
258
  // For request_response receive_request, handler returns the output
232
259
  if (spec.kind === 'request_response' && phase === 'receive_request') {
233
- imports.add_type(`${path_prefix}action_collections.js`, 'ActionOutputs');
260
+ imports.add_type(collections_path, 'ActionOutputs');
234
261
  const base_type = `ActionOutputs['${spec.method}']`;
235
262
  // Request/response actions are always async
236
263
  return `${base_type} | Promise<${base_type}>`;
237
264
  }
238
265
  // For local_call execute, handler returns the output
239
266
  if (spec.kind === 'local_call' && phase === 'execute') {
240
- imports.add_type(`${path_prefix}action_collections.js`, 'ActionOutputs');
267
+ imports.add_type(collections_path, 'ActionOutputs');
241
268
  const base_type = `ActionOutputs['${spec.method}']`;
242
269
  return spec.async ? `${base_type} | Promise<${base_type}>` : base_type;
243
270
  }
@@ -250,6 +277,8 @@ export const get_handler_return_type = (spec, phase, imports, path_prefix) => {
250
277
  *
251
278
  * @param options.action_event_type - custom type name to use instead of `ActionEvent`
252
279
  * (consumers can define a narrowed type that carries typed input/output via their codegen maps)
280
+ * @param options.collections_path - import path the side-effect `ActionOutputs` import
281
+ * resolves to. Defaults to `'./action_collections.js'`.
253
282
  */
254
283
  export const generate_phase_handlers = (spec, executor, imports, options) => {
255
284
  const { method } = spec;
@@ -258,16 +287,15 @@ export const generate_phase_handlers = (spec, executor, imports, options) => {
258
287
  return `${method}?: never`;
259
288
  }
260
289
  const action_event_type = options?.action_event_type ?? 'ActionEvent';
290
+ const collections_path = options?.collections_path ?? DEFAULT_COLLECTIONS_PATH;
261
291
  // Only add the default ActionEvent import if using the default type name
262
292
  if (action_event_type === 'ActionEvent') {
263
293
  imports.add_type('@fuzdev/fuz_app/actions/action_event.js', 'ActionEvent');
264
294
  }
265
- // Generate handler definitions for each phase
266
- const path_prefix = executor === 'frontend' ? './' : '../';
267
295
  const phase_handlers = phases
268
296
  .map((phase) => {
269
297
  // Pass imports to get_handler_return_type so it can add necessary imports
270
- const return_type = get_handler_return_type(spec, phase, imports, path_prefix);
298
+ const return_type = get_handler_return_type(spec, phase, imports, collections_path);
271
299
  return `${phase}?: (
272
300
  action_event: ${action_event_type}<'${method}', '${phase}', 'handling'>
273
301
  ) => ${return_type}`;
@@ -328,3 +356,403 @@ export const generate_actions_api_method_signature = (spec, options) => {
328
356
  : result_return;
329
357
  return `${spec.method}: (${input_param}${options_param}) => ${return_type};`;
330
358
  };
359
+ /** Default emit set — every enum kind. */
360
+ export const ACTION_METHOD_ENUM_KINDS_ALL = new Set([
361
+ 'all',
362
+ 'request_response',
363
+ 'remote_notification',
364
+ 'local_call',
365
+ 'frontend',
366
+ 'backend',
367
+ ]);
368
+ /**
369
+ * Filter `heartbeat` / `cancel` out of `specs` unless the consumer opts back in.
370
+ * Composables ship from fuz_app and are spread into every consumer's `actions`
371
+ * array at registration time — they should not appear in consumer-owned typed
372
+ * surfaces (`ActionMethod`, `ActionsApi`, `ActionInputs`, etc.) by default.
373
+ */
374
+ const filter_composables = (specs, include_composables) => include_composables ? specs : specs.filter((s) => !is_composable_action_method(s.method));
375
+ /**
376
+ * Resolve the per-spec identifier qualifier used by the multi-source helpers
377
+ * (`generate_action_specs_record`, `generate_action_inputs_outputs`,
378
+ * `generate_backend_actions_api`). When `qualify_spec` is set, returns the
379
+ * caller's callback verbatim — the consumer is managing its own namespace
380
+ * imports. Otherwise, registers the default `* as specs from specs_module`
381
+ * import (defaulting to `'./action_specs.js'`) and returns the matching
382
+ * `specs.${method}_action_spec` qualifier.
383
+ */
384
+ const resolve_spec_qualifier = (imports, options) => {
385
+ if (options?.qualify_spec)
386
+ return options.qualify_spec;
387
+ const specs_module = options?.specs_module ?? DEFAULT_SPECS_MODULE;
388
+ imports.add(specs_module, '* as specs');
389
+ return (s) => `specs.${s.method}_action_spec`;
390
+ };
391
+ /**
392
+ * Emit one or more `z.enum([...])` declarations for action method names —
393
+ * `ActionMethod`, `RequestResponseActionMethod`, `RemoteNotificationActionMethod`,
394
+ * `LocalCallActionMethod`, `FrontendActionMethod`, `BackendActionMethod`. Pairs
395
+ * each runtime const with a `z.infer` type alias under the same identifier.
396
+ *
397
+ * Composable methods (`heartbeat`, `cancel`) are filtered out by default —
398
+ * pass `include_composables: true` if a consumer genuinely wants them on
399
+ * their typed surface. Empty kinds are skipped so the helper never emits
400
+ * `z.enum([])` (zod runtime-throws on that).
401
+ *
402
+ * Adds `import {z} from 'zod';` to `imports` only when at least one block
403
+ * is emitted (idempotent).
404
+ *
405
+ * @param options.emit - subset of enums to emit; defaults to all six.
406
+ * @param options.include_composables - when true, retains `heartbeat` /
407
+ * `cancel` in the emitted enums. Default `false`.
408
+ */
409
+ export const generate_action_method_enums = (specs, imports, options) => {
410
+ const emit = options?.emit ?? ACTION_METHOD_ENUM_KINDS_ALL;
411
+ const filtered = filter_composables(specs, options?.include_composables);
412
+ const registry = new ActionRegistry([...filtered]);
413
+ const blocks = [];
414
+ const emit_block = (kind, name, methods, jsdoc) => {
415
+ if (!emit.has(kind))
416
+ return;
417
+ // `z.enum([])` is invalid — skip empty kinds rather than emit broken code.
418
+ // Consumers that need a kind to exist should check their spec set, not the helper.
419
+ if (methods.length === 0)
420
+ return;
421
+ const lines = methods.map((m) => `\t'${m}',`).join('\n');
422
+ blocks.push(`/**\n * ${jsdoc}\n */\nexport const ${name} = z.enum([\n${lines}\n]);
423
+ export type ${name} = z.infer<typeof ${name}>;`);
424
+ };
425
+ emit_block('all', 'ActionMethod', registry.methods, 'All action method names. Request/response actions have two types per method.');
426
+ emit_block('request_response', 'RequestResponseActionMethod', registry.request_response_methods, 'Names of all request_response actions.');
427
+ emit_block('remote_notification', 'RemoteNotificationActionMethod', registry.remote_notification_methods, 'Names of all remote_notification actions.');
428
+ emit_block('local_call', 'LocalCallActionMethod', registry.local_call_methods, 'Names of all local_call actions.');
429
+ emit_block('frontend', 'FrontendActionMethod', registry.frontend_methods, 'Names of all actions that may be handled on the client.');
430
+ emit_block('backend', 'BackendActionMethod', registry.backend_methods, 'Names of all actions that may be handled on the server.');
431
+ if (blocks.length === 0)
432
+ return '';
433
+ imports.add('zod', 'z');
434
+ return blocks.join('\n\n');
435
+ };
436
+ /**
437
+ * Emit the fixed-shape `TypedActionEvent` alias used by `FrontendActionHandlers`
438
+ * to narrow `ActionEvent.data` against the consumer's generated `ActionEventDatas`
439
+ * map. Registers the four fuz_app type imports it needs (`ActionEvent`,
440
+ * `ActionEventPhase`, `ActionEventStep`, `ActionEventDatas`) plus the
441
+ * `ActionMethod` type import — sourced from `collections_path` and
442
+ * `metatypes_path` respectively.
443
+ *
444
+ * Pair with `generate_action_method_enums` (emits `ActionMethod` into
445
+ * `metatypes_path`) and `generate_action_event_datas` (emits
446
+ * `ActionEventDatas` into `collections_path`).
447
+ */
448
+ export const generate_typed_action_event_alias = (imports, options) => {
449
+ const collections_path = options?.collections_path ?? DEFAULT_COLLECTIONS_PATH;
450
+ const metatypes_path = options?.metatypes_path ?? DEFAULT_METATYPES_PATH;
451
+ imports.add_type('@fuzdev/fuz_app/actions/action_event.js', 'ActionEvent');
452
+ imports.add_type('@fuzdev/fuz_app/actions/action_spec.js', 'ActionEventPhase');
453
+ imports.add_type('@fuzdev/fuz_app/actions/action_event_types.js', 'ActionEventStep');
454
+ imports.add_type(collections_path, 'ActionEventDatas');
455
+ imports.add_type(metatypes_path, 'ActionMethod');
456
+ return `/** ActionEvent narrowed with the generated ActionEventDatas for typed input/output. */
457
+ type TypedActionEvent<
458
+ TMethod extends ActionMethod,
459
+ TPhase extends ActionEventPhase,
460
+ TStep extends ActionEventStep,
461
+ > = ActionEvent<TMethod, TPhase, TStep> & {readonly data: ActionEventDatas[TMethod]};`;
462
+ };
463
+ /**
464
+ * Emit the `ActionSpecs` runtime const + interface + the `action_specs:
465
+ * Array<ActionSpecUnion>` value bundling every spec. Adds the `* as specs`
466
+ * namespace import + the `ActionSpecUnion` type import.
467
+ *
468
+ * @param options.qualify_spec - per-spec qualified identifier callback for
469
+ * multi-source consumers (e.g. ``(s) => `admin_specs.${s.method}_action_spec` ``).
470
+ * When set, the helper emits the callback's return value instead of
471
+ * ``specs.${method}_action_spec`` and skips the default `* as specs`
472
+ * import — the consumer manages its own namespace imports. `specs_module`
473
+ * is ignored when `qualify_spec` is set. Single-source consumers omit it.
474
+ */
475
+ export const generate_action_specs_record = (specs, imports, options) => {
476
+ const filtered = filter_composables(specs, options?.include_composables);
477
+ imports.add_type('@fuzdev/fuz_app/actions/action_spec.js', 'ActionSpecUnion');
478
+ if (filtered.length === 0) {
479
+ // Empty spec list — emit minimal valid output and skip the `* as specs`
480
+ // import that would have nothing to reference.
481
+ return `/**
482
+ * Action specifications indexed by method name.
483
+ * These represent the complete action spec definitions.
484
+ */
485
+ export const ActionSpecs = {} as const;
486
+ export interface ActionSpecs {}
487
+
488
+ export const action_specs: Array<ActionSpecUnion> = Object.values(ActionSpecs);`;
489
+ }
490
+ const qualify = resolve_spec_qualifier(imports, options);
491
+ const value_lines = filtered.map((s) => `\t${s.method}: ${qualify(s)},`).join('\n');
492
+ const type_lines = filtered.map((s) => `\t${s.method}: typeof ${qualify(s)};`).join('\n');
493
+ return `/**
494
+ * Action specifications indexed by method name.
495
+ * These represent the complete action spec definitions.
496
+ */
497
+ export const ActionSpecs = {
498
+ ${value_lines}
499
+ } as const;
500
+ export interface ActionSpecs {
501
+ ${type_lines}
502
+ }
503
+
504
+ export const action_specs: Array<ActionSpecUnion> = Object.values(ActionSpecs);`;
505
+ };
506
+ /**
507
+ * Emit `ActionInputs` + `ActionOutputs` runtime consts and matching interfaces.
508
+ * The runtime consts reference `specs.{method}_action_spec.input` /
509
+ * `.output`; the interfaces use `z.infer`.
510
+ *
511
+ * Adds `import {z} from 'zod';` and the `* as specs` namespace import.
512
+ *
513
+ * @param options.qualify_spec - per-spec qualified identifier callback for
514
+ * multi-source consumers. The helper appends `.input` / `.output` to the
515
+ * callback's return value. When set, the helper skips the default
516
+ * `* as specs` import — the consumer manages its own namespace imports —
517
+ * and `specs_module` is ignored. Single-source consumers omit it.
518
+ */
519
+ export const generate_action_inputs_outputs = (specs, imports, options) => {
520
+ const filtered = filter_composables(specs, options?.include_composables);
521
+ if (filtered.length === 0) {
522
+ // Empty spec list — emit minimal valid output and skip the `zod` /
523
+ // `* as specs` imports that would have nothing to reference.
524
+ return `/**
525
+ * Action parameter schemas indexed by method name.
526
+ * These represent the input data for each action,
527
+ * e.g. JSON-RPC request/notification params and local call arguments.
528
+ */
529
+ export const ActionInputs = {} as const;
530
+ export interface ActionInputs {}
531
+
532
+ /**
533
+ * Action result schemas indexed by method name.
534
+ * These represent the output data for each action,
535
+ * e.g. JSON-RPC response results and local call return values.
536
+ */
537
+ export const ActionOutputs = {} as const;
538
+ export interface ActionOutputs {}`;
539
+ }
540
+ imports.add('zod', 'z');
541
+ const qualify = resolve_spec_qualifier(imports, options);
542
+ const inputs_value = filtered.map((s) => `\t${s.method}: ${qualify(s)}.input,`).join('\n');
543
+ const inputs_type = filtered
544
+ .map((s) => `\t${s.method}: z.infer<typeof ${qualify(s)}.input>;`)
545
+ .join('\n');
546
+ const outputs_value = filtered.map((s) => `\t${s.method}: ${qualify(s)}.output,`).join('\n');
547
+ const outputs_type = filtered
548
+ .map((s) => `\t${s.method}: z.infer<typeof ${qualify(s)}.output>;`)
549
+ .join('\n');
550
+ return `/**
551
+ * Action parameter schemas indexed by method name.
552
+ * These represent the input data for each action,
553
+ * e.g. JSON-RPC request/notification params and local call arguments.
554
+ */
555
+ export const ActionInputs = {
556
+ ${inputs_value}
557
+ } as const;
558
+ export interface ActionInputs {
559
+ ${inputs_type}
560
+ }
561
+
562
+ /**
563
+ * Action result schemas indexed by method name.
564
+ * These represent the output data for each action,
565
+ * e.g. JSON-RPC response results and local call return values.
566
+ */
567
+ export const ActionOutputs = {
568
+ ${outputs_value}
569
+ } as const;
570
+ export interface ActionOutputs {
571
+ ${outputs_type}
572
+ }`;
573
+ };
574
+ /**
575
+ * Emit the `ActionEventDatas` interface — one `ActionEvent*Data` variant per
576
+ * method, parameterized by the spec's kind:
577
+ * - `request_response` → `ActionEventRequestResponseData<method, input, output>`
578
+ * - `remote_notification` → `ActionEventRemoteNotificationData<method, input>`
579
+ * - `local_call` → `ActionEventLocalCallData<method, input, output>`
580
+ *
581
+ * Adds the per-kind data type imports (only the kinds that appear in `specs`).
582
+ *
583
+ * @param options.same_file - when `true` (default), assumes `ActionInputs` /
584
+ * `ActionOutputs` are in the same module as the emitted `ActionEventDatas`
585
+ * and adds no import (the zzz pattern, where `generate_action_inputs_outputs`
586
+ * and this helper feed the same `action_collections.ts` output). When
587
+ * `false`, adds `ActionInputs` / `ActionOutputs` type imports from
588
+ * `collections_path`.
589
+ * @param options.collections_path - import path used when `same_file: false`.
590
+ * Defaults to `'./action_collections.js'`. Ignored when `same_file: true`
591
+ * — `same_file` is the file-layout switch; `collections_path` is just the
592
+ * path the import resolves to.
593
+ */
594
+ export const generate_action_event_datas = (specs, imports, options) => {
595
+ const filtered = filter_composables(specs, options?.include_composables);
596
+ if (filtered.length === 0) {
597
+ // Empty spec list — emit `interface ActionEventDatas {}` and skip
598
+ // the optional collections-path import that would be unused.
599
+ return `/**
600
+ * Action event data types indexed by method name.
601
+ * These represent the full discriminated union of all possible states
602
+ * for each action's event data, properly typed with inputs and outputs.
603
+ */
604
+ export interface ActionEventDatas {}`;
605
+ }
606
+ const same_file = options?.same_file ?? true;
607
+ if (!same_file) {
608
+ const collections_path = options?.collections_path ?? DEFAULT_COLLECTIONS_PATH;
609
+ imports.add_types(collections_path, 'ActionInputs', 'ActionOutputs');
610
+ }
611
+ const lines = filtered.map((spec) => {
612
+ const data_type = spec.kind === 'request_response'
613
+ ? 'ActionEventRequestResponseData'
614
+ : spec.kind === 'remote_notification'
615
+ ? 'ActionEventRemoteNotificationData'
616
+ : 'ActionEventLocalCallData';
617
+ imports.add_type('@fuzdev/fuz_app/actions/action_event_data.js', data_type);
618
+ const type_args = spec.kind === 'remote_notification'
619
+ ? `<'${spec.method}', ActionInputs['${spec.method}']>`
620
+ : `<'${spec.method}', ActionInputs['${spec.method}'], ActionOutputs['${spec.method}']>`;
621
+ return `\t${spec.method}: ${data_type}${type_args};`;
622
+ });
623
+ return `/**
624
+ * Action event data types indexed by method name.
625
+ * These represent the full discriminated union of all possible states
626
+ * for each action's event data, properly typed with inputs and outputs.
627
+ */
628
+ export interface ActionEventDatas {
629
+ ${lines.join('\n')}
630
+ }`;
631
+ };
632
+ /**
633
+ * Emit the `ActionsApi` interface — one method signature per spec via
634
+ * `generate_actions_api_method_signature`. Optionally filter the spec set
635
+ * (e.g. omit composable methods) via `method_filter`.
636
+ *
637
+ * Adds the `Result`, `JsonrpcErrorObject`, and `RpcClientCallOptions` type
638
+ * imports plus `ActionInputs` / `ActionOutputs` (sourced from `collections_path`).
639
+ */
640
+ export const generate_actions_api = (specs, imports, options) => {
641
+ const composable_filtered = filter_composables(specs, options?.include_composables);
642
+ const filter = options?.method_filter;
643
+ const filtered = filter ? composable_filtered.filter((s) => filter(s)) : composable_filtered;
644
+ const interface_doc = `/**
645
+ * Interface for action dispatch functions.
646
+ * Async methods (request_response, remote_notification, async local_call)
647
+ * return \`Promise<Result<...>>\` and accept an optional \`RpcClientCallOptions\`
648
+ * second arg that threads \`signal\`, \`transport_name\`, and \`queue\` through to
649
+ * the peer. Sync local_call methods return values directly.
650
+ */`;
651
+ if (filtered.length === 0) {
652
+ // Empty spec list — emit `ActionsApi {}` and skip every import. None
653
+ // of the symbols would be referenced by the empty body.
654
+ return `${interface_doc}
655
+ export interface ActionsApi {}`;
656
+ }
657
+ const collections_path = options?.collections_path ?? DEFAULT_COLLECTIONS_PATH;
658
+ imports.add_type('@fuzdev/fuz_util/result.js', 'Result');
659
+ imports.add_type('@fuzdev/fuz_app/http/jsonrpc.js', 'JsonrpcErrorObject');
660
+ imports.add_type('@fuzdev/fuz_app/actions/rpc_client.js', 'RpcClientCallOptions');
661
+ imports.add_types(collections_path, 'ActionInputs', 'ActionOutputs');
662
+ const lines = filtered
663
+ .map((spec) => generate_actions_api_method_signature(spec, {
664
+ sync_returns_value: options?.sync_returns_value,
665
+ }))
666
+ .map((line) => `\t${line}`)
667
+ .join('\n');
668
+ return `${interface_doc}
669
+ export interface ActionsApi {
670
+ ${lines}
671
+ }`;
672
+ };
673
+ /**
674
+ * Emit the `FrontendActionHandlers` interface — wraps `generate_phase_handlers`
675
+ * with the `TypedActionEvent` action-event type and standard 1-tab per-method
676
+ * indentation. Pairs with `generate_typed_action_event_alias` (emits the
677
+ * matching `TypedActionEvent` alias) — call both in the same gen producer.
678
+ */
679
+ export const generate_frontend_action_handlers = (specs, imports, options) => {
680
+ const filtered = filter_composables(specs, options?.include_composables);
681
+ const interface_doc = `/**
682
+ * Frontend action handlers organized by method and phase.
683
+ * Generated using spec.initiator to determine valid phases:
684
+ * - initiator: 'frontend' → send/execute phases
685
+ * - initiator: 'backend' → receive phases
686
+ * - initiator: 'both' → all valid phases
687
+ */`;
688
+ if (filtered.length === 0) {
689
+ // Empty spec list — emit `FrontendActionHandlers {}` and skip the
690
+ // dangling `;` that the body template would otherwise produce.
691
+ return `${interface_doc}
692
+ export interface FrontendActionHandlers {}`;
693
+ }
694
+ const handler_options = {
695
+ action_event_type: 'TypedActionEvent',
696
+ collections_path: options?.collections_path,
697
+ };
698
+ const lines = filtered
699
+ .map((spec) => generate_phase_handlers(spec, 'frontend', imports, handler_options))
700
+ .filter(Boolean)
701
+ .map((block) => `\t${block}`)
702
+ .join(';\n');
703
+ return `${interface_doc}
704
+ export interface FrontendActionHandlers {
705
+ ${lines};
706
+ }`;
707
+ };
708
+ /**
709
+ * Emit BOTH the typed `BackendActionsApi` interface AND the
710
+ * `broadcast_action_specs` runtime array. The interface is shaped for
711
+ * `create_broadcast_api`: backend-initiated `remote_notification` methods,
712
+ * each `(input) => Promise<void>`. The array bundles the matching specs as a
713
+ * `ReadonlyArray<ActionSpecUnion>`.
714
+ *
715
+ * Filter: `kind === 'remote_notification' && initiator !== 'frontend'`.
716
+ *
717
+ * Adds the `* as specs` namespace import (from `specs_module`), the
718
+ * `ActionInputs` type import (from `collections_path`), and the
719
+ * `ActionSpecUnion` type import.
720
+ *
721
+ * @param options.qualify_spec - per-spec qualified identifier callback for
722
+ * multi-source consumers. When set, the helper emits the callback's return
723
+ * value instead of ``specs.${method}_action_spec`` in the broadcast array
724
+ * and skips the default `* as specs` import — the consumer manages its own
725
+ * namespace imports. `specs_module` is ignored when `qualify_spec` is set.
726
+ * Single-source consumers omit it.
727
+ */
728
+ export const generate_backend_actions_api = (specs, imports, options) => {
729
+ const composable_filtered = filter_composables(specs, options?.include_composables);
730
+ const broadcast = composable_filtered.filter((s) => s.kind === 'remote_notification' && s.initiator !== 'frontend');
731
+ imports.add_type('@fuzdev/fuz_app/actions/action_spec.js', 'ActionSpecUnion');
732
+ const interface_doc = `/**
733
+ * Broadcast-style notifications from the backend to all connected clients.
734
+ * Request-scoped streaming goes through \`ctx.notify\` instead — it's
735
+ * socket-scoped, not a broadcast.
736
+ */`;
737
+ if (broadcast.length === 0) {
738
+ // No backend-initiated remote_notifications — skip `* as specs` and
739
+ // `ActionInputs` imports that would have nothing to reference.
740
+ return `${interface_doc}
741
+ export interface BackendActionsApi {}
742
+
743
+ export const broadcast_action_specs: ReadonlyArray<ActionSpecUnion> = [];`;
744
+ }
745
+ const collections_path = options?.collections_path ?? DEFAULT_COLLECTIONS_PATH;
746
+ imports.add_type(collections_path, 'ActionInputs');
747
+ const qualify = resolve_spec_qualifier(imports, options);
748
+ const interface_body = '\n' +
749
+ broadcast
750
+ .map((s) => `\t${s.method}: (input: ActionInputs['${s.method}']) => Promise<void>;`)
751
+ .join('\n') +
752
+ '\n';
753
+ const array_body = '\n' + broadcast.map((s) => `\t${qualify(s)},`).join('\n') + '\n';
754
+ return `${interface_doc}
755
+ export interface BackendActionsApi {${interface_body}}
756
+
757
+ export const broadcast_action_specs: ReadonlyArray<ActionSpecUnion> = [${array_body}];`;
758
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"action_event_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/action_event_helpers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"action_event_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/action_event_helpers.ts"],"names":[],"mappings":"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"}
@@ -29,14 +29,12 @@ export const is_send_request_with_parsed_input = (data) => is_send_request(data)
29
29
  export const is_notification_send_with_parsed_input = (data) => is_notification_send(data) && (data.step === 'parsed' || data.step === 'handling');
30
30
  // Validation helpers
31
31
  export const validate_step_transition = (from, to) => {
32
- const valid_transitions = ACTION_EVENT_STEP_TRANSITIONS[from];
33
- if (!valid_transitions.includes(to)) {
32
+ if (!ACTION_EVENT_STEP_TRANSITIONS[from].includes(to)) {
34
33
  throw new Error(`Invalid step transition from '${from}' to '${to}'`);
35
34
  }
36
35
  };
37
36
  export const validate_phase_for_kind = (kind, phase) => {
38
- const valid_phases = ACTION_EVENT_PHASE_BY_KIND[kind];
39
- if (!valid_phases.includes(phase)) {
37
+ if (!ACTION_EVENT_PHASE_BY_KIND[kind].includes(phase)) {
40
38
  throw new Error(`Invalid phase '${phase}' for ${kind} action`);
41
39
  }
42
40
  };
@@ -1 +1 @@
1
- {"version":3,"file":"action_event_types.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/action_event_types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AACtB,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAC,gBAAgB,EAAE,UAAU,EAAE,eAAe,EAAC,MAAM,kBAAkB,CAAC;AAEpF,eAAO,MAAM,cAAc;;;EAAkC,CAAC;AAC9D,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAE5D,eAAO,MAAM,eAAe;;;;;;EAAiE,CAAC;AAC9F,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D,eAAO,MAAM,6BAA6B,EAMrC,MAAM,CAAC,eAAe,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC,CAAC;AAE7D,eAAO,MAAM,0BAA0B,EAWlC,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,gBAAgB,CAAC,CAAC,CAAC;AAEzD,eAAO,MAAM,8BAA8B,EAUtC,MAAM,CAAC,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC,CAAC;AAEvD,MAAM,WAAW,sBAAsB;IACtC,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,qBAAqB,EAAE,CACtB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,gBAAgB,KACnB,CAAC,CAAC,KAAK,EAAE,GAAG,KAAK,GAAG,CAAC,GAAG,SAAS,CAAC;IACvC,kBAAkB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,eAAe,GAAG,SAAS,CAAC;IACpE,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B"}
1
+ {"version":3,"file":"action_event_types.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/action_event_types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AACtB,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAC,gBAAgB,EAAE,UAAU,EAAE,eAAe,EAAC,MAAM,kBAAkB,CAAC;AAEpF,eAAO,MAAM,cAAc;;;EAAkC,CAAC;AAC9D,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAE5D,eAAO,MAAM,eAAe;;;;;;EAAiE,CAAC;AAC9F,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAS9D,eAAO,MAAM,6BAA6B,EAAE,MAAM,CACjD,eAAe,EACf,aAAa,CAAC,eAAe,CAAC,CAO9B,CAAC;AAEF,eAAO,MAAM,0BAA0B,EAAE,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,gBAAgB,CAAC,CAW1F,CAAC;AAEF,eAAO,MAAM,8BAA8B,EAAE,MAAM,CAAC,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAU5F,CAAC;AAEF,MAAM,WAAW,sBAAsB;IACtC,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,qBAAqB,EAAE,CACtB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,gBAAgB,KACnB,CAAC,CAAC,KAAK,EAAE,GAAG,KAAK,GAAG,CAAC,GAAG,SAAS,CAAC;IACvC,kBAAkB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,eAAe,GAAG,SAAS,CAAC;IACpE,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B"}
@@ -6,6 +6,12 @@
6
6
  import { z } from 'zod';
7
7
  export const ActionExecutor = z.enum(['frontend', 'backend']);
8
8
  export const ActionEventStep = z.enum(['initial', 'parsed', 'handling', 'handled', 'failed']);
9
+ // The constants below use `Record<K, V> = {...}` rather than
10
+ // `as const satisfies Record<K, V>`. The typed annotation gives full
11
+ // completeness checking (TS rejects missing keys, excess keys, and wrong
12
+ // value types on the object literal) without narrowing lookups to literal
13
+ // tuple types — a `satisfies` shape forces every `X[k]` reader to widen
14
+ // back to `ReadonlyArray<V>` themselves to call `.includes(...)`.
9
15
  export const ACTION_EVENT_STEP_TRANSITIONS = {
10
16
  initial: ['parsed', 'failed'],
11
17
  parsed: ['handling', 'failed'],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fuzdev/fuz_app",
3
- "version": "0.46.0",
3
+ "version": "0.48.0",
4
4
  "description": "fullstack app library",
5
5
  "glyph": "🗝",
6
6
  "logo": "logo.svg",