@ifc-lite/flow 0.2.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/LICENSE +373 -0
- package/README.md +63 -0
- package/dist/availability.d.ts +28 -0
- package/dist/availability.d.ts.map +1 -0
- package/dist/availability.js +37 -0
- package/dist/availability.js.map +1 -0
- package/dist/digest.d.ts +14 -0
- package/dist/digest.d.ts.map +1 -0
- package/dist/digest.js +73 -0
- package/dist/digest.js.map +1 -0
- package/dist/document.d.ts +73 -0
- package/dist/document.d.ts.map +1 -0
- package/dist/document.js +158 -0
- package/dist/document.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/introspect.d.ts +61 -0
- package/dist/introspect.d.ts.map +1 -0
- package/dist/introspect.js +51 -0
- package/dist/introspect.js.map +1 -0
- package/dist/lift.d.ts +58 -0
- package/dist/lift.d.ts.map +1 -0
- package/dist/lift.js +213 -0
- package/dist/lift.js.map +1 -0
- package/dist/orphans.d.ts +32 -0
- package/dist/orphans.d.ts.map +1 -0
- package/dist/orphans.js +73 -0
- package/dist/orphans.js.map +1 -0
- package/dist/prepare.d.ts +13 -0
- package/dist/prepare.d.ts.map +1 -0
- package/dist/prepare.js +40 -0
- package/dist/prepare.js.map +1 -0
- package/dist/registry.d.ts +99 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +44 -0
- package/dist/registry.js.map +1 -0
- package/dist/scheduler.d.ts +91 -0
- package/dist/scheduler.d.ts.map +1 -0
- package/dist/scheduler.js +341 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/table.d.ts +64 -0
- package/dist/table.d.ts.map +1 -0
- package/dist/table.js +132 -0
- package/dist/table.js.map +1 -0
- package/dist/tracking.d.ts +86 -0
- package/dist/tracking.d.ts.map +1 -0
- package/dist/tracking.js +136 -0
- package/dist/tracking.js.map +1 -0
- package/dist/values.d.ts +102 -0
- package/dist/values.d.ts.map +1 -0
- package/dist/values.js +64 -0
- package/dist/values.js.map +1 -0
- package/dist/wiring.d.ts +20 -0
- package/dist/wiring.d.ts.map +1 -0
- package/dist/wiring.js +56 -0
- package/dist/wiring.js.map +1 -0
- package/package.json +53 -0
package/dist/table.js
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
export const COLUMN_TYPES = [
|
|
5
|
+
'string', 'real', 'integer', 'boolean', 'logical', 'label', 'identifier', 'text', 'enum', 'reference', 'list',
|
|
6
|
+
];
|
|
7
|
+
/** Structural validation of an untrusted table value. */
|
|
8
|
+
export function validateTable(value, path = 'table') {
|
|
9
|
+
const problems = [];
|
|
10
|
+
if (!value || typeof value !== 'object')
|
|
11
|
+
return [{ path, message: 'must be an object' }];
|
|
12
|
+
const t = value;
|
|
13
|
+
if (!Array.isArray(t.columns))
|
|
14
|
+
problems.push({ path: `${path}.columns`, message: 'must be an array' });
|
|
15
|
+
if (!Array.isArray(t.rows))
|
|
16
|
+
problems.push({ path: `${path}.rows`, message: 'must be an array' });
|
|
17
|
+
if (typeof t.key !== 'string' || t.key.length === 0)
|
|
18
|
+
problems.push({ path: `${path}.key`, message: 'must name the key column' });
|
|
19
|
+
if (problems.length > 0)
|
|
20
|
+
return problems;
|
|
21
|
+
const names = new Set();
|
|
22
|
+
t.columns.forEach((c, i) => {
|
|
23
|
+
const col = c;
|
|
24
|
+
const p = `${path}.columns[${i}]`;
|
|
25
|
+
if (typeof col.name !== 'string' || col.name.length === 0)
|
|
26
|
+
problems.push({ path: `${p}.name`, message: 'must be a non-empty string' });
|
|
27
|
+
else if (names.has(col.name))
|
|
28
|
+
problems.push({ path: `${p}.name`, message: `duplicate column "${col.name}"` });
|
|
29
|
+
else
|
|
30
|
+
names.add(col.name);
|
|
31
|
+
if (!COLUMN_TYPES.includes(col.type))
|
|
32
|
+
problems.push({ path: `${p}.type`, message: `unknown column type "${String(col.type)}"` });
|
|
33
|
+
if (col.unit !== undefined && typeof col.unit !== 'string')
|
|
34
|
+
problems.push({ path: `${p}.unit`, message: 'must be a string' });
|
|
35
|
+
if (col.binding !== undefined) {
|
|
36
|
+
const b = col.binding;
|
|
37
|
+
if (!b || typeof b.pset !== 'string' || typeof b.prop !== 'string')
|
|
38
|
+
problems.push({ path: `${p}.binding`, message: 'must be { pset, prop }' });
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
if (typeof t.key === 'string' && !names.has(t.key))
|
|
42
|
+
problems.push({ path: `${path}.key`, message: `key column "${t.key}" is not a column` });
|
|
43
|
+
t.rows.forEach((r, i) => {
|
|
44
|
+
if (!r || typeof r !== 'object' || Array.isArray(r)) {
|
|
45
|
+
problems.push({ path: `${path}.rows[${i}]`, message: 'must be an object' });
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
for (const [k, v] of Object.entries(r)) {
|
|
49
|
+
if (!names.has(k))
|
|
50
|
+
problems.push({ path: `${path}.rows[${i}].${k}`, message: 'not a declared column' });
|
|
51
|
+
else if (v !== null && typeof v !== 'string' && typeof v !== 'number' && typeof v !== 'boolean') {
|
|
52
|
+
problems.push({ path: `${path}.rows[${i}].${k}`, message: 'cell must be string, number, boolean or null' });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
return problems;
|
|
57
|
+
}
|
|
58
|
+
export function column(table, name) {
|
|
59
|
+
return table.columns.find((c) => c.name === name);
|
|
60
|
+
}
|
|
61
|
+
/** The row key as a group key; `null`/missing keys are reported, never coerced. */
|
|
62
|
+
export function rowKey(table, row) {
|
|
63
|
+
const v = row[table.key];
|
|
64
|
+
if (v === null || v === undefined || v === '')
|
|
65
|
+
return null;
|
|
66
|
+
return String(v);
|
|
67
|
+
}
|
|
68
|
+
/** Split rows into branches by a column's value (defaults to the key column). */
|
|
69
|
+
export function groupRows(table, by = table.key) {
|
|
70
|
+
const branches = new Map();
|
|
71
|
+
const unkeyed = [];
|
|
72
|
+
for (const row of table.rows) {
|
|
73
|
+
const v = row[by];
|
|
74
|
+
if (v === null || v === undefined || v === '') {
|
|
75
|
+
unkeyed.push(row);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const k = String(v);
|
|
79
|
+
const branch = branches.get(k);
|
|
80
|
+
if (branch)
|
|
81
|
+
branch.push(row);
|
|
82
|
+
else
|
|
83
|
+
branches.set(k, [row]);
|
|
84
|
+
}
|
|
85
|
+
return { branches, unkeyed };
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Widen a long-format table into one row per `rowKey` with a column per
|
|
89
|
+
* distinct `colKey` value. The pivoted column's type is taken from the
|
|
90
|
+
* `valueCol` column; a duplicate (rowKey, colKey) pair keeps the last value
|
|
91
|
+
* and is reported, and a `colKey` that names the key column is refused
|
|
92
|
+
* (`collisions`) rather than overwriting the row's identity.
|
|
93
|
+
*/
|
|
94
|
+
export function pivot(table, rowKeyCol, colKeyCol, valueCol) {
|
|
95
|
+
const valueColumn = column(table, valueCol);
|
|
96
|
+
const rows = new Map();
|
|
97
|
+
const colNames = new Set();
|
|
98
|
+
let duplicates = 0;
|
|
99
|
+
let collisions = 0;
|
|
100
|
+
for (const row of table.rows) {
|
|
101
|
+
const rk = row[rowKeyCol];
|
|
102
|
+
const ck = row[colKeyCol];
|
|
103
|
+
if (rk === null || rk === undefined || rk === '' || ck === null || ck === undefined || ck === '')
|
|
104
|
+
continue;
|
|
105
|
+
const rkey = String(rk);
|
|
106
|
+
const cname = String(ck);
|
|
107
|
+
// A pivoted column named like the key column (a long-format row whose
|
|
108
|
+
// `Prop` is literally `GlobalId`) would overwrite the row's identity with
|
|
109
|
+
// its value. Two distinct things, never merged: the row is counted and
|
|
110
|
+
// skipped, not silently folded into the key.
|
|
111
|
+
if (cname === rowKeyCol) {
|
|
112
|
+
collisions += 1;
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
colNames.add(cname);
|
|
116
|
+
let target = rows.get(rkey);
|
|
117
|
+
if (!target) {
|
|
118
|
+
target = { [rowKeyCol]: rk };
|
|
119
|
+
rows.set(rkey, target);
|
|
120
|
+
}
|
|
121
|
+
if (cname in target)
|
|
122
|
+
duplicates += 1;
|
|
123
|
+
target[cname] = row[valueCol] ?? null;
|
|
124
|
+
}
|
|
125
|
+
const keyColumn = column(table, rowKeyCol) ?? { name: rowKeyCol, type: 'identifier' };
|
|
126
|
+
const columns = [
|
|
127
|
+
keyColumn,
|
|
128
|
+
...[...colNames].map((name) => ({ name, type: valueColumn?.type ?? 'string', unit: valueColumn?.unit })),
|
|
129
|
+
];
|
|
130
|
+
return { table: { columns, rows: [...rows.values()], key: rowKeyCol }, duplicates, collisions };
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=table.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"table.js","sourceRoot":"","sources":["../src/table.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { TrackingMode } from './document.js';
|
|
2
|
+
import type { GroupKey } from './values.js';
|
|
3
|
+
export interface TrackedEntry {
|
|
4
|
+
readonly globalId: string;
|
|
5
|
+
/** Digest of the inputs that produced this element. */
|
|
6
|
+
readonly digest: string;
|
|
7
|
+
}
|
|
8
|
+
export interface TrackedSet {
|
|
9
|
+
readonly trackingKey: string;
|
|
10
|
+
/** Bumped on every `replace` run so fresh GlobalIds stay deterministic. */
|
|
11
|
+
readonly generation: number;
|
|
12
|
+
readonly entries: Readonly<Record<GroupKey, TrackedEntry>>;
|
|
13
|
+
/**
|
|
14
|
+
* Type of the node that made the set. Recorded so that a set whose node
|
|
15
|
+
* was deleted from the graph can still be removed from the model by that
|
|
16
|
+
* type's `remove` — without it the elements outlive the node that made
|
|
17
|
+
* them, the orphan this whole module exists to prevent.
|
|
18
|
+
*/
|
|
19
|
+
readonly nodeType?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare const TRACKING_SIDECAR_VERSION = 1;
|
|
22
|
+
/** Shape check for a set read from storage: `load()` promises a `TrackedSet`, not whatever a hand-edited sidecar holds. */
|
|
23
|
+
export declare function isTrackedSet(value: unknown): value is TrackedSet;
|
|
24
|
+
/** The `sets` record of a sidecar, or undefined when any set is malformed. */
|
|
25
|
+
export declare function trackedSetsFrom(sets: unknown): Record<string, TrackedSet> | undefined;
|
|
26
|
+
/** `<graph>.tracking.json` */
|
|
27
|
+
export interface TrackingSidecar {
|
|
28
|
+
readonly version: number;
|
|
29
|
+
/** Layer stack id or file hash the sets were made against. */
|
|
30
|
+
readonly pinnedTo: string;
|
|
31
|
+
readonly sets: Readonly<Record<string, TrackedSet>>;
|
|
32
|
+
}
|
|
33
|
+
/** Where a run reads and writes tracked sets (a sidecar file, browser storage, memory). */
|
|
34
|
+
export interface TrackingStore {
|
|
35
|
+
load(trackingKey: string): TrackedSet | undefined;
|
|
36
|
+
save(set: TrackedSet): void;
|
|
37
|
+
/** Every tracking key the store holds, so a run can find sets no node claims any more. */
|
|
38
|
+
keys(): readonly string[];
|
|
39
|
+
delete(trackingKey: string): void;
|
|
40
|
+
}
|
|
41
|
+
/** In-memory store: tests, and hosts that have not chosen a sidecar yet. */
|
|
42
|
+
export declare class MemoryTrackingStore implements TrackingStore {
|
|
43
|
+
private readonly sets;
|
|
44
|
+
load(trackingKey: string): TrackedSet | undefined;
|
|
45
|
+
save(set: TrackedSet): void;
|
|
46
|
+
keys(): readonly string[];
|
|
47
|
+
delete(trackingKey: string): void;
|
|
48
|
+
}
|
|
49
|
+
export interface DesiredLane {
|
|
50
|
+
readonly laneKey: GroupKey;
|
|
51
|
+
readonly digest: string;
|
|
52
|
+
}
|
|
53
|
+
export interface TrackingPlan {
|
|
54
|
+
readonly mode: TrackingMode;
|
|
55
|
+
readonly create: readonly {
|
|
56
|
+
laneKey: GroupKey;
|
|
57
|
+
globalId: string;
|
|
58
|
+
}[];
|
|
59
|
+
readonly update: readonly {
|
|
60
|
+
laneKey: GroupKey;
|
|
61
|
+
globalId: string;
|
|
62
|
+
}[];
|
|
63
|
+
readonly keep: readonly {
|
|
64
|
+
laneKey: GroupKey;
|
|
65
|
+
globalId: string;
|
|
66
|
+
}[];
|
|
67
|
+
readonly remove: readonly {
|
|
68
|
+
laneKey: GroupKey;
|
|
69
|
+
globalId: string;
|
|
70
|
+
}[];
|
|
71
|
+
/** The set to persist after the plan is applied. */
|
|
72
|
+
readonly next: TrackedSet;
|
|
73
|
+
/** Lane keys that appeared more than once: only the first is tracked. */
|
|
74
|
+
readonly duplicateLanes: readonly GroupKey[];
|
|
75
|
+
}
|
|
76
|
+
export declare function emptyTrackedSet(trackingKey: string): TrackedSet;
|
|
77
|
+
export declare function planTracking(previous: TrackedSet, desired: readonly DesiredLane[], mode: TrackingMode): TrackingPlan;
|
|
78
|
+
export declare class TrackingPinMismatch extends Error {
|
|
79
|
+
readonly expected: string;
|
|
80
|
+
readonly actual: string;
|
|
81
|
+
constructor(expected: string, actual: string);
|
|
82
|
+
}
|
|
83
|
+
/** Read one node's set from a sidecar; refuses a sidecar pinned to another model state. */
|
|
84
|
+
export declare function trackedSetFrom(sidecar: TrackingSidecar | undefined, pinnedTo: string, trackingKey: string): TrackedSet;
|
|
85
|
+
export declare function withTrackedSet(sidecar: TrackingSidecar | undefined, pinnedTo: string, set: TrackedSet): TrackingSidecar;
|
|
86
|
+
//# sourceMappingURL=tracking.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracking.d.ts","sourceRoot":"","sources":["../src/tracking.ts"],"names":[],"mappings":"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"}
|
package/dist/tracking.js
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
/**
|
|
5
|
+
* Element tracking: how a write node's output survives re-runs.
|
|
6
|
+
*
|
|
7
|
+
* Each write node owns a *tracked set*: the lane keys it produced last time
|
|
8
|
+
* and the GlobalId + input digest of each. A re-run plans, per lane:
|
|
9
|
+
*
|
|
10
|
+
* create — lane is new (GlobalId = trackingGuid(trackingKey, laneKey))
|
|
11
|
+
* update — lane exists, digest changed
|
|
12
|
+
* keep — lane exists, digest equal (nothing to write)
|
|
13
|
+
* remove — lane vanished (tombstone; the orphan Dynamo leaves behind)
|
|
14
|
+
*
|
|
15
|
+
* This is Rhino.Inside.Revit's "remember what each output added" made
|
|
16
|
+
* explicit, keyed by the user-visible `trackingKey` instead of the node's
|
|
17
|
+
* position in a file. Sets are pinned to the model state they were made
|
|
18
|
+
* against (layer stack id or file hash), so a set from another model is
|
|
19
|
+
* refused rather than adopted by accident.
|
|
20
|
+
*/
|
|
21
|
+
import { trackingGuid } from './digest.js';
|
|
22
|
+
export const TRACKING_SIDECAR_VERSION = 1;
|
|
23
|
+
/** Shape check for a set read from storage: `load()` promises a `TrackedSet`, not whatever a hand-edited sidecar holds. */
|
|
24
|
+
export function isTrackedSet(value) {
|
|
25
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
26
|
+
return false;
|
|
27
|
+
const v = value;
|
|
28
|
+
if (typeof v.trackingKey !== 'string' || typeof v.generation !== 'number' || !Number.isInteger(v.generation))
|
|
29
|
+
return false;
|
|
30
|
+
if (v.nodeType !== undefined && typeof v.nodeType !== 'string')
|
|
31
|
+
return false;
|
|
32
|
+
if (typeof v.entries !== 'object' || v.entries === null || Array.isArray(v.entries))
|
|
33
|
+
return false;
|
|
34
|
+
return Object.values(v.entries).every((e) => typeof e === 'object' && e !== null && typeof e.globalId === 'string' && typeof e.digest === 'string');
|
|
35
|
+
}
|
|
36
|
+
/** The `sets` record of a sidecar, or undefined when any set is malformed. */
|
|
37
|
+
export function trackedSetsFrom(sets) {
|
|
38
|
+
if (typeof sets !== 'object' || sets === null || Array.isArray(sets))
|
|
39
|
+
return undefined;
|
|
40
|
+
const out = {};
|
|
41
|
+
for (const [key, value] of Object.entries(sets)) {
|
|
42
|
+
if (!isTrackedSet(value) || value.trackingKey !== key)
|
|
43
|
+
return undefined;
|
|
44
|
+
out[key] = value;
|
|
45
|
+
}
|
|
46
|
+
return out;
|
|
47
|
+
}
|
|
48
|
+
/** In-memory store: tests, and hosts that have not chosen a sidecar yet. */
|
|
49
|
+
export class MemoryTrackingStore {
|
|
50
|
+
sets = new Map();
|
|
51
|
+
load(trackingKey) {
|
|
52
|
+
return this.sets.get(trackingKey);
|
|
53
|
+
}
|
|
54
|
+
save(set) {
|
|
55
|
+
this.sets.set(set.trackingKey, set);
|
|
56
|
+
}
|
|
57
|
+
keys() {
|
|
58
|
+
return [...this.sets.keys()];
|
|
59
|
+
}
|
|
60
|
+
delete(trackingKey) {
|
|
61
|
+
this.sets.delete(trackingKey);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
export function emptyTrackedSet(trackingKey) {
|
|
65
|
+
return { trackingKey, generation: 0, entries: {} };
|
|
66
|
+
}
|
|
67
|
+
export function planTracking(previous, desired, mode) {
|
|
68
|
+
const create = [];
|
|
69
|
+
const update = [];
|
|
70
|
+
const keep = [];
|
|
71
|
+
const remove = [];
|
|
72
|
+
const duplicateLanes = [];
|
|
73
|
+
const nextEntries = {};
|
|
74
|
+
if (mode === 'disabled') {
|
|
75
|
+
return { mode, create, update, keep, remove, next: previous, duplicateLanes };
|
|
76
|
+
}
|
|
77
|
+
const generation = mode === 'replace' ? previous.generation + 1 : previous.generation;
|
|
78
|
+
const guidKey = generation === 0 ? previous.trackingKey : `${previous.trackingKey}#${generation}`;
|
|
79
|
+
const seen = new Set();
|
|
80
|
+
for (const lane of desired) {
|
|
81
|
+
if (seen.has(lane.laneKey)) {
|
|
82
|
+
duplicateLanes.push(lane.laneKey);
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
seen.add(lane.laneKey);
|
|
86
|
+
const prev = mode === 'update' ? previous.entries[lane.laneKey] : undefined;
|
|
87
|
+
if (!prev) {
|
|
88
|
+
const globalId = trackingGuid(guidKey, lane.laneKey);
|
|
89
|
+
create.push({ laneKey: lane.laneKey, globalId });
|
|
90
|
+
nextEntries[lane.laneKey] = { globalId, digest: lane.digest };
|
|
91
|
+
}
|
|
92
|
+
else if (prev.digest !== lane.digest) {
|
|
93
|
+
update.push({ laneKey: lane.laneKey, globalId: prev.globalId });
|
|
94
|
+
nextEntries[lane.laneKey] = { globalId: prev.globalId, digest: lane.digest };
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
keep.push({ laneKey: lane.laneKey, globalId: prev.globalId });
|
|
98
|
+
nextEntries[lane.laneKey] = prev;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// `replace` re-creates every lane under a fresh GlobalId, so every previous
|
|
102
|
+
// element is removed — otherwise the old set is orphaned, not replaced.
|
|
103
|
+
for (const [laneKey, entry] of Object.entries(previous.entries)) {
|
|
104
|
+
if (mode === 'replace' || !seen.has(laneKey))
|
|
105
|
+
remove.push({ laneKey, globalId: entry.globalId });
|
|
106
|
+
}
|
|
107
|
+
return { mode, create, update, keep, remove, next: { trackingKey: previous.trackingKey, generation, entries: nextEntries }, duplicateLanes };
|
|
108
|
+
}
|
|
109
|
+
export class TrackingPinMismatch extends Error {
|
|
110
|
+
expected;
|
|
111
|
+
actual;
|
|
112
|
+
constructor(expected, actual) {
|
|
113
|
+
super(`tracking sidecar is pinned to ${expected}, current model state is ${actual}`);
|
|
114
|
+
this.expected = expected;
|
|
115
|
+
this.actual = actual;
|
|
116
|
+
this.name = 'TrackingPinMismatch';
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/** Read one node's set from a sidecar; refuses a sidecar pinned to another model state. */
|
|
120
|
+
export function trackedSetFrom(sidecar, pinnedTo, trackingKey) {
|
|
121
|
+
if (!sidecar)
|
|
122
|
+
return emptyTrackedSet(trackingKey);
|
|
123
|
+
if (sidecar.version !== TRACKING_SIDECAR_VERSION)
|
|
124
|
+
throw new Error(`unsupported tracking sidecar version ${sidecar.version}`);
|
|
125
|
+
if (sidecar.pinnedTo !== pinnedTo)
|
|
126
|
+
throw new TrackingPinMismatch(sidecar.pinnedTo, pinnedTo);
|
|
127
|
+
return sidecar.sets[trackingKey] ?? emptyTrackedSet(trackingKey);
|
|
128
|
+
}
|
|
129
|
+
export function withTrackedSet(sidecar, pinnedTo, set) {
|
|
130
|
+
return {
|
|
131
|
+
version: TRACKING_SIDECAR_VERSION,
|
|
132
|
+
pinnedTo,
|
|
133
|
+
sets: { ...(sidecar?.pinnedTo === pinnedTo ? sidecar.sets : {}), [set.trackingKey]: set },
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=tracking.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracking.js","sourceRoot":"","sources":["../src/tracking.ts"],"names":[],"mappings":"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"}
|
package/dist/values.d.ts
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The flow data model.
|
|
3
|
+
*
|
|
4
|
+
* Every value travelling along an edge is wrapped in an explicit container
|
|
5
|
+
* (`FlowData`) that says how it is structured — one item, an ordered list,
|
|
6
|
+
* or a group of lists keyed by string. Nodes never sniff shapes: a port
|
|
7
|
+
* declares the `access` it wants and the scheduler lifts the node over the
|
|
8
|
+
* container (see `lift.ts`). That single rule replaces Grasshopper's data
|
|
9
|
+
* trees (paths, Path Mapper, Simplify) and Dynamo's list levels with one
|
|
10
|
+
* keyed level, which is what BIM data actually is: keyed by GlobalId,
|
|
11
|
+
* storey, sheet name, grid intersection.
|
|
12
|
+
*
|
|
13
|
+
* Values are plain data. An entity is a *handle* (`EntityRef`), never a copy
|
|
14
|
+
* of its properties; nodes read what they need through `bim.*` on demand.
|
|
15
|
+
*/
|
|
16
|
+
/** How a port consumes or produces data. */
|
|
17
|
+
export type Access = 'item' | 'list' | 'group';
|
|
18
|
+
/** How an `item` port is lifted over lists (Dynamo lacing). */
|
|
19
|
+
export type Lacing = 'shortest' | 'longest' | 'cross';
|
|
20
|
+
/** A group key: a string so it survives JSON and sidecars unchanged. */
|
|
21
|
+
export type GroupKey = string;
|
|
22
|
+
export type FlowData<T = unknown> = {
|
|
23
|
+
readonly kind: 'item';
|
|
24
|
+
readonly value: T;
|
|
25
|
+
} | {
|
|
26
|
+
readonly kind: 'list';
|
|
27
|
+
readonly items: readonly T[];
|
|
28
|
+
} | {
|
|
29
|
+
readonly kind: 'group';
|
|
30
|
+
readonly branches: ReadonlyMap<GroupKey, readonly T[]>;
|
|
31
|
+
};
|
|
32
|
+
export declare function item<T>(value: T): FlowData<T>;
|
|
33
|
+
export declare function list<T>(items: readonly T[]): FlowData<T>;
|
|
34
|
+
export declare function group<T>(branches: ReadonlyMap<GroupKey, readonly T[]> | Iterable<readonly [GroupKey, readonly T[]]>): FlowData<T>;
|
|
35
|
+
/** The branch key a plain list occupies when a `group` port receives it. */
|
|
36
|
+
export declare const SINGLE_BRANCH: GroupKey;
|
|
37
|
+
/** Total item count regardless of structure. */
|
|
38
|
+
export declare function countItems(data: FlowData): number;
|
|
39
|
+
/** Flatten any structure to a list, branch order preserved. */
|
|
40
|
+
export declare function flatten<T>(data: FlowData<T>): readonly T[];
|
|
41
|
+
/** View any structure as a group: an item or list becomes the single branch. */
|
|
42
|
+
export declare function asGroup<T>(data: FlowData<T>): ReadonlyMap<GroupKey, readonly T[]>;
|
|
43
|
+
export type Scalar = string | number | boolean | null;
|
|
44
|
+
/**
|
|
45
|
+
* An entity handle. `globalId` is the identity that survives reloads,
|
|
46
|
+
* exports and layer publishes; `modelId`/`expressId` are the host's
|
|
47
|
+
* session-scoped address for it and may be re-resolved. Keys, joins and
|
|
48
|
+
* tracking use `globalId`; reads through `bim.*` use the address.
|
|
49
|
+
*/
|
|
50
|
+
export interface EntityRef {
|
|
51
|
+
readonly globalId: string;
|
|
52
|
+
readonly modelId?: string;
|
|
53
|
+
readonly expressId?: number;
|
|
54
|
+
}
|
|
55
|
+
export type Point = readonly [number, number, number];
|
|
56
|
+
export interface Polyline {
|
|
57
|
+
readonly points: readonly Point[];
|
|
58
|
+
readonly closed: boolean;
|
|
59
|
+
}
|
|
60
|
+
export interface Placement {
|
|
61
|
+
readonly position: Point;
|
|
62
|
+
/** Rotation about Z in degrees. */
|
|
63
|
+
readonly rotationZ: number;
|
|
64
|
+
}
|
|
65
|
+
/** Parametric profiles — the shapes `@ifc-lite/create` can author. No BRep. */
|
|
66
|
+
export type Profile = {
|
|
67
|
+
readonly kind: 'rectangle';
|
|
68
|
+
readonly width: number;
|
|
69
|
+
readonly depth: number;
|
|
70
|
+
} | {
|
|
71
|
+
readonly kind: 'circle';
|
|
72
|
+
readonly radius: number;
|
|
73
|
+
} | {
|
|
74
|
+
readonly kind: 'hollowCircle';
|
|
75
|
+
readonly radius: number;
|
|
76
|
+
readonly wallThickness: number;
|
|
77
|
+
} | {
|
|
78
|
+
readonly kind: 'hollowRectangle';
|
|
79
|
+
readonly width: number;
|
|
80
|
+
readonly depth: number;
|
|
81
|
+
readonly wallThickness: number;
|
|
82
|
+
} | {
|
|
83
|
+
readonly kind: 'I' | 'L' | 'T' | 'U';
|
|
84
|
+
readonly width: number;
|
|
85
|
+
readonly depth: number;
|
|
86
|
+
readonly webThickness: number;
|
|
87
|
+
readonly flangeThickness: number;
|
|
88
|
+
};
|
|
89
|
+
/** The value kinds a port can carry. `any` accepts everything (Watch, Script). */
|
|
90
|
+
export type ValueKind = 'scalar' | 'entity' | 'point' | 'polyline' | 'placement' | 'profile' | 'elementSpec' | 'table' | 'any';
|
|
91
|
+
/** A port's static type: what kind of value, and how it is structured. */
|
|
92
|
+
export interface PortType {
|
|
93
|
+
readonly kind: ValueKind;
|
|
94
|
+
readonly access: Access;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Whether a value produced on `from` can be consumed by `to`.
|
|
98
|
+
* Structure is always adaptable (the scheduler lifts), so only the value
|
|
99
|
+
* kind is checked; `any` matches both ways.
|
|
100
|
+
*/
|
|
101
|
+
export declare function isAssignable(from: PortType, to: PortType): boolean;
|
|
102
|
+
//# sourceMappingURL=values.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"values.d.ts","sourceRoot":"","sources":["../src/values.ts"],"names":[],"mappings":"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"}
|
package/dist/values.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
export function item(value) {
|
|
5
|
+
return { kind: 'item', value };
|
|
6
|
+
}
|
|
7
|
+
export function list(items) {
|
|
8
|
+
return { kind: 'list', items };
|
|
9
|
+
}
|
|
10
|
+
export function group(branches) {
|
|
11
|
+
return { kind: 'group', branches: branches instanceof Map ? branches : new Map(branches) };
|
|
12
|
+
}
|
|
13
|
+
/** The branch key a plain list occupies when a `group` port receives it. */
|
|
14
|
+
export const SINGLE_BRANCH = '';
|
|
15
|
+
/** Total item count regardless of structure. */
|
|
16
|
+
export function countItems(data) {
|
|
17
|
+
switch (data.kind) {
|
|
18
|
+
case 'item':
|
|
19
|
+
return 1;
|
|
20
|
+
case 'list':
|
|
21
|
+
return data.items.length;
|
|
22
|
+
case 'group': {
|
|
23
|
+
let n = 0;
|
|
24
|
+
for (const items of data.branches.values())
|
|
25
|
+
n += items.length;
|
|
26
|
+
return n;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/** Flatten any structure to a list, branch order preserved. */
|
|
31
|
+
export function flatten(data) {
|
|
32
|
+
switch (data.kind) {
|
|
33
|
+
case 'item':
|
|
34
|
+
return [data.value];
|
|
35
|
+
case 'list':
|
|
36
|
+
return data.items;
|
|
37
|
+
case 'group': {
|
|
38
|
+
const out = [];
|
|
39
|
+
for (const items of data.branches.values())
|
|
40
|
+
out.push(...items);
|
|
41
|
+
return out;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/** View any structure as a group: an item or list becomes the single branch. */
|
|
46
|
+
export function asGroup(data) {
|
|
47
|
+
switch (data.kind) {
|
|
48
|
+
case 'item':
|
|
49
|
+
return new Map([[SINGLE_BRANCH, [data.value]]]);
|
|
50
|
+
case 'list':
|
|
51
|
+
return new Map([[SINGLE_BRANCH, data.items]]);
|
|
52
|
+
case 'group':
|
|
53
|
+
return data.branches;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Whether a value produced on `from` can be consumed by `to`.
|
|
58
|
+
* Structure is always adaptable (the scheduler lifts), so only the value
|
|
59
|
+
* kind is checked; `any` matches both ways.
|
|
60
|
+
*/
|
|
61
|
+
export function isAssignable(from, to) {
|
|
62
|
+
return from.kind === 'any' || to.kind === 'any' || from.kind === to.kind;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=values.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"values.js","sourceRoot":"","sources":["../src/values.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAkC/D,MAAM,UAAU,IAAI,CAAI,KAAQ;IAC9B,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,IAAI,CAAI,KAAmB;IACzC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,KAAK,CAAI,QAA2F;IAClH,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,YAAY,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;AAC7F,CAAC;AAED,4EAA4E;AAC5E,MAAM,CAAC,MAAM,aAAa,GAAa,EAAE,CAAC;AAE1C,gDAAgD;AAChD,MAAM,UAAU,UAAU,CAAC,IAAc;IACvC,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,MAAM;YACT,OAAO,CAAC,CAAC;QACX,KAAK,MAAM;YACT,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;QAC3B,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,IAAI,CAAC,GAAG,CAAC,CAAC;YACV,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE;gBAAE,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC;YAC9D,OAAO,CAAC,CAAC;QACX,CAAC;IACH,CAAC;AACH,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,OAAO,CAAI,IAAiB;IAC1C,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,MAAM;YACT,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACtB,KAAK,MAAM;YACT,OAAO,IAAI,CAAC,KAAK,CAAC;QACpB,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,MAAM,GAAG,GAAQ,EAAE,CAAC;YACpB,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE;gBAAE,GAAG,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC;YAC/D,OAAO,GAAG,CAAC;QACb,CAAC;IACH,CAAC;AACH,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,OAAO,CAAI,IAAiB;IAC1C,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,MAAM;YACT,OAAO,IAAI,GAAG,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAClD,KAAK,MAAM;YACT,OAAO,IAAI,GAAG,CAAC,CAAC,CAAC,aAAa,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAChD,KAAK,OAAO;YACV,OAAO,IAAI,CAAC,QAAQ,CAAC;IACzB,CAAC;AACH,CAAC;AAyDD;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,IAAc,EAAE,EAAY;IACvD,OAAO,IAAI,CAAC,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,IAAI,KAAK,EAAE,CAAC,IAAI,CAAC;AAC3E,CAAC"}
|
package/dist/wiring.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registry-aware validation: the half `validateFlowDocument` cannot do.
|
|
3
|
+
*
|
|
4
|
+
* The document validator is structural — it can say `edges[0].from` is
|
|
5
|
+
* `[nodeId, port]` and that the node exists, but not that the node HAS that
|
|
6
|
+
* port, because it deliberately works without a registry. Everything that
|
|
7
|
+
* needs to know what a node type actually declares lives here: unknown node
|
|
8
|
+
* types, edge endpoints naming ports that do not exist, type-incompatible
|
|
9
|
+
* edges, and Player `inputs` / graph `outputs` pointing at parameters and
|
|
10
|
+
* ports that are not there.
|
|
11
|
+
*
|
|
12
|
+
* Without this, a graph whose declared output names a missing port passed
|
|
13
|
+
* validation, reported `ok: true`, exited 0 — and then produced nothing for
|
|
14
|
+
* that output at run time. A validator that cannot see the defect it is
|
|
15
|
+
* asked about reads as success, which is worse than not running at all.
|
|
16
|
+
*/
|
|
17
|
+
import type { FlowDocument, DocumentProblem } from './document.js';
|
|
18
|
+
import type { NodeRegistry } from './registry.js';
|
|
19
|
+
export declare function validateFlowWiring(doc: FlowDocument, registry: NodeRegistry<unknown>): DocumentProblem[];
|
|
20
|
+
//# sourceMappingURL=wiring.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wiring.d.ts","sourceRoot":"","sources":["../src/wiring.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAGlD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,YAAY,EAAE,QAAQ,EAAE,YAAY,CAAC,OAAO,CAAC,GAAG,eAAe,EAAE,CAmDxG"}
|
package/dist/wiring.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
import { isAssignable } from './values.js';
|
|
5
|
+
export function validateFlowWiring(doc, registry) {
|
|
6
|
+
const problems = [];
|
|
7
|
+
const add = (path, message) => problems.push({ path, message });
|
|
8
|
+
const defOf = (nodeId) => {
|
|
9
|
+
const node = doc.nodes.find((n) => n.id === nodeId);
|
|
10
|
+
return node ? registry.get(node.type) : undefined;
|
|
11
|
+
};
|
|
12
|
+
doc.nodes.forEach((n, i) => {
|
|
13
|
+
if (!registry.get(n.type))
|
|
14
|
+
add(`nodes[${i}]`, `unknown node type "${n.type}" (${n.id})`);
|
|
15
|
+
});
|
|
16
|
+
doc.edges.forEach((e, i) => {
|
|
17
|
+
const from = defOf(e.from[0])?.outputs.find((p) => p.name === e.from[1]);
|
|
18
|
+
const to = defOf(e.to[0])?.inputs.find((p) => p.name === e.to[1]);
|
|
19
|
+
if (defOf(e.from[0]) && !from)
|
|
20
|
+
add(`edges[${i}].from`, `node "${e.from[0]}" has no output "${e.from[1]}"`);
|
|
21
|
+
if (defOf(e.to[0]) && !to)
|
|
22
|
+
add(`edges[${i}].to`, `node "${e.to[0]}" has no input "${e.to[1]}"`);
|
|
23
|
+
if (from && to && !isAssignable(from.type, to.type)) {
|
|
24
|
+
add(`edges[${i}]`, `${e.from[0]}.${e.from[1]} (${from.type.kind}) cannot feed ${e.to[0]}.${e.to[1]} (${to.type.kind})`);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
doc.inputs.forEach((input, i) => {
|
|
28
|
+
const def = defOf(input.nodeId);
|
|
29
|
+
if (def && !def.params.some((p) => p.name === input.param)) {
|
|
30
|
+
add(`inputs[${i}]`, `node "${input.nodeId}" has no parameter "${input.param}"`);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
doc.outputs.forEach((output, i) => {
|
|
34
|
+
const def = defOf(output.nodeId);
|
|
35
|
+
if (def && !def.outputs.some((p) => p.name === output.port)) {
|
|
36
|
+
add(`outputs[${i}]`, `node "${output.nodeId}" has no output "${output.port}"`);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
// Required inputs with nothing connected: the scheduler fails the node at
|
|
40
|
+
// run time, so a validator that stayed silent here would promise a run it
|
|
41
|
+
// knows cannot happen.
|
|
42
|
+
for (const node of doc.nodes) {
|
|
43
|
+
const def = registry.get(node.type);
|
|
44
|
+
if (!def)
|
|
45
|
+
continue;
|
|
46
|
+
for (const port of def.inputs) {
|
|
47
|
+
if (port.optional)
|
|
48
|
+
continue;
|
|
49
|
+
if (!doc.edges.some((e) => e.to[0] === node.id && e.to[1] === port.name)) {
|
|
50
|
+
add(`nodes[${doc.nodes.indexOf(node)}]`, `required input "${port.name}" of "${node.id}" is not connected`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return problems;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=wiring.js.map
|