@burojs/react 0.2.0 → 0.5.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.
@@ -0,0 +1,1162 @@
1
+ 'use client';
2
+ "use client";
3
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
4
+ const require_present_flow = require("./present-flow-BM8vaiZX.cjs");
5
+ let _burojs_core = require("@burojs/core");
6
+ let react = require("react");
7
+ let _burojs_core_internal = require("@burojs/core/internal");
8
+ let _burojs_core_flows = require("@burojs/core/flows");
9
+ //#region src/flows/source-reader.ts
10
+ const resource = (target) => ({
11
+ kind: "resource",
12
+ ref: target
13
+ });
14
+ function ownData(value, key) {
15
+ if (value === null || typeof value !== "object") return void 0;
16
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
17
+ return descriptor !== void 0 && "value" in descriptor ? descriptor.value : void 0;
18
+ }
19
+ function operand(value, sources) {
20
+ if (value.kind !== "ref") return value.value;
21
+ let current = sources[value.source];
22
+ for (const segment of value.path) {
23
+ current = ownData(current, segment);
24
+ if (current === void 0) return void 0;
25
+ }
26
+ return current;
27
+ }
28
+ const isMissing = (error) => error.kind === "not-found" || error.kind === "forbidden";
29
+ function configuration(code, message) {
30
+ return (0, _burojs_core.normalizeDataError)(Object.assign(new Error(message), {
31
+ kind: "configuration",
32
+ code
33
+ }));
34
+ }
35
+ function createFlowSourceReader(input) {
36
+ const { runtime } = input;
37
+ const app = runtime.app;
38
+ const registryDef = (target) => app.registry.get(resource(target));
39
+ const idFieldOf = (target) => {
40
+ const idField = registryDef(target)?.data?.idField;
41
+ return typeof idField === "string" && idField !== "" ? idField : void 0;
42
+ };
43
+ const recordDto = (target) => {
44
+ const def = registryDef(target);
45
+ return def === void 0 ? void 0 : (0, _burojs_core.dtoFields)(def, "record");
46
+ };
47
+ const listDto = (target) => {
48
+ const def = registryDef(target);
49
+ return def === void 0 ? void 0 : (0, _burojs_core.dtoFields)(def, "list");
50
+ };
51
+ const context = async () => ({
52
+ runtime,
53
+ snapshot: app.runtime.getSnapshot(),
54
+ session: await runtime.waitUntilReady(),
55
+ locale: input.inputs().locale
56
+ });
57
+ const run = async (descriptor, signal) => {
58
+ if ((0, _burojs_core.isDataError)(descriptor)) throw descriptor;
59
+ return await require_present_flow.executeQuery({
60
+ descriptor,
61
+ runtime,
62
+ signal
63
+ });
64
+ };
65
+ const row = async (target, key, by, signal) => {
66
+ const ctx = await context();
67
+ const projection = recordDto(target);
68
+ const idField = idFieldOf(target) ?? "id";
69
+ const dto = projection === void 0 || projection.includes(idField) ? projection : [...projection, idField];
70
+ try {
71
+ if (by === void 0) {
72
+ const answer = await run(require_present_flow.buildStandardQueryDescriptor(ctx, resource(target), {
73
+ kind: "one",
74
+ id: key,
75
+ ...projection === void 0 ? {} : { projection }
76
+ }), signal);
77
+ return require_present_flow.withRowId(require_present_flow.projectRow(answer?.data ?? null, dto), idField, key);
78
+ }
79
+ const answer = await run(require_present_flow.buildStandardQueryDescriptor(ctx, resource(target), {
80
+ kind: "list",
81
+ filter: {
82
+ field: by,
83
+ op: "eq",
84
+ value: key
85
+ },
86
+ pagination: {
87
+ kind: "offset",
88
+ page: 1,
89
+ pageSize: 2
90
+ },
91
+ ...projection === void 0 ? {} : { projection }
92
+ }), signal);
93
+ const rows = Array.isArray(answer?.data) ? answer.data : [];
94
+ if (rows.length === 0) return null;
95
+ if (rows.length === 1) return require_present_flow.projectRow(rows[0], dto);
96
+ throw (0, _burojs_core.relationAmbiguousError)((0, _burojs_core.formatEntityRef)(target), by, String(key));
97
+ } catch (error) {
98
+ const failure = (0, _burojs_core.normalizeDataError)(error);
99
+ if (isMissing(failure)) return null;
100
+ throw failure;
101
+ }
102
+ };
103
+ const list = async (entry, sources, signal) => {
104
+ const fieldKind = (field) => {
105
+ const found = (0, _burojs_core.snapshotResourceFields)(app.runtime.getSnapshot())(entry.target)?.find((candidate) => candidate.name === field);
106
+ return typeof found?.kind === "string" ? found.kind : void 0;
107
+ };
108
+ const conditions = [];
109
+ for (const condition of entry.where) {
110
+ const value = operand(condition.value, sources);
111
+ if (!require_present_flow.whereValueUsable(value, fieldKind(condition.field))) return entry.count === true ? require_present_flow.EMPTY_COUNTED : require_present_flow.EMPTY_ROWS;
112
+ conditions.push({
113
+ field: condition.field,
114
+ op: condition.op,
115
+ value
116
+ });
117
+ }
118
+ const filter = conditions.length === 0 ? void 0 : conditions.length === 1 ? conditions[0] : { and: conditions };
119
+ const ctx = await context();
120
+ const projection = listDto(entry.target);
121
+ const answer = await run(require_present_flow.buildStandardQueryDescriptor(ctx, resource(entry.target), {
122
+ kind: "list",
123
+ ...filter === void 0 ? {} : { filter },
124
+ ...entry.sort === void 0 ? {} : { sort: entry.sort },
125
+ pagination: {
126
+ kind: "offset",
127
+ page: 1,
128
+ pageSize: entry.limit ?? _burojs_core.LIST_SOURCE_DEFAULT_LIMIT
129
+ },
130
+ ...projection === void 0 ? {} : { projection }
131
+ }), signal);
132
+ const rows = require_present_flow.projectRows(answer?.data, projection);
133
+ if (entry.count !== true) return require_present_flow.listValue(rows);
134
+ const inline = typeof answer?.total === "number" ? answer.total : void 0;
135
+ const execution = require_present_flow.resolveCountExecution("auto", inline, {
136
+ query: true,
137
+ live: false
138
+ });
139
+ if ((0, _burojs_core.isDataError)(execution)) throw execution;
140
+ if (execution.kind === "inline") return require_present_flow.listValue(rows, execution.total);
141
+ const counted = await run(require_present_flow.buildStandardQueryDescriptor(ctx, resource(entry.target), {
142
+ kind: "count",
143
+ ...filter === void 0 ? {} : { filter }
144
+ }), signal);
145
+ if (typeof counted?.total !== "number") throw configuration("count-missing", "The count read returned no total.");
146
+ return require_present_flow.listValue(rows, counted.total);
147
+ };
148
+ const query = async (entry, sources, signal) => {
149
+ const values = {};
150
+ for (const [key, value] of Object.entries(entry.input)) {
151
+ const resolved = operand(value, sources);
152
+ values[key] = resolved === void 0 ? null : resolved;
153
+ }
154
+ const ctx = await context();
155
+ return run((0, _burojs_core.createCustomQueryDescriptor)({
156
+ snapshot: ctx.snapshot,
157
+ session: ctx.session,
158
+ locale: ctx.locale,
159
+ ref: entry.query,
160
+ input: values
161
+ }), signal);
162
+ };
163
+ const via = async (entry, sources, signal) => {
164
+ if (input.owner === null) throw configuration("via-invalid", "An app flow has no record for a 'via' source.");
165
+ let hops;
166
+ try {
167
+ hops = (0, _burojs_core.lowerVia)((0, _burojs_core.normalizeEntityRef)(input.owner), {
168
+ path: entry.path,
169
+ ...entry.to === void 0 ? {} : { to: entry.to },
170
+ ...entry.by === void 0 ? {} : { by: entry.by }
171
+ }, (0, _burojs_core.snapshotResourceFields)(app.runtime.getSnapshot()), idFieldOf);
172
+ } catch (error) {
173
+ const code = error.code;
174
+ throw configuration(typeof code === "string" ? code : "via-invalid", error instanceof Error ? error.message : String(error));
175
+ }
176
+ let current = sources.record ?? null;
177
+ for (const hop of hops) {
178
+ const key = require_present_flow.hopId(ownData(current, hop.field));
179
+ if (key === null) return null;
180
+ current = await row(hop.target, key, hop.by, signal);
181
+ if (current === null) return null;
182
+ }
183
+ return current;
184
+ };
185
+ return async (source, signal) => {
186
+ const { record, args, scope } = input.inputs();
187
+ const sources = {
188
+ record,
189
+ args,
190
+ scope
191
+ };
192
+ switch (source.kind) {
193
+ case "one": {
194
+ const key = require_present_flow.hopId(operand(source.key, sources));
195
+ if (key === null) return null;
196
+ return row(source.target, key, source.by, signal);
197
+ }
198
+ case "list": return list(source, sources, signal);
199
+ case "query": return query(source, sources, signal);
200
+ case "via": return via(source, sources, signal);
201
+ }
202
+ };
203
+ }
204
+ //#endregion
205
+ //#region src/flows/ports.ts
206
+ const resourceHandle = (resource) => ({
207
+ kind: "resource",
208
+ ref: (0, _burojs_core.normalizeEntityRef)(resource)
209
+ });
210
+ function validKey(value) {
211
+ if (typeof value === "string") return value === "" ? void 0 : value;
212
+ if (typeof value === "number" && Number.isFinite(value)) return value;
213
+ }
214
+ function settleQueries(runtime) {
215
+ const client = runtime.queryClient;
216
+ return new Promise((resolve) => {
217
+ let done = false;
218
+ let unsubscribe;
219
+ const finish = () => {
220
+ if (done) return;
221
+ done = true;
222
+ clearTimeout(timer);
223
+ unsubscribe?.();
224
+ resolve();
225
+ };
226
+ const timer = setTimeout(finish, _burojs_core.FLOW_SETTLE_MS);
227
+ Promise.resolve().then(() => {
228
+ if (client.isFetching() === 0) {
229
+ finish();
230
+ return;
231
+ }
232
+ unsubscribe = client.getQueryCache().subscribe(() => {
233
+ if (client.isFetching() === 0) finish();
234
+ });
235
+ });
236
+ });
237
+ }
238
+ function createRuntimeFlowPorts(input) {
239
+ const { runtime, app, flow } = input;
240
+ const projectSourceValue = (name, value) => {
241
+ if (value === null || typeof value !== "object") return value;
242
+ const owner = flow.resource === null ? void 0 : (0, _burojs_core.normalizeEntityRef)(flow.resource);
243
+ let target;
244
+ let shape = "row";
245
+ if (name === "record") target = owner;
246
+ else {
247
+ const withs = flow.flow.with ?? {};
248
+ const entry = Object.hasOwn(withs, name) ? withs[name] : void 0;
249
+ if (entry?.kind === "one") target = entry.target;
250
+ else if (entry?.kind === "list") {
251
+ target = entry.target;
252
+ shape = "rows";
253
+ } else if (entry?.kind === "via" && owner !== void 0) try {
254
+ const fieldsOf = (0, _burojs_core.snapshotResourceFields)(app.runtime.getSnapshot());
255
+ target = (0, _burojs_core.lowerVia)(owner, {
256
+ path: entry.path,
257
+ ...entry.to === void 0 ? {} : { to: entry.to },
258
+ ...entry.by === void 0 ? {} : { by: entry.by }
259
+ }, fieldsOf).at(-1)?.target;
260
+ } catch {
261
+ return null;
262
+ }
263
+ }
264
+ if (target === void 0) return value;
265
+ const def = app.registry.get({
266
+ kind: "resource",
267
+ ref: target
268
+ });
269
+ if (def === void 0) return value;
270
+ const context = input.context();
271
+ const resource = (0, _burojs_core.formatEntityRef)(target);
272
+ const decisions = (0, _burojs_core.resolveFieldAccess)({
273
+ rules: app.ruleSource.rules(resource),
274
+ fields: (0, _burojs_core.dtoFields)(def, shape === "row" ? "record" : "list"),
275
+ context: (0, _burojs_core.ruleContextFor)({
276
+ user: context.user,
277
+ scope: context.scope,
278
+ ...context.permissions === void 0 ? {} : { permissions: context.permissions },
279
+ ...shape === "row" ? { record: value } : {},
280
+ mode: shape === "row" ? "show" : "list",
281
+ resource
282
+ }),
283
+ permissions: context.permissions,
284
+ reach: shape === "row" ? "record" : "every"
285
+ });
286
+ const closed = Object.fromEntries(Object.entries(decisions).map(([field, decision]) => [field, decision.view === "visible" ? decision : {
287
+ ...decision,
288
+ view: "hidden"
289
+ }]));
290
+ const idField = def.data?.idField;
291
+ const projected = (0, _burojs_core.projectSource)({
292
+ status: "ready",
293
+ value,
294
+ refreshing: false
295
+ }, closed, shape, typeof idField === "string" && idField !== "" ? idField : "id");
296
+ return projected.status === "ready" ? projected.value : value;
297
+ };
298
+ const describe = async (call) => {
299
+ if (call.op === "custom") return require_present_flow.customMutationDescriptor(runtime, (0, _burojs_core.normalizeEntityRef)(call.target), call.input);
300
+ const base = await require_present_flow.resourceDescriptorBase(runtime, resourceHandle(call.target));
301
+ const kind = call.op === "create" ? "createOne" : call.op === "update" ? "updateOne" : "deleteOne";
302
+ return (0, _burojs_core.createMutationDescriptor)({
303
+ ...base,
304
+ kind,
305
+ ...call.id === void 0 ? {} : { id: call.id },
306
+ ...call.op === "delete" || call.input === void 0 ? {} : { input: call.input }
307
+ });
308
+ };
309
+ const queryOne = async (resource, id, signal) => {
310
+ const base = await require_present_flow.resourceDescriptorBase(runtime, resourceHandle(resource));
311
+ return (await require_present_flow.executeQuery({
312
+ descriptor: (0, _burojs_core.createQueryDescriptor)({
313
+ ...base,
314
+ kind: "one",
315
+ id
316
+ }),
317
+ runtime,
318
+ signal
319
+ })).data;
320
+ };
321
+ const hasOne = (resource) => {
322
+ try {
323
+ const binding = app.resolveDataBinding(resourceHandle(resource));
324
+ const service = runtime.providers.resolve(binding.providerId);
325
+ return typeof (0, _burojs_core.resolveDataCapability)(service.provider, {
326
+ lifecycle: "query",
327
+ kind: "one"
328
+ }) === "function";
329
+ } catch {
330
+ return false;
331
+ }
332
+ };
333
+ const replaySafe = () => {
334
+ if (flow.op !== "custom") return false;
335
+ try {
336
+ const value = app.runtime.getSnapshot().getProjection({
337
+ kind: "custom-mutation",
338
+ ref: (0, _burojs_core.normalizeEntityRef)(flow.target)
339
+ })?.value;
340
+ const replay = value?.replay;
341
+ if ((replay?.capability === "mutation-replay-policy" && typeof replay.id === "string" ? app.getPluginCapability("mutation-replay-policy", replay.id) : void 0) !== "once-after-auth-refresh" || value?.binding?.provider === void 0) return false;
342
+ const service = runtime.providers.resolve(value.binding.provider);
343
+ return (0, _burojs_core.resolveDataCapability)(service.provider, {
344
+ lifecycle: "mutation",
345
+ kind: "custom"
346
+ }).replay?.idempotency === "keyed-replay-once";
347
+ } catch {
348
+ return false;
349
+ }
350
+ };
351
+ const readSource = createFlowSourceReader({
352
+ runtime,
353
+ owner: flow.resource,
354
+ inputs: () => {
355
+ const context = input.context();
356
+ return {
357
+ record: input.record,
358
+ args: input.args ?? {},
359
+ scope: context.scope,
360
+ locale: context.locale
361
+ };
362
+ }
363
+ });
364
+ return {
365
+ gate() {
366
+ const context = input.context();
367
+ return (0, _burojs_core.resolveFlowGate)({
368
+ app,
369
+ resource: flow.resource,
370
+ flow,
371
+ ctx: (0, _burojs_core.ruleContextFor)({
372
+ ...input.record === void 0 ? {} : { record: input.record },
373
+ ...flow.resource === null ? {} : { resource: flow.resource },
374
+ mode: input.record === void 0 ? "list" : "show",
375
+ user: context.user,
376
+ scope: context.scope,
377
+ ...context.permissions === void 0 ? {} : { permissions: context.permissions }
378
+ }),
379
+ permissionsStatus: context.permissions?.status
380
+ });
381
+ },
382
+ read(_name, source, signal) {
383
+ return readSource(source, signal);
384
+ },
385
+ async reread(resource, ids, signal) {
386
+ const clean = ids.filter((id) => validKey(id) !== void 0);
387
+ if (clean.length === 0) return [];
388
+ if (clean.length === 1 && hasOne(resource)) {
389
+ const row = await queryOne(resource, clean[0], signal);
390
+ return row === null || row === void 0 ? [] : [row];
391
+ }
392
+ const base = await require_present_flow.resourceDescriptorBase(runtime, resourceHandle(resource));
393
+ return (await require_present_flow.executeQuery({
394
+ descriptor: (0, _burojs_core.createQueryDescriptor)({
395
+ ...base,
396
+ kind: "many",
397
+ ids: clean
398
+ }),
399
+ runtime,
400
+ signal
401
+ })).data ?? [];
402
+ },
403
+ async mutate(call) {
404
+ const descriptor = await describe(call);
405
+ const output = await require_present_flow.executeMutation({
406
+ descriptor,
407
+ runtime,
408
+ idempotencyKey: call.key,
409
+ ...call.replay === "never" ? { replay: "never" } : {}
410
+ });
411
+ await settleQueries(runtime);
412
+ return output;
413
+ },
414
+ attempts: input.attempts,
415
+ audit(event) {
416
+ input.context().audit?.emit(event, { initiates: true });
417
+ },
418
+ checks: {
419
+ source: (resource) => app.checkSource.checks(resource),
420
+ data: input.data,
421
+ get ctx() {
422
+ return input.context().visibility;
423
+ },
424
+ t: (key, params, defaultValue) => input.context().t(key, params, defaultValue)
425
+ },
426
+ form: input.form,
427
+ project: projectSourceValue,
428
+ attachment: (0, _burojs_core.trustedFlowAttachments)(app.runtime, flow),
429
+ clock: {
430
+ now: () => Date.now(),
431
+ uuid: () => (0, _burojs_core.createIdempotencyKey)(),
432
+ wait: (ms, signal) => new Promise((resolve, reject) => {
433
+ const timer = setTimeout(resolve, ms);
434
+ signal.addEventListener("abort", () => {
435
+ clearTimeout(timer);
436
+ reject(signal.reason);
437
+ }, { once: true });
438
+ })
439
+ },
440
+ hash: _burojs_core_flows.hashCanonical,
441
+ user: input.user,
442
+ get replaySafe() {
443
+ return replaySafe();
444
+ }
445
+ };
446
+ }
447
+ //#endregion
448
+ //#region src/flows/use-flow-run.ts
449
+ const IDLE = Object.freeze({
450
+ step: "gate",
451
+ history: Object.freeze([]),
452
+ control: "undecided",
453
+ values: Object.freeze({}),
454
+ issues: Object.freeze([]),
455
+ warnings: Object.freeze([]),
456
+ key: null,
457
+ attempt: null,
458
+ canResend: false,
459
+ formStep: null
460
+ });
461
+ const ABSENT = Object.freeze({
462
+ ...IDLE,
463
+ control: "absent"
464
+ });
465
+ const NO_SUBSCRIBE = () => () => {};
466
+ function compileTarget(app, target) {
467
+ const named = (0, _burojs_core.isFlowRef)(target) ? target.target : target;
468
+ try {
469
+ if ("appAction" in named) {
470
+ const def = app.registry.appActions.get(named.appAction);
471
+ return def === void 0 ? null : (0, _burojs_core.compileFlow)(def, named.appAction);
472
+ }
473
+ const def = app.registry.get(named.resource);
474
+ return def === void 0 ? null : (0, _burojs_core.compileFlow)(def, named.action);
475
+ } catch {
476
+ return null;
477
+ }
478
+ }
479
+ const keyOfTarget = (target) => {
480
+ const named = (0, _burojs_core.isFlowRef)(target) ? target.target : target;
481
+ return "appAction" in named ? `app:${named.appAction}` : `r:${named.resource}/${named.action}`;
482
+ };
483
+ let warnedNoUser = false;
484
+ const isObject$1 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
485
+ const idOf = (value) => typeof value === "string" && value !== "" ? value : typeof value === "number" && Number.isFinite(value) ? String(value) : void 0;
486
+ function flowAttemptKey(compiled, user, record, args, selection) {
487
+ const recordId = idOf(record?.[compiled.idField] ?? record?.id);
488
+ return (0, _burojs_core.attemptKey)({
489
+ user,
490
+ flow: compiled.flowId,
491
+ slot: (0, _burojs_core.attemptSlot)({
492
+ ...recordId === void 0 ? {} : { record: recordId },
493
+ ...args === void 0 || Object.keys(args).length === 0 ? {} : { argsHash: (0, _burojs_core.fnv1a)(args) },
494
+ ...selection === void 0 || compiled.each ? {} : { selectionHash: (0, _burojs_core.fnv1a)([...selection].sort()) }
495
+ })
496
+ });
497
+ }
498
+ function thenFor(compiled, effects, record) {
499
+ let open;
500
+ const target = effects.open;
501
+ if (isObject$1(target) && !Object.hasOwn(target, "block")) {
502
+ const resource = typeof target.resource === "string" ? target.resource : compiled.resource ?? void 0;
503
+ const id = idOf(target.id) ?? idOf(record?.[compiled.idField] ?? record?.id);
504
+ if (resource !== void 0 && id !== void 0) open = {
505
+ kind: "record",
506
+ resource,
507
+ id,
508
+ mode: target.mode === "edit" ? "edit" : "show",
509
+ quick: target.quick === true
510
+ };
511
+ }
512
+ const then = {
513
+ ...effects.notify === void 0 ? {} : { notify: effects.notify },
514
+ ...effects.redirect === void 0 ? {} : { redirect: effects.redirect },
515
+ ...open === void 0 ? {} : { open }
516
+ };
517
+ return Object.keys(then).length === 0 ? null : then;
518
+ }
519
+ function useFlowRun(target, opts = {}) {
520
+ const app = require_present_flow.useApp();
521
+ const runtime = require_present_flow.useOptionalDataRuntime();
522
+ const appSnapshot = require_present_flow.useApplicationSnapshot();
523
+ const targetKey = keyOfTarget(target);
524
+ const compiled = (0, react.useMemo)(() => compileTarget(app, target), [
525
+ app,
526
+ targetKey,
527
+ appSnapshot
528
+ ]);
529
+ const userId = require_present_flow.useCurrentUserId();
530
+ const store = require_present_flow.useFlowAttemptsStore();
531
+ const context = require_present_flow.useContextSnapshot();
532
+ const signedOut = require_present_flow.isSignedOut(context);
533
+ const visibility = require_present_flow.useOptionalVisibilityCtx();
534
+ const translate = require_present_flow.useTranslate();
535
+ const locale = require_present_flow.useLocale();
536
+ const audit = require_present_flow.useFrameworkAudit();
537
+ const latest = (0, react.useRef)(null);
538
+ latest.current = {
539
+ user: context.user,
540
+ scope: context.scope,
541
+ permissions: require_present_flow.readPermissions(context),
542
+ visibility,
543
+ t: (key, params, defaultValue) => {
544
+ const text = translate(key, params === void 0 ? void 0 : { params });
545
+ return text === key ? defaultValue ?? key : text;
546
+ },
547
+ audit,
548
+ locale
549
+ };
550
+ const data = (0, react.useMemo)(() => runtime === null ? require_present_flow.createUnavailableActionDataAccess("A flow needs a data runtime; none is mounted.") : require_present_flow.createRuntimeActionDataAccess(runtime), [runtime]);
551
+ const liveRecordKey = opts.record === void 0 ? "" : JSON.stringify(opts.record);
552
+ const heldRecordKey = (0, react.useRef)(liveRecordKey);
553
+ const writing = (0, react.useRef)(false);
554
+ if (!writing.current) heldRecordKey.current = liveRecordKey;
555
+ const recordKey = heldRecordKey.current;
556
+ const argsKey = opts.args === void 0 ? "" : JSON.stringify(opts.args);
557
+ const selectionKey = opts.selection === void 0 ? "" : [...opts.selection.ids].sort().join("\0");
558
+ const latestOpts = (0, react.useRef)(opts);
559
+ latestOpts.current = opts;
560
+ const [generation, setGeneration] = (0, react.useState)(0);
561
+ const carried = (0, react.useRef)(void 0);
562
+ const restart = (0, react.useCallback)(() => {
563
+ const lease = latestOpts.current.lease;
564
+ if (lease?.run != null) {
565
+ require_present_flow.disposeWhenIdle(lease.run);
566
+ lease.run = null;
567
+ lease.deps = null;
568
+ }
569
+ setGeneration((value) => value + 1);
570
+ }, []);
571
+ const attempts = (0, react.useMemo)(() => {
572
+ if (compiled === null) return null;
573
+ if (userId === null) return signedOut ? (0, _burojs_core_flows.createMemoryAttempts)() : null;
574
+ return store === null || store.user !== userId ? null : store.port;
575
+ }, [
576
+ compiled,
577
+ userId,
578
+ signedOut,
579
+ store,
580
+ generation
581
+ ]);
582
+ const run = (0, react.useMemo)(() => {
583
+ if (compiled === null || runtime === null || attempts === null) return null;
584
+ if (latestOpts.current.linkOnly === true && (compiled.flow.host === "page" || compiled.flow.host !== void 0 && typeof compiled.flow.host === "object")) return null;
585
+ const { record, args, selection, lease } = latestOpts.current;
586
+ const leaseDeps = lease === void 0 ? null : [
587
+ compiled.flowId,
588
+ userId,
589
+ lease.record,
590
+ argsKey
591
+ ].join("\0");
592
+ if (lease?.run != null && lease.deps === leaseDeps) return lease.run;
593
+ const ports = createRuntimeFlowPorts({
594
+ runtime,
595
+ app,
596
+ flow: compiled,
597
+ ...record === void 0 ? {} : { record },
598
+ ...args === void 0 ? {} : { args },
599
+ attempts,
600
+ form: (0, _burojs_core_flows.createFlowFormPort)(compiled.flow, app, { i18nPrefix: compiled.formId }),
601
+ user: userId,
602
+ data,
603
+ context: () => latest.current
604
+ });
605
+ const created = (0, _burojs_core_flows.createFlowRun)({
606
+ flow: compiled,
607
+ ...record === void 0 ? {} : { record },
608
+ ...args === void 0 ? {} : { args },
609
+ ...selection === void 0 ? {} : { selection },
610
+ ports
611
+ });
612
+ require_present_flow.registerFlowRun(created, compiled.formId);
613
+ if (carried.current !== void 0) created.setValues(carried.current);
614
+ carried.current = void 0;
615
+ if (lease !== void 0) {
616
+ lease.run = created;
617
+ lease.deps = leaseDeps;
618
+ lease.opened = false;
619
+ }
620
+ return created;
621
+ }, [
622
+ compiled,
623
+ runtime,
624
+ attempts,
625
+ app,
626
+ userId,
627
+ data,
628
+ recordKey,
629
+ argsKey,
630
+ selectionKey,
631
+ generation
632
+ ]);
633
+ (0, react.useEffect)(() => {
634
+ if (!signedOut || compiled === null || warnedNoUser) return;
635
+ if (process.env.NODE_ENV === "production") return;
636
+ warnedNoUser = true;
637
+ console.warn(`[burojs] ${compiled.flowId} runs without a signed-in user: its attempts are kept in memory for this run only, and its form keeps no draft.`);
638
+ }, [signedOut, compiled]);
639
+ (0, react.useEffect)(() => {
640
+ if (run === null) return;
641
+ const lease = latestOpts.current.lease;
642
+ if (lease === void 0) {
643
+ run.open();
644
+ return () => run.dispose();
645
+ }
646
+ if (lease.opened) return;
647
+ lease.opened = true;
648
+ run.open();
649
+ }, [run]);
650
+ const permissionsStatus = require_present_flow.readPermissions(context)?.status;
651
+ (0, react.useEffect)(() => {
652
+ if (run === null || permissionsStatus === "loading") return;
653
+ if (run.getSnapshot().control === "undecided") run.open();
654
+ }, [run, permissionsStatus]);
655
+ const snapshot = (0, react.useSyncExternalStore)(run?.subscribe ?? NO_SUBSCRIBE, () => run?.getSnapshot() ?? (compiled === null ? ABSENT : IDLE), () => run?.getSnapshot() ?? (compiled === null ? ABSENT : IDLE));
656
+ const locked = snapshot.control === "running";
657
+ writing.current = locked;
658
+ require_present_flow.useDismissPending(locked);
659
+ require_present_flow.useNavigationHold(locked);
660
+ const scopeEffects = require_present_flow.useDispatcherEffects();
661
+ const effectsOf = opts.thenEffects ?? scopeEffects;
662
+ const ranThen = (0, react.useRef)(null);
663
+ const outcome = snapshot.outcome;
664
+ const thenEffects = snapshot.effects;
665
+ const thenHeld = (0, react.useRef)(false);
666
+ const thenOf = (0, react.useRef)({
667
+ run,
668
+ compiled
669
+ });
670
+ thenOf.current = {
671
+ run,
672
+ compiled
673
+ };
674
+ const runThen = (0, react.useCallback)(() => {
675
+ const { run: current, compiled: flow } = thenOf.current;
676
+ if (current === null || flow === null) return;
677
+ const now = current.getSnapshot();
678
+ if (now.outcome?.outcome !== "success" || now.effects === void 0) return;
679
+ if (ranThen.current === now.outcome) return;
680
+ ranThen.current = now.outcome;
681
+ const then = thenFor(flow, now.effects, latestOpts.current.record);
682
+ if (then === null) return;
683
+ (0, _burojs_core.runThenEffects)(effectsOf(), then).catch((error) => {
684
+ if (process.env.NODE_ENV !== "production") console.error(`[burojs] ${flow.flowId}: a then effect threw.`, error);
685
+ });
686
+ }, [effectsOf]);
687
+ const holdThen = (0, react.useCallback)((hold) => {
688
+ thenHeld.current = hold;
689
+ }, []);
690
+ (0, react.useEffect)(() => {
691
+ if (thenHeld.current) return;
692
+ runThen();
693
+ }, [
694
+ run,
695
+ compiled,
696
+ outcome,
697
+ thenEffects,
698
+ runThen
699
+ ]);
700
+ const lockedRef = (0, react.useRef)(locked);
701
+ lockedRef.current = locked;
702
+ (0, react.useEffect)(() => {
703
+ if (run === null || store === null || compiled === null || compiled.each) return;
704
+ const { record, args, selection } = latestOpts.current;
705
+ const key = flowAttemptKey(compiled, userId, record, args, selection?.ids);
706
+ return store.subscribe(() => {
707
+ if (lockedRef.current) return;
708
+ const now = run.getSnapshot();
709
+ const held = now.outcome?.outcome === "unknown" ? now.key : null;
710
+ if ((store.port.read(key)?.key ?? null) === held) return;
711
+ carried.current = now.values;
712
+ restart();
713
+ });
714
+ }, [
715
+ run,
716
+ store,
717
+ compiled,
718
+ userId,
719
+ restart
720
+ ]);
721
+ const reset = (0, react.useCallback)(() => {
722
+ if (lockedRef.current) return;
723
+ carried.current = void 0;
724
+ restart();
725
+ }, [restart]);
726
+ return (0, react.useMemo)(() => ({
727
+ run,
728
+ snapshot,
729
+ compiled,
730
+ reset,
731
+ holdThen,
732
+ runThen
733
+ }), [
734
+ run,
735
+ snapshot,
736
+ compiled,
737
+ reset,
738
+ holdThen,
739
+ runThen
740
+ ]);
741
+ }
742
+ //#endregion
743
+ //#region src/hooks/use-action.ts
744
+ const isObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
745
+ const isStandard = (value) => isObject(value) && isObject(value["~standard"]) && typeof value["~standard"].validate === "function";
746
+ function schemaOf(declared, attachment) {
747
+ if (isStandard(declared)) return declared;
748
+ if (!isObject(declared)) return void 0;
749
+ const schema = declared.schema;
750
+ if (isStandard(schema)) return schema;
751
+ if (isObject(schema) && typeof schema.capability === "string" && typeof schema.id === "string") {
752
+ const resolved = attachment(schema.capability, schema.id);
753
+ if (isStandard(resolved)) return resolved;
754
+ if (isObject(resolved) && isStandard(resolved.schema)) return resolved.schema;
755
+ }
756
+ }
757
+ async function invalidArg(compiled, args, attachment) {
758
+ const declared = compiled.flow.args;
759
+ for (const [name, entry] of Object.entries(declared ?? {})) {
760
+ const optional = isObject(entry) && (entry.optional === true || entry.isOptional === true);
761
+ if (args[name] === void 0 && optional) continue;
762
+ const schema = schemaOf(entry, attachment);
763
+ if (schema === void 0) continue;
764
+ if ((await schema["~standard"].validate(args[name])).issues !== void 0) return name;
765
+ }
766
+ return null;
767
+ }
768
+ function useAction(target, opts = {}) {
769
+ const app = require_present_flow.useApp();
770
+ const navigate = require_present_flow.useRouteNavigate();
771
+ const { run, snapshot, compiled } = useFlowRun(target, {
772
+ ...opts.record === void 0 ? {} : { record: opts.record },
773
+ ...opts.args === void 0 ? {} : { args: opts.args },
774
+ surface: "code",
775
+ linkOnly: true
776
+ });
777
+ const named = (0, _burojs_core.isFlowRef)(target) ? target.target : target;
778
+ const resolveRow = require_present_flow.useFlowRowResolver("resource" in named ? named.resource : "");
779
+ const pageHosted = compiled?.flow.host === "page";
780
+ const hostPage = require_present_flow.useHostPage(compiled?.flow.host);
781
+ const hosted = require_present_flow.hostPageRef(compiled?.flow.host) !== void 0;
782
+ const hostGate = require_present_flow.usePageFlowGate(named);
783
+ const pageControl = pageHosted ? resolveRow(compiled.name, opts.record) : void 0;
784
+ const title = require_present_flow.useProjectedTitle(compiled?.resource ?? void 0, compiled === null ? void 0 : opts.record?.[compiled.idField] ?? opts.record?.id, opts.record);
785
+ const present = require_present_flow.usePresentFlow();
786
+ const presentInput = {
787
+ label: compiled === null ? "" : typeof compiled.flow.label === "string" ? compiled.flow.label : (0, _burojs_core.humanizeKey)(compiled.name),
788
+ ...opts.record === void 0 || title === "" ? {} : { title },
789
+ ...opts.record === void 0 ? {} : { record: opts.record },
790
+ ...opts.args === void 0 ? {} : { args: opts.args }
791
+ };
792
+ const hostRef = (0, react.useRef)({
793
+ present,
794
+ presentInput,
795
+ target
796
+ });
797
+ hostRef.current = {
798
+ present,
799
+ presentInput,
800
+ target
801
+ };
802
+ const inflight = (0, react.useRef)(null);
803
+ const hostState = hosted ? require_present_flow.conjoinHostPage(hostGate.answer.state === "available" ? "ready" : hostGate.answer.state === "hidden" ? "absent" : hostGate.answer.state, hostPage) : void 0;
804
+ const hostLink = hostState === void 0 ? void 0 : {
805
+ state: hostState,
806
+ href: hostPage?.href ?? ""
807
+ };
808
+ const latest = (0, react.useRef)({
809
+ run,
810
+ snapshot,
811
+ compiled,
812
+ args: opts.args,
813
+ pageControl,
814
+ hostLink
815
+ });
816
+ latest.current = {
817
+ run,
818
+ snapshot,
819
+ compiled,
820
+ args: opts.args,
821
+ pageControl,
822
+ hostLink
823
+ };
824
+ const pageOf = (0, react.useRef)({
825
+ navigate,
826
+ app,
827
+ record: opts.record
828
+ });
829
+ pageOf.current = {
830
+ navigate,
831
+ app,
832
+ record: opts.record
833
+ };
834
+ const attachments = (0, react.useMemo)(() => compiled === null ? () => void 0 : (0, _burojs_core.trustedFlowAttachments)(app.runtime, compiled), [app, compiled]);
835
+ const runNow = (0, react.useCallback)(() => {
836
+ if (inflight.current !== null) return inflight.current;
837
+ const { run: current, snapshot: now, compiled: flow, args, pageControl: link, hostLink: pageLink } = latest.current;
838
+ if (pageLink !== void 0) {
839
+ if (pageLink.state !== "ready" || pageLink.href === "") return Promise.resolve(Object.freeze({
840
+ outcome: "unavailable",
841
+ state: pageLink.state
842
+ }));
843
+ pageOf.current.navigate(pageLink.href);
844
+ return Promise.resolve(Object.freeze({ outcome: "navigated" }));
845
+ }
846
+ if (flow !== null && link !== void 0) {
847
+ if (link.state !== "ready") return Promise.resolve(Object.freeze({
848
+ outcome: "unavailable",
849
+ state: link.state
850
+ }));
851
+ const { navigate: go, app: owner, record } = pageOf.current;
852
+ const id = record?.[flow.idField] ?? record?.id;
853
+ if (typeof id !== "string" && typeof id !== "number" || String(id) === "" || flow.resource === null) {
854
+ if (process.env.NODE_ENV !== "production") console.error(`[burojs] useAction(${flow.flowId}): a page-hosted flow needs the record it runs on; nothing was opened.`);
855
+ return Promise.resolve(Object.freeze({ outcome: "failed" }));
856
+ }
857
+ go((0, _burojs_core.hrefFor)(owner.registry, flow.resource, {
858
+ action: "flow",
859
+ id: String(id),
860
+ flow: flow.name
861
+ }));
862
+ return Promise.resolve(Object.freeze({ outcome: "navigated" }));
863
+ }
864
+ if (current === null || flow === null || now.control === "undecided" || now.control === "absent") return Promise.resolve(Object.freeze({
865
+ outcome: "unavailable",
866
+ state: current === null && flow !== null ? "undecided" : now.control
867
+ }));
868
+ const started = (async () => {
869
+ const invalid = await invalidArg(flow, args ?? {}, attachments);
870
+ if (invalid !== null) {
871
+ if (process.env.NODE_ENV !== "production") console.error(`[burojs] useAction(${flow.flowId}): args.${invalid} does not match the flow's declared args; nothing was run.`);
872
+ return Object.freeze({ outcome: "failed" });
873
+ }
874
+ if (flow.hasHost) {
875
+ const { present: open, presentInput: given, target: what } = hostRef.current;
876
+ const closed = open({
877
+ ...given,
878
+ target: what,
879
+ host: flow.flow.host === "sheet" ? "sheet" : "dialog"
880
+ });
881
+ if (closed !== null) return closed;
882
+ }
883
+ return current.submit();
884
+ })();
885
+ inflight.current = started;
886
+ started.finally(() => {
887
+ if (inflight.current === started) inflight.current = null;
888
+ });
889
+ return started;
890
+ }, [attachments]);
891
+ const state = hostState ?? (pageControl === void 0 ? snapshot.control : pageControl.state);
892
+ const reason = hosted ? hostGate.answer.state === "disabled" ? hostGate.answer.reason : void 0 : pageControl === void 0 ? snapshot.reason : pageControl.reason;
893
+ return (0, react.useMemo)(() => Object.freeze({
894
+ state,
895
+ ...reason === void 0 ? {} : { reason },
896
+ run: runNow
897
+ }), [
898
+ state,
899
+ reason,
900
+ runNow
901
+ ]);
902
+ }
903
+ //#endregion
904
+ //#region src/flows/use-flow-form.ts
905
+ const NO_FIELDS = Object.freeze({});
906
+ function useFlowForm(run, compiled, snapshot, opts = {}) {
907
+ const form = compiled?.flow.form;
908
+ const userId = require_present_flow.useCurrentUserId();
909
+ const fields = (0, react.useMemo)(() => form === void 0 ? NO_FIELDS : (0, _burojs_core_internal.fieldEntriesToMap)(form.fields), [compiled]);
910
+ const tree = form?.body;
911
+ const body = (0, react.useMemo)(() => tree === void 0 ? void 0 : () => tree, [compiled]);
912
+ const recordId = opts.record === void 0 || compiled === null ? void 0 : opts.record[compiled.idField] ?? opts.record.id;
913
+ const flowDraft = (0, react.useMemo)(() => ({
914
+ ...typeof recordId === "string" || typeof recordId === "number" ? { recordId: String(recordId) } : {},
915
+ ...opts.args === void 0 || Object.keys(opts.args).length === 0 ? {} : { argsHash: (0, _burojs_core.fnv1a)(opts.args) },
916
+ ...opts.selection === void 0 || compiled?.each === true ? {} : { selectionHash: (0, _burojs_core.fnv1a)([...opts.selection].sort()) },
917
+ ...compiled === null ? {} : { attempt: flowAttemptKey(compiled, userId, opts.record, opts.args, opts.selection) }
918
+ }), [
919
+ String(recordId),
920
+ JSON.stringify(opts.args ?? null),
921
+ (opts.selection ?? []).join("\0"),
922
+ compiled,
923
+ userId
924
+ ]);
925
+ const onChange = (0, react.useCallback)((values) => run?.setValues(values), [run]);
926
+ const blocks = require_present_flow.useFlowFormCatalog();
927
+ const metaSources = (0, react.useMemo)(() => Object.freeze({
928
+ record: Object.freeze({
929
+ status: "ready",
930
+ value: opts.record ?? null,
931
+ refreshing: false
932
+ }),
933
+ args: Object.freeze({
934
+ status: "ready",
935
+ value: opts.args ?? {},
936
+ refreshing: false
937
+ })
938
+ }), [opts.record, opts.args]);
939
+ const controller = require_present_flow.useFormController({
940
+ blocks,
941
+ metaSources,
942
+ fields,
943
+ ...body === void 0 ? {} : { body },
944
+ mode: "create",
945
+ surface: "create",
946
+ ...compiled === null ? {} : {
947
+ formId: compiled.formId,
948
+ i18nPrefix: compiled.resource === null ? `appActions.${compiled.name}` : `${compiled.resource}.actions.${compiled.name}`
949
+ },
950
+ ...form?.draft === void 0 || run === null ? {} : { draft: form.draft },
951
+ ...run === null ? {} : { flowRun: run },
952
+ flowDraft,
953
+ flowIssues: snapshot.issues,
954
+ value: snapshot.values,
955
+ onChange
956
+ });
957
+ const seeded = (0, react.useRef)(null);
958
+ const opened = snapshot.history.includes("open");
959
+ (0, react.useEffect)(() => {
960
+ if (run === null || !opened || seeded.current === run) return;
961
+ seeded.current = run;
962
+ controller.reset(run.getSnapshot().values);
963
+ }, [
964
+ run,
965
+ opened,
966
+ controller
967
+ ]);
968
+ const succeeded = snapshot.outcome?.outcome === "success";
969
+ const outcome = snapshot.outcome;
970
+ const cleared = (0, react.useRef)(null);
971
+ (0, react.useEffect)(() => {
972
+ if (!succeeded || cleared.current === outcome) return;
973
+ cleared.current = outcome;
974
+ controller.reset(snapshot.values);
975
+ controller.draft.discard();
976
+ }, [
977
+ succeeded,
978
+ outcome,
979
+ snapshot.values,
980
+ controller
981
+ ]);
982
+ return controller;
983
+ }
984
+ //#endregion
985
+ //#region src/flows/use-flow-host.ts
986
+ function useFlowHost(flow, opts) {
987
+ const runtime = require_present_flow.useUIPresentationRuntime();
988
+ const mount = require_present_flow.useOptionalPresentationMount();
989
+ const registry = (0, react.useContext)(require_present_flow.DismissRegistryContext);
990
+ const enclosing = (0, react.useContext)(require_present_flow.DismissScopeContext);
991
+ const activation = require_present_flow.useCurrentUIActivation();
992
+ const [openId, setOpenId] = (0, react.useState)(null);
993
+ const [closed, setClosed] = (0, react.useState)(0);
994
+ const current = (0, react.useRef)(null);
995
+ const latest = (0, react.useRef)({
996
+ flow,
997
+ opts
998
+ });
999
+ latest.current = {
1000
+ flow,
1001
+ opts
1002
+ };
1003
+ const open = (0, react.useCallback)((hostOverride) => {
1004
+ const { flow: now, opts: given } = latest.current;
1005
+ const { run, compiled } = now;
1006
+ if (run === null || compiled === null || mount === null) return null;
1007
+ if (current.current !== null) return null;
1008
+ const host = hostOverride ?? (compiled.flow.host === "sheet" ? "sheet" : "dialog");
1009
+ if (mount.mode === "app" && !mount.registry.isMounted()) {
1010
+ console.error(`[burojs] ${compiled.flowId} opened nothing: BuroProvider is in presentations="app" mode but no <BuroPresentationOutlet/> is mounted.`);
1011
+ return null;
1012
+ }
1013
+ if (mount.hosts[host] === void 0) {
1014
+ require_present_flow.reportMissingFlowHost(mount.registry, host);
1015
+ return null;
1016
+ }
1017
+ if (mount.hosts.screens?.flow === void 0) {
1018
+ require_present_flow.reportMissingFlowHost(mount.registry, "screens.flow");
1019
+ return null;
1020
+ }
1021
+ const props = {
1022
+ run,
1023
+ host,
1024
+ label: given.label,
1025
+ compiled,
1026
+ ...given.title === void 0 ? {} : { title: given.title },
1027
+ ...given.record === void 0 ? {} : { record: given.record },
1028
+ ...given.args === void 0 ? {} : { args: given.args },
1029
+ ...given.selection === void 0 ? {} : { selection: given.selection }
1030
+ };
1031
+ const result = runtime.present({
1032
+ component: require_present_flow.FLOW_COMPONENT,
1033
+ host,
1034
+ props: { entry: new require_present_flow.FlowScreenEntry(props) },
1035
+ options: {
1036
+ ownTitle: true,
1037
+ wrapper: false
1038
+ },
1039
+ activation
1040
+ });
1041
+ if (!result.ok) return null;
1042
+ if (registry !== null && enclosing !== null) registry.recordParent(result.instance.id, enclosing.id);
1043
+ let settle = () => void 0;
1044
+ const closed = new Promise((resolve) => {
1045
+ settle = resolve;
1046
+ });
1047
+ current.current = {
1048
+ id: result.instance.id,
1049
+ settle,
1050
+ run
1051
+ };
1052
+ now.holdThen(true);
1053
+ setOpenId(result.instance.id);
1054
+ return closed;
1055
+ }, [
1056
+ runtime,
1057
+ mount,
1058
+ registry,
1059
+ enclosing,
1060
+ activation
1061
+ ]);
1062
+ (0, react.useEffect)(() => {
1063
+ if (openId === null) return;
1064
+ const check = () => {
1065
+ const open = current.current;
1066
+ if (open === null || open.id !== openId) return;
1067
+ if (runtime.getSnapshot().some((descriptor) => descriptor.id === openId)) return;
1068
+ current.current = null;
1069
+ const snapshot = open.run.getSnapshot();
1070
+ open.settle(snapshot.outcome ?? Object.freeze({
1071
+ outcome: "halted",
1072
+ step: snapshot.step
1073
+ }));
1074
+ setOpenId(null);
1075
+ setClosed((value) => value + 1);
1076
+ };
1077
+ check();
1078
+ return runtime.subscribe(check);
1079
+ }, [runtime, openId]);
1080
+ (0, react.useEffect)(() => {
1081
+ if (closed === 0) return;
1082
+ latest.current.flow.holdThen(false);
1083
+ latest.current.flow.runThen();
1084
+ latest.current.flow.reset();
1085
+ }, [closed]);
1086
+ (0, react.useEffect)(() => () => {
1087
+ const open = current.current;
1088
+ if (open === null) return;
1089
+ current.current = null;
1090
+ runtime.dismiss(open.id);
1091
+ const snapshot = open.run.getSnapshot();
1092
+ open.settle(snapshot.outcome ?? Object.freeze({
1093
+ outcome: "halted",
1094
+ step: snapshot.step
1095
+ }));
1096
+ }, [runtime]);
1097
+ return (0, react.useMemo)(() => ({
1098
+ open,
1099
+ isOpen: openId !== null
1100
+ }), [open, openId]);
1101
+ }
1102
+ //#endregion
1103
+ //#region src/flows/forget-each.ts
1104
+ function useForgetEachAttempt(target) {
1105
+ const app = require_present_flow.useApp();
1106
+ const userId = require_present_flow.useCurrentUserId();
1107
+ const store = require_present_flow.useFlowAttemptsStore();
1108
+ const audit = require_present_flow.useFrameworkAudit();
1109
+ const latest = (0, react.useRef)({
1110
+ app,
1111
+ userId,
1112
+ store,
1113
+ audit,
1114
+ target
1115
+ });
1116
+ latest.current = {
1117
+ app,
1118
+ userId,
1119
+ store,
1120
+ audit,
1121
+ target
1122
+ };
1123
+ return (0, react.useCallback)(async (id) => {
1124
+ const { app: now, userId: user, store: tab, audit: sink, target: what } = latest.current;
1125
+ if (user === null || tab === null || tab.user !== user || id === "") return false;
1126
+ const named = (0, _burojs_core.isFlowRef)(what) ? what.target : what;
1127
+ if ("appAction" in named) return false;
1128
+ const def = now.registry.get(named.resource);
1129
+ if (def === void 0) return false;
1130
+ const compiled = (0, _burojs_core.compileFlow)(def, named.action);
1131
+ const key = (0, _burojs_core.attemptKey)({
1132
+ user,
1133
+ flow: compiled.flowId,
1134
+ slot: (0, _burojs_core.attemptSlot)({ record: id })
1135
+ });
1136
+ const stored = tab.port.read(key) ?? await tab.port.ask(key);
1137
+ if (stored === null || stored.flow !== compiled.flowId) return false;
1138
+ tab.port.drop(key);
1139
+ sink?.emit(Object.freeze({
1140
+ type: "flow.submit",
1141
+ flow: compiled.flowId,
1142
+ ...compiled.resource === null ? {} : { resource: compiled.resource },
1143
+ recordId: id,
1144
+ mutation: compiled.mutationRef,
1145
+ idempotencyKey: stored.key,
1146
+ attempt: 1,
1147
+ outcome: "unknown",
1148
+ resolvedBy: "user",
1149
+ acceptedWarnings: Object.freeze([]),
1150
+ diff: Object.freeze({})
1151
+ }), { initiates: true });
1152
+ return true;
1153
+ }, []);
1154
+ }
1155
+ //#endregion
1156
+ exports.useAction = useAction;
1157
+ exports.useFlowForm = useFlowForm;
1158
+ exports.useFlowHost = useFlowHost;
1159
+ exports.useFlowRun = useFlowRun;
1160
+ exports.useForgetEachAttempt = useForgetEachAttempt;
1161
+
1162
+ //# sourceMappingURL=flows-runtime.cjs.map