@nanobpm/urban 0.17.0 → 0.18.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/dist/cli.js +69 -5
- package/dist/toolkit/derivers/domain.d.ts +69 -0
- package/dist/toolkit/derivers/domain.js +243 -0
- package/dist/toolkit/derivers/messages.d.ts +36 -0
- package/dist/toolkit/derivers/messages.js +134 -0
- package/dist/toolkit/derivers/meta.d.ts +34 -0
- package/dist/toolkit/derivers/meta.js +106 -0
- package/dist/toolkit/derivers/migrations.d.ts +34 -1
- package/dist/toolkit/derivers/migrations.js +37 -8
- package/dist/toolkit/derivers/worker-io.d.ts +14 -0
- package/dist/toolkit/derivers/worker-io.js +13 -2
- package/dist/toolkit/fsio.js +31 -2
- package/dist/toolkit/gen.d.ts +28 -0
- package/dist/toolkit/gen.js +60 -11
- package/dist/toolkit/index.d.ts +18 -4
- package/dist/toolkit/index.js +12 -3
- package/dist/toolkit/models.d.ts +63 -0
- package/dist/toolkit/models.js +142 -0
- package/package.json +1 -1
- package/src/cli.ts +70 -5
- package/src/toolkit/derivers/domain.test.ts +140 -0
- package/src/toolkit/derivers/domain.ts +317 -0
- package/src/toolkit/derivers/messages.test.ts +174 -0
- package/src/toolkit/derivers/messages.ts +166 -0
- package/src/toolkit/derivers/meta.test.ts +140 -0
- package/src/toolkit/derivers/meta.ts +128 -0
- package/src/toolkit/derivers/migrations.test.ts +42 -4
- package/src/toolkit/derivers/migrations.ts +61 -9
- package/src/toolkit/derivers/worker-io.test.ts +10 -0
- package/src/toolkit/derivers/worker-io.ts +14 -2
- package/src/toolkit/fsio.test.ts +23 -0
- package/src/toolkit/fsio.ts +22 -2
- package/src/toolkit/gen.test.ts +84 -2
- package/src/toolkit/gen.ts +86 -12
- package/src/toolkit/index.ts +66 -3
- package/src/toolkit/models.test.ts +159 -0
- package/src/toolkit/models.ts +193 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// Deriver: BPMN models → `meta.ts`, the typed model-metadata accessor (`@nanobpm/meta`,
|
|
2
|
+
// ADR 0040 §5). Scans each process for `nano:meta` key/value siblings of the `nano:shapes`
|
|
3
|
+
// container and folds them into a single app-wide record. It emits the SAME `meta.ts` the
|
|
4
|
+
// console generates (a faithful port of `foldMeta`/`emitMeta` in the server's domain_types.ts),
|
|
5
|
+
// so the toolkit is a drop-in for the IDE's codegen (ADR 0053).
|
|
6
|
+
import { GENERATED_DIR } from "../artifact.js";
|
|
7
|
+
import { byModelPath } from "./worker-io.js";
|
|
8
|
+
/** Basename the console uses for the generated accessor; kept identical (drop-in). */
|
|
9
|
+
export const META_TS = "meta.ts";
|
|
10
|
+
function attr(tag, name) {
|
|
11
|
+
const m = tag.match(new RegExp(`\\b${name}\\s*=\\s*"([^"]*)"`));
|
|
12
|
+
return m ? m[1] : undefined;
|
|
13
|
+
}
|
|
14
|
+
function processId(xml) {
|
|
15
|
+
const m = xml.match(/<[\w.-]*:?process\b[^>]*>/);
|
|
16
|
+
return m ? attr(m[0], "id") : undefined;
|
|
17
|
+
}
|
|
18
|
+
// A `nano:shape` carrier (`<...:shape>…</...:shape>`, and self-closing) — meta is only
|
|
19
|
+
// recognised *outside* a shape carrier (its op children, e.g. `extend`, are a different
|
|
20
|
+
// vocabulary), so strip carriers before matching `nano:meta` (mirrors envelope_scan.rs).
|
|
21
|
+
const SHAPE_BLOCK_RE = /<([\w.-]*:)?shape\b[^>]*(?:\/>|>[\s\S]*?<\/\1?shape\s*>)/g;
|
|
22
|
+
// A `nano:meta` element (any namespace prefix; matches the Rust local-name scan).
|
|
23
|
+
const META_RE = /<([\w.-]*:)?meta\b([^>]*?)\/?>/g;
|
|
24
|
+
/** Scan one BPMN document for model-level `nano:meta` key/value entries, in document
|
|
25
|
+
* order, tagged with the enclosing process. A meta with no `key` attribute is ignored;
|
|
26
|
+
* a missing `value` folds to the empty string (matches envelope_scan.rs). */
|
|
27
|
+
export function scanModelMeta(xml) {
|
|
28
|
+
const proc = processId(xml);
|
|
29
|
+
const withoutShapes = xml.replace(SHAPE_BLOCK_RE, "");
|
|
30
|
+
const out = [];
|
|
31
|
+
let m;
|
|
32
|
+
while ((m = META_RE.exec(withoutShapes)) !== null) {
|
|
33
|
+
const key = attr(`<x ${m[2]}>`, "key");
|
|
34
|
+
if (key == null)
|
|
35
|
+
continue;
|
|
36
|
+
out.push({ process: proc, key, value: attr(`<x ${m[2]}>`, "value") ?? "" });
|
|
37
|
+
}
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
// --- Faithful port of the console's foldMeta/emitMeta (domain_types.ts) so the emitted
|
|
41
|
+
// meta.ts is byte-compatible with the IDE's own codegen. ---
|
|
42
|
+
function isIdent(name) {
|
|
43
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name);
|
|
44
|
+
}
|
|
45
|
+
/** Fold the scanned model-level metadata into a single key→value record. When two models
|
|
46
|
+
* declare the same key the last (scan order) wins. Empty keys are ignored. The accumulator
|
|
47
|
+
* is a **null-prototype** dict so user-authored keys (including `__proto__`) round-trip
|
|
48
|
+
* deterministically as own data properties. */
|
|
49
|
+
export function foldMeta(metas) {
|
|
50
|
+
const out = Object.create(null);
|
|
51
|
+
for (const m of metas) {
|
|
52
|
+
const key = (m?.key ?? "").trim();
|
|
53
|
+
if (!key)
|
|
54
|
+
continue;
|
|
55
|
+
out[key] = m.value ?? "";
|
|
56
|
+
}
|
|
57
|
+
return out;
|
|
58
|
+
}
|
|
59
|
+
/** Emit `meta.ts`: the typed model-metadata accessor (`@nanobpm/meta`, ADR 0040 §5).
|
|
60
|
+
* `AppMeta` keys the declared metadata as string-literal properties; `appMeta` is the
|
|
61
|
+
* concrete null-prototype record; the overloaded `meta()` returns the typed value for a
|
|
62
|
+
* known key and `string | undefined` for a dynamic one. Strip-safe (ADR 0036). */
|
|
63
|
+
export function emitMeta(metas) {
|
|
64
|
+
const folded = foldMeta(metas);
|
|
65
|
+
const keys = Object.keys(folded);
|
|
66
|
+
const propKey = (k) => (isIdent(k) ? k : JSON.stringify(k));
|
|
67
|
+
const header = "// AUTO-GENERATED by nanobpmn from the process model (ADR 0040 §5): the typed\n" +
|
|
68
|
+
"// model-metadata accessor. Each `nano:meta` key/value the models declare becomes\n" +
|
|
69
|
+
"// a checked `AppMeta` property, so `meta(key)` is typed and app-wide queryable\n" +
|
|
70
|
+
"// without a second store. Do not edit — regenerated from the model.\n" +
|
|
71
|
+
"// eslint-disable\n\n";
|
|
72
|
+
const iface = keys.length > 0
|
|
73
|
+
? `export interface AppMeta {\n${keys.map((k) => ` ${propKey(k)}: string;`).join("\n")}\n}\n`
|
|
74
|
+
: `export interface AppMeta {}\n`;
|
|
75
|
+
const constDecl = keys.length > 0
|
|
76
|
+
? `export const appMeta: AppMeta = (() => {\n` +
|
|
77
|
+
` const m: Record<string, string> = Object.create(null);\n` +
|
|
78
|
+
keys.map((k) => ` m[${JSON.stringify(k)}] = ${JSON.stringify(folded[k])};`).join("\n") +
|
|
79
|
+
`\n return m as AppMeta;\n})();\n`
|
|
80
|
+
: `export const appMeta: AppMeta = Object.create(null) as AppMeta;\n`;
|
|
81
|
+
const accessor = `\n/** Read a model-level metadata value. A declared key is typed; a dynamic key\n` +
|
|
82
|
+
` * returns \`string | undefined\`. */\n` +
|
|
83
|
+
`export function meta<K extends keyof AppMeta>(key: K): AppMeta[K];\n` +
|
|
84
|
+
`export function meta(key: string): string | undefined;\n` +
|
|
85
|
+
`export function meta(key: string): string | undefined {\n` +
|
|
86
|
+
` return (appMeta as Record<string, string>)[key];\n` +
|
|
87
|
+
`}\n`;
|
|
88
|
+
return `${header}/** Every model-level metadata key the App's models declare (ADR 0040 §5). */\n` +
|
|
89
|
+
iface + "\n" + constDecl + accessor;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Derive `nano-generated/meta.ts` from BPMN models. Deterministic: models are scanned in
|
|
93
|
+
* path order, then their `nano:meta` entries folded (last write wins) into the accessor.
|
|
94
|
+
*/
|
|
95
|
+
export function deriveMeta(models) {
|
|
96
|
+
const metas = [];
|
|
97
|
+
for (const model of [...models].sort(byModelPath)) {
|
|
98
|
+
metas.push(...scanModelMeta(model.xml));
|
|
99
|
+
}
|
|
100
|
+
return [{ path: `${GENERATED_DIR}/${META_TS}`, content: emitMeta(metas) }];
|
|
101
|
+
}
|
|
102
|
+
export const metaDeriver = {
|
|
103
|
+
id: "model->meta",
|
|
104
|
+
describe: "Derive meta.ts (the typed @nanobpm/meta accessor) from BPMN nano:meta entries.",
|
|
105
|
+
derive: ({ models }) => deriveMeta(models),
|
|
106
|
+
};
|
|
@@ -2,6 +2,8 @@ import type { DerivedArtifact, Deriver } from "../artifact.ts";
|
|
|
2
2
|
export interface ToolkitField {
|
|
3
3
|
type?: string;
|
|
4
4
|
optional?: boolean;
|
|
5
|
+
/** Marks a repeated field: the domain row-type projects it as `T[]` in the `DomainTypes` registry. */
|
|
6
|
+
list?: boolean;
|
|
5
7
|
}
|
|
6
8
|
export interface ToolkitType {
|
|
7
9
|
table?: string;
|
|
@@ -20,7 +22,38 @@ export interface ToolkitManifest {
|
|
|
20
22
|
/** Map a domain field type to a SQLite column type. Unknown types fall back to TEXT. */
|
|
21
23
|
export declare function sqlType(t: string | undefined): string;
|
|
22
24
|
export declare function assertSqlIdent(kind: string, name: string): string;
|
|
23
|
-
/**
|
|
25
|
+
/** Double-quote an identifier for DDL. `assertSqlIdent` already rejects non-identifier
|
|
26
|
+
* chars (the injection guard), but quoting is what makes valid-but-reserved words
|
|
27
|
+
* (`order`, `group`, `select`, …) legal SQL — no reserved-word denylist to maintain.
|
|
28
|
+
* Embedded double quotes are doubled per the SQL standard (defensive; can't occur here). */
|
|
29
|
+
export declare function quoteSqlIdent(name: string): string;
|
|
30
|
+
/** A column in a manifest type's canonical table schema. `sqlType` is the SQLite
|
|
31
|
+
* declared type (the affinity source for both the CREATE TABLE and the row types). */
|
|
32
|
+
export interface DerivedColumn {
|
|
33
|
+
name: string;
|
|
34
|
+
sqlType: string;
|
|
35
|
+
notNull: boolean;
|
|
36
|
+
primaryKey: boolean;
|
|
37
|
+
}
|
|
38
|
+
/** The canonical table schema for one manifest type. */
|
|
39
|
+
export interface DerivedTable {
|
|
40
|
+
table: string;
|
|
41
|
+
columns: DerivedColumn[];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Derive the canonical table schema for one manifest type: an implicit
|
|
45
|
+
* autoincrement `id` primary key plus one column per declared field. This is the
|
|
46
|
+
* single source of truth both the SQL migration (`createTableSql`) and the domain
|
|
47
|
+
* row-types (`domain.ts`) derive from, so the schema can never drift between them.
|
|
48
|
+
*/
|
|
49
|
+
export declare function tableSchemaForType(typeName: string, def: ToolkitType): DerivedTable;
|
|
50
|
+
/** Group manifest types by their datasource, preserving declaration order within each
|
|
51
|
+
* source. Shared by the migrations and domain derivers so both partition and order
|
|
52
|
+
* sources identically (a single source of truth for the source spine). */
|
|
53
|
+
export declare function groupTypesBySource(manifest: ToolkitManifest): Map<string, Array<[string, ToolkitType]>>;
|
|
54
|
+
/** Build the CREATE TABLE statement for one type, from its canonical schema. Table and
|
|
55
|
+
* column identifiers are quoted so valid-but-reserved names (`order`, `group`, …) emit
|
|
56
|
+
* legal SQL. */
|
|
24
57
|
export declare function createTableSql(typeName: string, def: ToolkitType): string;
|
|
25
58
|
export declare function deriveMigrations(manifest: ToolkitManifest): DerivedArtifact[];
|
|
26
59
|
export declare const migrationsDeriver: Deriver<ToolkitManifest>;
|
|
@@ -31,25 +31,40 @@ export function assertSqlIdent(kind, name) {
|
|
|
31
31
|
}
|
|
32
32
|
return name;
|
|
33
33
|
}
|
|
34
|
-
/**
|
|
35
|
-
|
|
34
|
+
/** Double-quote an identifier for DDL. `assertSqlIdent` already rejects non-identifier
|
|
35
|
+
* chars (the injection guard), but quoting is what makes valid-but-reserved words
|
|
36
|
+
* (`order`, `group`, `select`, …) legal SQL — no reserved-word denylist to maintain.
|
|
37
|
+
* Embedded double quotes are doubled per the SQL standard (defensive; can't occur here). */
|
|
38
|
+
export function quoteSqlIdent(name) {
|
|
39
|
+
return `"${name.replace(/"/g, '""')}"`;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Derive the canonical table schema for one manifest type: an implicit
|
|
43
|
+
* autoincrement `id` primary key plus one column per declared field. This is the
|
|
44
|
+
* single source of truth both the SQL migration (`createTableSql`) and the domain
|
|
45
|
+
* row-types (`domain.ts`) derive from, so the schema can never drift between them.
|
|
46
|
+
*/
|
|
47
|
+
export function tableSchemaForType(typeName, def) {
|
|
36
48
|
const table = assertSqlIdent("table name", def.table ?? typeName);
|
|
37
|
-
const
|
|
49
|
+
const columns = [
|
|
50
|
+
{ name: "id", sqlType: "INTEGER", notNull: false, primaryKey: true },
|
|
51
|
+
];
|
|
38
52
|
const fields = def.fields ?? {};
|
|
39
53
|
for (const [name, f] of Object.entries(fields)) {
|
|
40
54
|
assertSqlIdent("field name", name);
|
|
41
55
|
if (name === "id") {
|
|
42
56
|
throw new Error(`type "${typeName}": field "id" is reserved (the table's implicit primary key)`);
|
|
43
57
|
}
|
|
44
|
-
|
|
45
|
-
cols.push(` ${name} ${sqlType(f.type)}${notNull}`);
|
|
58
|
+
columns.push({ name, sqlType: sqlType(f.type), notNull: !f.optional, primaryKey: false });
|
|
46
59
|
}
|
|
47
|
-
return
|
|
60
|
+
return { table, columns };
|
|
48
61
|
}
|
|
49
|
-
|
|
62
|
+
/** Group manifest types by their datasource, preserving declaration order within each
|
|
63
|
+
* source. Shared by the migrations and domain derivers so both partition and order
|
|
64
|
+
* sources identically (a single source of truth for the source spine). */
|
|
65
|
+
export function groupTypesBySource(manifest) {
|
|
50
66
|
const types = manifest.types ?? {};
|
|
51
67
|
const defaultSource = manifest.data?.default ?? "app";
|
|
52
|
-
// Group types by their datasource.
|
|
53
68
|
const bySource = new Map();
|
|
54
69
|
for (const [name, def] of Object.entries(types)) {
|
|
55
70
|
const src = def.source ?? defaultSource;
|
|
@@ -57,6 +72,20 @@ export function deriveMigrations(manifest) {
|
|
|
57
72
|
list.push([name, def]);
|
|
58
73
|
bySource.set(src, list);
|
|
59
74
|
}
|
|
75
|
+
return bySource;
|
|
76
|
+
}
|
|
77
|
+
/** Build the CREATE TABLE statement for one type, from its canonical schema. Table and
|
|
78
|
+
* column identifiers are quoted so valid-but-reserved names (`order`, `group`, …) emit
|
|
79
|
+
* legal SQL. */
|
|
80
|
+
export function createTableSql(typeName, def) {
|
|
81
|
+
const { table, columns } = tableSchemaForType(typeName, def);
|
|
82
|
+
const cols = columns.map((c) => c.primaryKey
|
|
83
|
+
? ` ${quoteSqlIdent(c.name)} ${c.sqlType} PRIMARY KEY AUTOINCREMENT`
|
|
84
|
+
: ` ${quoteSqlIdent(c.name)} ${c.sqlType}${c.notNull ? " NOT NULL" : ""}`);
|
|
85
|
+
return `CREATE TABLE IF NOT EXISTS ${quoteSqlIdent(table)} (\n${cols.join(",\n")}\n);`;
|
|
86
|
+
}
|
|
87
|
+
export function deriveMigrations(manifest) {
|
|
88
|
+
const bySource = groupTypesBySource(manifest);
|
|
60
89
|
const artifacts = [];
|
|
61
90
|
for (const [src, list] of bySource) {
|
|
62
91
|
const header = `-- Derived from manifest \`types\` by @nanobpm/urban (urban gen). Do not edit.\n` +
|
|
@@ -4,6 +4,16 @@ export interface ModelSource {
|
|
|
4
4
|
path: string;
|
|
5
5
|
xml: string;
|
|
6
6
|
}
|
|
7
|
+
/** Deterministic comparator for ordering `ModelSource` lists by path. Returns 0 on equal
|
|
8
|
+
* paths so it satisfies the JS sort contract (a comparator that reports both `a>b` and
|
|
9
|
+
* `b>a` can reorder equal elements unpredictably) — keeping the derivers' cross-model
|
|
10
|
+
* fold order stable even if a manifest pattern yields duplicate paths. Shared by every
|
|
11
|
+
* model-scanning deriver so there is one canonical ordering. */
|
|
12
|
+
export declare function byModelPath(a: {
|
|
13
|
+
path: string;
|
|
14
|
+
}, b: {
|
|
15
|
+
path: string;
|
|
16
|
+
}): number;
|
|
7
17
|
export interface WorkerIo {
|
|
8
18
|
taskType: string;
|
|
9
19
|
elementId?: string;
|
|
@@ -19,6 +29,10 @@ export declare const WORKER_BINDINGS_DTS = "worker-io.d.ts";
|
|
|
19
29
|
export declare const DOMAIN_DTS = "domain-rows.d.ts";
|
|
20
30
|
/** Scan one BPMN document for service-task worker I/O. */
|
|
21
31
|
export declare function scanModelWorkers(xml: string): WorkerIo[];
|
|
32
|
+
/** A `DomainTypes[...]` ref for a declared type id (else undefined). The one canonical
|
|
33
|
+
* ref helper shared by the worker- and message-IO emitters (matches the console's single
|
|
34
|
+
* `typeRefFor` in domain_types.ts), so both stay identical to the host codegen. */
|
|
35
|
+
export declare function typeRefFor(id: string | undefined, declared: Set<string>): string | undefined;
|
|
22
36
|
export interface WorkerBindingDecl {
|
|
23
37
|
taskType?: string;
|
|
24
38
|
inputType?: string;
|
|
@@ -4,6 +4,14 @@
|
|
|
4
4
|
// `worker-io.d.ts` the console generates (a faithful port of `emitWorkerBindings` in the
|
|
5
5
|
// server's domain_types.ts), so the toolkit is a drop-in for the IDE's codegen (ADR 0053).
|
|
6
6
|
import { GENERATED_DIR } from "../artifact.js";
|
|
7
|
+
/** Deterministic comparator for ordering `ModelSource` lists by path. Returns 0 on equal
|
|
8
|
+
* paths so it satisfies the JS sort contract (a comparator that reports both `a>b` and
|
|
9
|
+
* `b>a` can reorder equal elements unpredictably) — keeping the derivers' cross-model
|
|
10
|
+
* fold order stable even if a manifest pattern yields duplicate paths. Shared by every
|
|
11
|
+
* model-scanning deriver so there is one canonical ordering. */
|
|
12
|
+
export function byModelPath(a, b) {
|
|
13
|
+
return a.path < b.path ? -1 : a.path > b.path ? 1 : 0;
|
|
14
|
+
}
|
|
7
15
|
/** Filename the console uses; kept identical so the artifact is a drop-in. */
|
|
8
16
|
export const WORKER_BINDINGS_DTS = "worker-io.d.ts";
|
|
9
17
|
/** The domain type declarations `worker-io.d.ts` imports from (console: domain-rows.d.ts). */
|
|
@@ -51,7 +59,10 @@ export function scanModelWorkers(xml) {
|
|
|
51
59
|
}
|
|
52
60
|
// --- Faithful port of the console's emitWorkerBindings (domain_types.ts) so the emitted
|
|
53
61
|
// worker-io.d.ts is byte-compatible with the IDE's own codegen. ---
|
|
54
|
-
|
|
62
|
+
/** A `DomainTypes[...]` ref for a declared type id (else undefined). The one canonical
|
|
63
|
+
* ref helper shared by the worker- and message-IO emitters (matches the console's single
|
|
64
|
+
* `typeRefFor` in domain_types.ts), so both stay identical to the host codegen. */
|
|
65
|
+
export function typeRefFor(id, declared) {
|
|
55
66
|
return id != null && declared.has(id) ? `DomainTypes[${JSON.stringify(id)}]` : undefined;
|
|
56
67
|
}
|
|
57
68
|
/** Emit `worker-io.d.ts` from worker bindings + the set of declared domain type ids. */
|
|
@@ -120,7 +131,7 @@ export function emitWorkerBindings(workers, declaredTypeIds) {
|
|
|
120
131
|
*/
|
|
121
132
|
export function deriveWorkerBindings(models, declaredTypeIds = []) {
|
|
122
133
|
const workers = [];
|
|
123
|
-
for (const model of [...models].sort(
|
|
134
|
+
for (const model of [...models].sort(byModelPath)) {
|
|
124
135
|
for (const w of scanModelWorkers(model.xml)) {
|
|
125
136
|
workers.push({ taskType: w.taskType, inputType: w.in, outputType: w.out });
|
|
126
137
|
}
|
package/dist/toolkit/fsio.js
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
// Node/Deno filesystem implementation of the GenIO port. Uses node:fs/promises, which both
|
|
2
2
|
// Node and Deno provide, so a single implementation serves both runtimes.
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
4
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
5
|
+
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
6
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
return path;
|
|
10
|
+
};
|
|
11
|
+
import { mkdir, readdir, readFile, rm, stat, writeFile } from "node:fs/promises";
|
|
12
|
+
import { dirname, resolve } from "node:path";
|
|
13
|
+
import { pathToFileURL } from "node:url";
|
|
5
14
|
export function createNodeGenIO() {
|
|
6
15
|
return {
|
|
7
16
|
async readText(path) {
|
|
@@ -31,5 +40,25 @@ export function createNodeGenIO() {
|
|
|
31
40
|
return false;
|
|
32
41
|
}
|
|
33
42
|
},
|
|
43
|
+
async remove(path) {
|
|
44
|
+
await rm(path, { force: true });
|
|
45
|
+
},
|
|
46
|
+
// Import an app module for code-first model derivation. `import()` of a file URL loads TS on
|
|
47
|
+
// Deno natively and on Node with type-stripping (≥22.18 default, or `--experimental-strip-types`).
|
|
48
|
+
// The cache key is the file's mtime, not a monotonic clock: an unchanged workflow reuses the
|
|
49
|
+
// cached module instance (bounding the ESM module map in long-lived processes like `urban dev`)
|
|
50
|
+
// while an edited one still reloads.
|
|
51
|
+
async importModule(path) {
|
|
52
|
+
const abs = resolve(path);
|
|
53
|
+
let version = "";
|
|
54
|
+
try {
|
|
55
|
+
version = `?v=${(await stat(abs)).mtimeMs}`;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// stat failed (e.g. a virtual path); fall back to no cache key and let import() surface it.
|
|
59
|
+
}
|
|
60
|
+
const href = `${pathToFileURL(abs).href}${version}`;
|
|
61
|
+
return import(__rewriteRelativeImportExtension(href));
|
|
62
|
+
},
|
|
34
63
|
};
|
|
35
64
|
}
|
package/dist/toolkit/gen.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { DerivedArtifact } from "./artifact.ts";
|
|
2
2
|
import { type ModelSource } from "./derivers/worker-io.ts";
|
|
3
|
+
import { type DerivedModels, type ModelError } from "./models.ts";
|
|
3
4
|
/** Minimal filesystem port. Node/Deno impls live in `fsio.ts`. */
|
|
4
5
|
export interface GenIO {
|
|
5
6
|
readText(path: string): Promise<string>;
|
|
@@ -7,19 +8,43 @@ export interface GenIO {
|
|
|
7
8
|
/** File names (not paths) in a directory; empty if it does not exist. */
|
|
8
9
|
listDir(path: string): Promise<string[]>;
|
|
9
10
|
exists(path: string): Promise<boolean>;
|
|
11
|
+
/**
|
|
12
|
+
* Import an app module and return its exports. Optional: only present when the caller runs on
|
|
13
|
+
* a runtime that can load the app's TypeScript (Node with type-stripping, or Deno). Code-first
|
|
14
|
+
* model derivation (`workflows/*.ts` → BPMN) needs it; when absent, gen still derives everything
|
|
15
|
+
* else (migrations, domain, worker I/O from authored/derived models).
|
|
16
|
+
*/
|
|
17
|
+
importModule?(path: string): Promise<Record<string, unknown>>;
|
|
18
|
+
/** Delete a file. Optional: enables the provenance-scoped stale-model sweep (skipped when absent). */
|
|
19
|
+
remove?(path: string): Promise<void>;
|
|
10
20
|
}
|
|
11
21
|
export interface GenOptions {
|
|
12
22
|
root: string;
|
|
13
23
|
io: GenIO;
|
|
14
24
|
manifestFile?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Whether to emit (write) derived `.bpmn` models. Default true. When false, code-first models
|
|
27
|
+
* are still derived *in-memory* to feed the type-contract derivers (worker I/O, meta, messages)
|
|
28
|
+
* — they are just not written to disk or swept. This is the `urban gen --no-models` path, so
|
|
29
|
+
* the console's type-contract regen never mutates `processes/*.bpmn`.
|
|
30
|
+
*/
|
|
31
|
+
emitModels?: boolean;
|
|
32
|
+
/** Derive ONLY the models (skip migrations/domain/worker-I/O). The `urban derive` path. */
|
|
33
|
+
modelsOnly?: boolean;
|
|
15
34
|
}
|
|
16
35
|
export interface GenResult {
|
|
17
36
|
artifacts: DerivedArtifact[];
|
|
18
37
|
/** Paths that differ from disk (only populated by `check`). */
|
|
19
38
|
drift: string[];
|
|
39
|
+
/** True if any code-first workflow failed to import/derive. */
|
|
40
|
+
incomplete: boolean;
|
|
41
|
+
/** Per-file model-derivation errors (empty on success). */
|
|
42
|
+
modelErrors: ModelError[];
|
|
20
43
|
}
|
|
21
44
|
declare function join(root: string, rel: string): string;
|
|
22
45
|
declare function dirOf(p: string): string;
|
|
46
|
+
/** Resolve a `dir/*.ext` (or literal) manifest pattern to file paths relative to root. */
|
|
47
|
+
export declare function expandPattern(root: string, io: GenIO, pattern: string): Promise<string[]>;
|
|
23
48
|
/** Read + resolve the app's process models from the manifest's `models.processes` patterns. */
|
|
24
49
|
export declare function readModels(root: string, io: GenIO, manifest: {
|
|
25
50
|
models?: {
|
|
@@ -33,3 +58,6 @@ export declare function runGen(opts: GenOptions & {
|
|
|
33
58
|
check?: boolean;
|
|
34
59
|
}): Promise<GenResult>;
|
|
35
60
|
export { dirOf, join as joinPath };
|
|
61
|
+
/** Derive code-first models in-memory (no writes) — backs `urban derive --stdout`, the
|
|
62
|
+
* non-mutating preview the console's read-only model viewer uses. */
|
|
63
|
+
export declare function previewModels(opts: GenOptions): Promise<DerivedModels>;
|
package/dist/toolkit/gen.js
CHANGED
|
@@ -4,7 +4,11 @@
|
|
|
4
4
|
// pure; all IO is confined here behind a tiny FS port so the same code runs on Node and Deno.
|
|
5
5
|
import { sortArtifacts } from "./artifact.js";
|
|
6
6
|
import { deriveMigrations } from "./derivers/migrations.js";
|
|
7
|
+
import { deriveDomain } from "./derivers/domain.js";
|
|
7
8
|
import { deriveWorkerBindings } from "./derivers/worker-io.js";
|
|
9
|
+
import { deriveMeta } from "./derivers/meta.js";
|
|
10
|
+
import { deriveMessageBindings } from "./derivers/messages.js";
|
|
11
|
+
import { deriveModels, MODEL_PROVENANCE } from "./models.js";
|
|
8
12
|
function join(root, rel) {
|
|
9
13
|
// Trim either separator so callers may pass Windows-style paths; GenIO
|
|
10
14
|
// implementations accept forward slashes on all platforms.
|
|
@@ -15,7 +19,7 @@ function dirOf(p) {
|
|
|
15
19
|
return i <= 0 ? "." : p.slice(0, i);
|
|
16
20
|
}
|
|
17
21
|
/** Resolve a `dir/*.ext` (or literal) manifest pattern to file paths relative to root. */
|
|
18
|
-
async function expandPattern(root, io, pattern) {
|
|
22
|
+
export async function expandPattern(root, io, pattern) {
|
|
19
23
|
const star = pattern.indexOf("*");
|
|
20
24
|
if (star === -1) {
|
|
21
25
|
return (await io.exists(join(root, pattern))) ? [pattern] : [];
|
|
@@ -43,26 +47,47 @@ export async function readModels(root, io, manifest) {
|
|
|
43
47
|
}
|
|
44
48
|
/** Collect all artifacts a run would produce, without touching disk beyond reads. */
|
|
45
49
|
export async function collectArtifacts(opts) {
|
|
50
|
+
return (await collectAll(opts)).artifacts;
|
|
51
|
+
}
|
|
52
|
+
/** Internal: collect artifacts AND the model-derivation details (needed by `runGen` for the
|
|
53
|
+
* incomplete signal + the provenance-scoped stale sweep). */
|
|
54
|
+
async function collectAll(opts) {
|
|
46
55
|
const { root, io } = opts;
|
|
56
|
+
const emitModels = opts.emitModels ?? true;
|
|
47
57
|
const manifestPath = join(root, opts.manifestFile ?? "nano.app.json");
|
|
48
58
|
const manifest = JSON.parse(await io.readText(manifestPath));
|
|
49
59
|
const artifacts = [];
|
|
50
|
-
// 1. types → migrations
|
|
51
|
-
if (manifest.types && Object.keys(manifest.types).length > 0) {
|
|
60
|
+
// 1. types → migrations + domain row types (DomainTables spine + DomainTypes registry)
|
|
61
|
+
if (!opts.modelsOnly && manifest.types && Object.keys(manifest.types).length > 0) {
|
|
52
62
|
artifacts.push(...deriveMigrations(manifest));
|
|
63
|
+
artifacts.push(...deriveDomain(manifest));
|
|
53
64
|
}
|
|
54
|
-
// 2. models →
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
65
|
+
// 2. code-first models → derive BPMN from `workflows/*.ts` (executes the app's TS). The derived
|
|
66
|
+
// `.bpmn` are emitted only when `emitModels` (default), but always fed to the type-contract
|
|
67
|
+
// derivers below so worker I/O works for a code-first app even on a `--no-models` run.
|
|
68
|
+
const derived = await deriveModels(root, io, manifest);
|
|
69
|
+
if (emitModels)
|
|
70
|
+
artifacts.push(...derived.artifacts);
|
|
71
|
+
// 3. models (authored on-disk + derived) → worker I/O index + meta accessor + message map.
|
|
72
|
+
// Drop any on-disk model whose path a derived model already covers, to avoid double-counting.
|
|
73
|
+
if (!opts.modelsOnly) {
|
|
74
|
+
const derivedPaths = new Set(derived.artifacts.map((a) => a.path));
|
|
75
|
+
const diskModels = (await readModels(root, io, manifest)).filter((m) => !derivedPaths.has(m.path));
|
|
76
|
+
const models = [...diskModels, ...derived.models];
|
|
77
|
+
if (models.length > 0) {
|
|
78
|
+
const declaredTypeIds = Object.keys(manifest.types ?? {});
|
|
79
|
+
artifacts.push(...deriveWorkerBindings(models, declaredTypeIds));
|
|
80
|
+
artifacts.push(...deriveMeta(models));
|
|
81
|
+
artifacts.push(...deriveMessageBindings(models, declaredTypeIds));
|
|
82
|
+
}
|
|
59
83
|
}
|
|
60
|
-
return sortArtifacts(artifacts);
|
|
84
|
+
return { artifacts: sortArtifacts(artifacts), derived };
|
|
61
85
|
}
|
|
62
86
|
/** Run the derivers and write artifacts (or, with `check`, report drift without writing). */
|
|
63
87
|
export async function runGen(opts) {
|
|
64
|
-
const artifacts = await
|
|
88
|
+
const { artifacts, derived } = await collectAll(opts);
|
|
65
89
|
const { root, io } = opts;
|
|
90
|
+
const emitModels = opts.emitModels ?? true;
|
|
66
91
|
const drift = [];
|
|
67
92
|
for (const a of artifacts) {
|
|
68
93
|
const abs = join(root, a.path);
|
|
@@ -75,6 +100,30 @@ export async function runGen(opts) {
|
|
|
75
100
|
await io.writeText(abs, a.content);
|
|
76
101
|
}
|
|
77
102
|
}
|
|
78
|
-
|
|
103
|
+
// Provenance-scoped stale sweep: delete derived `.bpmn` for flows that no longer exist. Only
|
|
104
|
+
// in write mode, only when we emitted models, only on a complete derivation (never delete when
|
|
105
|
+
// a workflow failed to load), and only files bearing our marker (authored models are untouched).
|
|
106
|
+
if (!opts.check && emitModels && derived.attempted && !derived.incomplete && io.remove) {
|
|
107
|
+
const keep = new Set(derived.artifacts.map((a) => a.path));
|
|
108
|
+
for (const name of await io.listDir(join(root, derived.outDir))) {
|
|
109
|
+
if (!name.endsWith(".bpmn"))
|
|
110
|
+
continue;
|
|
111
|
+
const rel = `${derived.outDir}/${name}`;
|
|
112
|
+
if (keep.has(rel))
|
|
113
|
+
continue;
|
|
114
|
+
const content = await io.readText(join(root, rel));
|
|
115
|
+
if (content.includes(MODEL_PROVENANCE))
|
|
116
|
+
await io.remove(join(root, rel));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return { artifacts, drift, incomplete: derived.incomplete, modelErrors: derived.errors };
|
|
79
120
|
}
|
|
80
121
|
export { dirOf, join as joinPath };
|
|
122
|
+
/** Derive code-first models in-memory (no writes) — backs `urban derive --stdout`, the
|
|
123
|
+
* non-mutating preview the console's read-only model viewer uses. */
|
|
124
|
+
export async function previewModels(opts) {
|
|
125
|
+
const { root, io } = opts;
|
|
126
|
+
const manifestPath = join(root, opts.manifestFile ?? "nano.app.json");
|
|
127
|
+
const manifest = JSON.parse(await io.readText(manifestPath));
|
|
128
|
+
return deriveModels(root, io, manifest);
|
|
129
|
+
}
|
package/dist/toolkit/index.d.ts
CHANGED
|
@@ -1,16 +1,30 @@
|
|
|
1
1
|
export type { DerivedArtifact, Deriver } from "./artifact.ts";
|
|
2
2
|
export { GENERATED_DIR, sortArtifacts } from "./artifact.ts";
|
|
3
|
-
export { deriveMigrations, migrationsDeriver, sqlType, createTableSql, } from "./derivers/migrations.ts";
|
|
4
|
-
export type { ToolkitManifest, ToolkitType, ToolkitField } from "./derivers/migrations.ts";
|
|
3
|
+
export { deriveMigrations, migrationsDeriver, sqlType, createTableSql, tableSchemaForType, groupTypesBySource, } from "./derivers/migrations.ts";
|
|
4
|
+
export type { ToolkitManifest, ToolkitType, ToolkitField, DerivedColumn, DerivedTable, } from "./derivers/migrations.ts";
|
|
5
|
+
export { deriveDomain, emitDomain, emitDomainModel, emitDomainDts, emitDomainDtsForSources, emitDomainTypeRegistry, domainDeriver, sourcesFromManifest, registryFromManifest, sqliteAffinityToTs, interfaceName, } from "./derivers/domain.ts";
|
|
6
|
+
export type { SourceSchema, DomainFieldDef, DomainTypeDef, DomainTypeRegistry, } from "./derivers/domain.ts";
|
|
5
7
|
export { deriveWorkerBindings, emitWorkerBindings, workerIoDeriver, scanModelWorkers, WORKER_BINDINGS_DTS, DOMAIN_DTS, } from "./derivers/worker-io.ts";
|
|
6
8
|
export type { ModelSource, WorkerIo, WorkerBindingDecl } from "./derivers/worker-io.ts";
|
|
7
|
-
export
|
|
9
|
+
export { byModelPath, typeRefFor } from "./derivers/worker-io.ts";
|
|
10
|
+
export { deriveMeta, emitMeta, foldMeta, metaDeriver, scanModelMeta, META_TS, } from "./derivers/meta.ts";
|
|
11
|
+
export type { MetaDecl } from "./derivers/meta.ts";
|
|
12
|
+
export { deriveMessageBindings, emitMessageBindings, messagesDeriver, scanModelMessages, MESSAGE_BINDINGS_DTS, } from "./derivers/messages.ts";
|
|
13
|
+
export type { MessageIo, MessageBindingDecl } from "./derivers/messages.ts";
|
|
14
|
+
export declare const DERIVERS: readonly [import("./artifact.ts").Deriver<import("./derivers/migrations.ts").ToolkitManifest>, import("./artifact.ts").Deriver<import("./derivers/migrations.ts").ToolkitManifest>, import("./artifact.ts").Deriver<{
|
|
15
|
+
models: import("./derivers/worker-io.ts").ModelSource[];
|
|
16
|
+
declaredTypeIds: Iterable<string>;
|
|
17
|
+
}>, import("./artifact.ts").Deriver<{
|
|
18
|
+
models: import("./derivers/worker-io.ts").ModelSource[];
|
|
19
|
+
}>, import("./artifact.ts").Deriver<{
|
|
8
20
|
models: import("./derivers/worker-io.ts").ModelSource[];
|
|
9
21
|
declaredTypeIds: Iterable<string>;
|
|
10
22
|
}>];
|
|
11
|
-
export { collectArtifacts, runGen, joinPath, readModels } from "./gen.ts";
|
|
23
|
+
export { collectArtifacts, runGen, joinPath, readModels, expandPattern, previewModels } from "./gen.ts";
|
|
12
24
|
export type { GenIO, GenOptions, GenResult } from "./gen.ts";
|
|
13
25
|
export { createNodeGenIO } from "./fsio.ts";
|
|
26
|
+
export { deriveModels, isWorkflow, bpmnFilename, processesOutDir, DEFAULT_WORKFLOW_PATTERNS, PROCESSES_DIR, MODEL_PROVENANCE, } from "./models.ts";
|
|
27
|
+
export type { DerivedModels, DerivedModelInfo, ModelError, ModelsManifest } from "./models.ts";
|
|
14
28
|
export { planWorkerScaffold, renderWorkerStub, slugifyTaskType, } from "./scaffold/workers.ts";
|
|
15
29
|
export type { ScaffoldWorker, SkippedWorker, SkipReason, StubManifestEntry, WorkerScaffoldPlan, WorkerStubPlan, } from "./scaffold/workers.ts";
|
|
16
30
|
export { scaffoldWorkers } from "./scaffold.ts";
|
package/dist/toolkit/index.js
CHANGED
|
@@ -4,15 +4,24 @@
|
|
|
4
4
|
// (ADR 0054) — the toolkit derives migrations and worker I/O from the model.
|
|
5
5
|
export { GENERATED_DIR, sortArtifacts } from "./artifact.js";
|
|
6
6
|
// Derivers
|
|
7
|
-
export { deriveMigrations, migrationsDeriver, sqlType, createTableSql, } from "./derivers/migrations.js";
|
|
7
|
+
export { deriveMigrations, migrationsDeriver, sqlType, createTableSql, tableSchemaForType, groupTypesBySource, } from "./derivers/migrations.js";
|
|
8
|
+
export { deriveDomain, emitDomain, emitDomainModel, emitDomainDts, emitDomainDtsForSources, emitDomainTypeRegistry, domainDeriver, sourcesFromManifest, registryFromManifest, sqliteAffinityToTs, interfaceName, } from "./derivers/domain.js";
|
|
8
9
|
export { deriveWorkerBindings, emitWorkerBindings, workerIoDeriver, scanModelWorkers, WORKER_BINDINGS_DTS, DOMAIN_DTS, } from "./derivers/worker-io.js";
|
|
10
|
+
export { byModelPath, typeRefFor } from "./derivers/worker-io.js";
|
|
11
|
+
export { deriveMeta, emitMeta, foldMeta, metaDeriver, scanModelMeta, META_TS, } from "./derivers/meta.js";
|
|
12
|
+
export { deriveMessageBindings, emitMessageBindings, messagesDeriver, scanModelMessages, MESSAGE_BINDINGS_DTS, } from "./derivers/messages.js";
|
|
9
13
|
// The registry of all derivers (for discovery / IDE migration).
|
|
10
14
|
import { migrationsDeriver } from "./derivers/migrations.js";
|
|
15
|
+
import { domainDeriver } from "./derivers/domain.js";
|
|
11
16
|
import { workerIoDeriver } from "./derivers/worker-io.js";
|
|
12
|
-
|
|
17
|
+
import { metaDeriver } from "./derivers/meta.js";
|
|
18
|
+
import { messagesDeriver } from "./derivers/messages.js";
|
|
19
|
+
export const DERIVERS = [migrationsDeriver, domainDeriver, workerIoDeriver, metaDeriver, messagesDeriver];
|
|
13
20
|
// Gen orchestrator + IO
|
|
14
|
-
export { collectArtifacts, runGen, joinPath, readModels } from "./gen.js";
|
|
21
|
+
export { collectArtifacts, runGen, joinPath, readModels, expandPattern, previewModels } from "./gen.js";
|
|
15
22
|
export { createNodeGenIO } from "./fsio.js";
|
|
23
|
+
// Code-first model derivation (workflows/*.ts → BPMN)
|
|
24
|
+
export { deriveModels, isWorkflow, bpmnFilename, processesOutDir, DEFAULT_WORKFLOW_PATTERNS, PROCESSES_DIR, MODEL_PROVENANCE, } from "./models.js";
|
|
16
25
|
// Worker-stub scaffolder (ADR 0056): write-once handler stubs from the model.
|
|
17
26
|
export { planWorkerScaffold, renderWorkerStub, slugifyTaskType, } from "./scaffold/workers.js";
|
|
18
27
|
export { scaffoldWorkers } from "./scaffold.js";
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { Workflow } from "@nanobpm/workflow";
|
|
2
|
+
import type { DerivedArtifact } from "./artifact.ts";
|
|
3
|
+
import type { GenIO } from "./gen.ts";
|
|
4
|
+
import type { ModelSource } from "./derivers/worker-io.ts";
|
|
5
|
+
/** Default source glob for code-first flows (matches the code-first scaffold layout). */
|
|
6
|
+
export declare const DEFAULT_WORKFLOW_PATTERNS: string[];
|
|
7
|
+
/** Default output dir for derived models (matches the model-first scaffold, so a derived
|
|
8
|
+
* model is ejectable to model-first and picked up by `models.processes: ["processes/*.bpmn"]`). */
|
|
9
|
+
export declare const PROCESSES_DIR = "processes";
|
|
10
|
+
/** Marker stamped into every derived `.bpmn` (an XML comment) so re-gen overwrites and the
|
|
11
|
+
* stale sweep deletes ONLY derived output — never an authored, human-owned model. */
|
|
12
|
+
export declare const MODEL_PROVENANCE = "Generated by @nanobpm/urban from workflows/*.ts \u2014 do not edit; edit the flow and re-run `urban gen`.";
|
|
13
|
+
/** Minimal shape a code-first manifest declares for model derivation. */
|
|
14
|
+
export interface ModelsManifest {
|
|
15
|
+
models?: {
|
|
16
|
+
processes?: string[];
|
|
17
|
+
workflows?: string[];
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/** A `defineFlow` export: has a string `id` and an `imperative`/`declarative` `kind`. */
|
|
21
|
+
export declare function isWorkflow(v: unknown): v is Workflow;
|
|
22
|
+
/** Sanitize a flow id into a safe `.bpmn` filename (mirrors the host generator). */
|
|
23
|
+
export declare function bpmnFilename(id: string): string;
|
|
24
|
+
/** The directory derived models are written to: the dir of the first `models.processes`
|
|
25
|
+
* glob if the manifest declares one, else the `processes/` default. */
|
|
26
|
+
export declare function processesOutDir(manifest: ModelsManifest): string;
|
|
27
|
+
export interface ModelError {
|
|
28
|
+
path: string;
|
|
29
|
+
message: string;
|
|
30
|
+
}
|
|
31
|
+
export interface DerivedModels {
|
|
32
|
+
/** `processes/<id>.bpmn` artifacts (path + provenance-stamped content). */
|
|
33
|
+
artifacts: DerivedArtifact[];
|
|
34
|
+
/** The same models, in-memory, for feeding the type-contract derivers (worker I/O, meta, messages). */
|
|
35
|
+
models: ModelSource[];
|
|
36
|
+
/** Derived flow ids (used for the provenance-scoped stale sweep). */
|
|
37
|
+
ids: string[];
|
|
38
|
+
/** Per-flow `{ id, kind, xml }` for the non-writing preview (`urban derive --stdout`). */
|
|
39
|
+
list: DerivedModelInfo[];
|
|
40
|
+
/** The output directory the artifacts were written into. */
|
|
41
|
+
outDir: string;
|
|
42
|
+
/** True once we attempted derivation (loader present AND at least one workflow file matched). */
|
|
43
|
+
attempted: boolean;
|
|
44
|
+
/** True if any workflow module failed to import/derive — suppresses the destructive sweep. */
|
|
45
|
+
incomplete: boolean;
|
|
46
|
+
/** Per-file derivation errors (empty on full success). */
|
|
47
|
+
errors: ModelError[];
|
|
48
|
+
}
|
|
49
|
+
export interface DerivedModelInfo {
|
|
50
|
+
id: string;
|
|
51
|
+
kind: Workflow["kind"];
|
|
52
|
+
xml: string;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Derive executable BPMN models from an app's code-first `workflows/*.ts`.
|
|
56
|
+
*
|
|
57
|
+
* Returns everything in-memory (artifacts + `ModelSource`s + ids). The caller decides whether
|
|
58
|
+
* to write the `.bpmn` to disk (`urban gen` / `urban derive`) or keep them in memory to feed
|
|
59
|
+
* the type-contract derivers only (`urban gen --no-models`) or preview them (`urban derive
|
|
60
|
+
* --stdout`). Never throws on a bad workflow: a failing module is recorded in `errors`,
|
|
61
|
+
* `incomplete` is set, and the sweep is suppressed so a partial failure can't delete good models.
|
|
62
|
+
*/
|
|
63
|
+
export declare function deriveModels(root: string, io: GenIO, manifest: ModelsManifest): Promise<DerivedModels>;
|