@burojs/react 0.4.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.
- package/README.md +15 -6
- package/dist/flows-runtime.cjs +1162 -0
- package/dist/flows-runtime.cjs.map +1 -0
- package/dist/flows-runtime.d.cts +153 -0
- package/dist/flows-runtime.d.ts +153 -0
- package/dist/flows-runtime.js +1157 -0
- package/dist/flows-runtime.js.map +1 -0
- package/dist/index.cjs +1629 -3862
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2259 -723
- package/dist/index.d.ts +2259 -723
- package/dist/index.js +1412 -3692
- package/dist/index.js.map +1 -1
- package/dist/present-flow-BM8vaiZX.cjs +10152 -0
- package/dist/present-flow-BM8vaiZX.cjs.map +1 -0
- package/dist/present-flow-pFzbn0cZ.js +9015 -0
- package/dist/present-flow-pFzbn0cZ.js.map +1 -0
- package/dist/types-DH3beUOO.d.cts +488 -0
- package/dist/types-DH3beUOO.d.ts +488 -0
- package/package.json +24 -6
|
@@ -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
|