@hockeymask/core 0.1.0 → 0.1.1

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/index.d.mts CHANGED
@@ -206,7 +206,8 @@ export declare class SourceUnavailable extends ConnectorError {
206
206
  //#region src/mapping/result.d.ts
207
207
  /**
208
208
  * Failure as a value. Steps, nodes and the compiler return `Result` instead of throwing, so
209
- * the executor can apply each node's failure policy (arch.md, "Step signature").
209
+ * the executor can apply each node's failure policy (arch.md,
210
+ * "Failure policy and partial results").
210
211
  */
211
212
  export type Result<T, E> = {
212
213
  readonly ok: true;
@@ -231,7 +232,7 @@ export interface StepError {
231
232
  readonly code: ErrorCode;
232
233
  readonly detail: string;
233
234
  }
234
- /** A runtime failure located in the graph (arch.md `MappingError`). */
235
+ /** A runtime failure located in the graph. */
235
236
  export interface MappingError extends StepError {
236
237
  readonly node: string;
237
238
  readonly op: string;
@@ -913,7 +914,7 @@ interface Origin {
913
914
  interface NodeOutput {
914
915
  readonly type: RecordType;
915
916
  readonly origins: Readonly<Record<string, Origin>>;
916
- /** Can drop records on a failed field (limitation #20). */
917
+ /** Can drop records on a failed field. */
917
918
  readonly canSkip?: boolean;
918
919
  }
919
920
  /** An input slot: the param path that holds an upstream node id. */
@@ -977,7 +978,7 @@ interface SinkWrite {
977
978
  * The rows are the complete set of the target for the company (and source system, where
978
979
  * the identity has one): the host calls `synchronize_all(rows, scope)`, deleting rows not
979
980
  * written. Writes of one run go in one transaction, inserts parent-first and deletes
980
- * child-first (exclusions before the lanes they reference). A sink that does not write
981
+ * child-first (join-table rows before the entities they reference). A sink that does not write
981
982
  * (held, stopped, failed) deletes nothing.
982
983
  */
983
984
  readonly mode?: 'replace';
@@ -1022,7 +1023,7 @@ interface RunOptions {
1022
1023
  interface CompiledNode {
1023
1024
  /** Null for a node without an output relation (`sink`); `metrics` types what it reports. */
1024
1025
  readonly output: NodeOutput | null;
1025
- /** Target entities a sink writes (one sink per entity, structural rule 3). */
1026
+ /** Target entities a sink writes (one sink per entity, structural rule 4). */
1026
1027
  readonly writes?: readonly string[];
1027
1028
  readonly run: (inputs: Inputs, ctx: RunContext, options?: RunOptions) => Result<NodeRun, NodeFailure>;
1028
1029
  }
@@ -1040,7 +1041,7 @@ interface NodeDef<P = unknown> {
1040
1041
  readonly defaults: () => Record<string, unknown>;
1041
1042
  /** Parses the node object without `id`, `op` and `label`. */
1042
1043
  readonly params: z.ZodType<P>;
1043
- /** Accepts `tolerate_skips` (limitation #20). */
1044
+ /** Accepts `tolerate_skips` (arch.md, "Failure policy and partial results"). */
1044
1045
  readonly toleratesSkips?: (params: P) => boolean;
1045
1046
  readonly compile: (params: P, env: NodeEnv) => Result<CompiledNode, readonly NodeIssue[]>;
1046
1047
  }
@@ -1361,7 +1362,7 @@ export declare function topologicalOrder(nodes: readonly NodeJson[]): string[];
1361
1362
  export declare function sourceRecordType(schema: JsonSchema): Result<RecordType, string>;
1362
1363
  /** A starting `expectedSchema` for a source table, from what its connector describes. */
1363
1364
  export declare function tableJsonSchema(table: Table, system?: string): JsonSchema;
1364
- /** The pinned schema of an internal entity read as a source (structural rule 4). */
1365
+ /** The pinned schema of an internal entity read as a source. */
1365
1366
  export declare function entityJsonSchema(entity: TargetEntity): JsonSchema;
1366
1367
  /** Does a runtime value inhabit this type? */
1367
1368
  export declare function conforms(value: Val, type: MType): boolean;
@@ -1389,12 +1390,12 @@ interface OpDef<P = unknown> {
1389
1390
  readonly nulls: 'propagate' | 'consume';
1390
1391
  /**
1391
1392
  * What the op reads: 'value' (default) needs a piped value; 'none' starts a pipeline and
1392
- * takes no piped input (`()` in arch.md signatures); 'any' accepts either (`when`).
1393
+ * takes no piped input; 'any' accepts either (`when`).
1393
1394
  */
1394
1395
  readonly input?: 'value' | 'none' | 'any';
1395
1396
  /** Must be the last step (`omit_if_null`). */
1396
1397
  readonly terminal?: boolean;
1397
- /** Whether this op can return an error at runtime, given its params (limitation #20). */
1398
+ /** Whether this op can return an error at runtime, given its params. */
1398
1399
  readonly fallible: (params: P, env: RecordEnv, input: MType) => boolean;
1399
1400
  /** Output type for an input type; an error message when the input is not accepted. */
1400
1401
  readonly signature: (input: MType, params: P, env: RecordEnv) => Result<MType, string>;
@@ -1437,8 +1438,8 @@ export type StepJson = {
1437
1438
  export declare function expandStep(raw: StepJson): Result<StepJson, string>;
1438
1439
  /**
1439
1440
  * The form a version is stored and compiled in: sugar expanded, `lookup_entity` rewritten
1440
- * into graph nodes, and every internal source's schema pinned from the target model
1441
- * (structural rule 4). Idempotent.
1441
+ * into graph nodes, and every internal source's schema pinned from the target model.
1442
+ * Idempotent.
1442
1443
  */
1443
1444
  export declare function normalizeVersion(version: MappingVersion, env: Pick<GraphEnv, 'targets'>): {
1444
1445
  version: MappingVersion;
package/dist/index.mjs CHANGED
@@ -64,7 +64,7 @@ function stepError(code, detail) {
64
64
  * The runtime value of the `date` type is an `Instant`: a frozen point in time in epoch
65
65
  * milliseconds. Calendar math ("+10y", "start of day") happens in an IANA time zone, using
66
66
  * `Intl.DateTimeFormat` to move between instants and wall-clock time. "now" only ever comes
67
- * from the run context (arch.md limitation #10), never from `Date.now()`.
67
+ * from the run context (arch.md, "Time"), never from `Date.now()`.
68
68
  */
69
69
  var Instant = class Instant {
70
70
  epochMs;
@@ -609,7 +609,7 @@ function typeName(text) {
609
609
  * A record is a plain frozen-by-convention object; an absent key reads as null (that is how
610
610
  * `omit_if_null` and `skip_field` leave a field out of a write). Nothing here depends on
611
611
  * insertion order: hashing and sorting go through `canonicalKey` and `compareVals`, so two
612
- * runs over the same snapshot produce identical output (arch.md limitation #17).
612
+ * runs over the same snapshot produce identical output (arch.md, "Execution model").
613
613
  */
614
614
  /** Returned by a step to leave its field out of the record entirely (`omit_if_null`). */
615
615
  const OMIT = Symbol("omit");
@@ -698,8 +698,8 @@ function sortRecords(rows, fields) {
698
698
  //#endregion
699
699
  //#region src/mapping/predicate.ts
700
700
  /**
701
- * The one predicate vocabulary (arch.md, "Conditional"), shared by `when`, `filter_list`,
702
- * the `filter` node, join `where`, aggregate `where`, and `rows` expectations.
701
+ * The one predicate vocabulary, shared by `when`, `filter_list`, the `filter` node, join
702
+ * `where`, aggregate `where`, and `rows` expectations.
703
703
  *
704
704
  * Shape: `{ path?, <predicate>: operand }` with exactly one predicate key, or a composition
705
705
  * `{ all: { of: [...] } }`, `{ any: { of: [...] } }`, `{ not: cond }`. Without `path` the
@@ -707,7 +707,7 @@ function sortRecords(rows, fields) {
707
707
  * `{ param: name }`.
708
708
  *
709
709
  * Null never compares: ordering and equality with a null side are false, `neq` and `not_in`
710
- * are their negations and so hold for null (arch.md L366).
710
+ * are their negations and so hold for null (arch.md, "Null semantics").
711
711
  */
712
712
  const COMPARISONS = [
713
713
  "eq",
@@ -1173,7 +1173,7 @@ function compileExpectation(node, exp, output, renv) {
1173
1173
  //#endregion
1174
1174
  //#region src/mapping/nodes/def.ts
1175
1175
  /**
1176
- * Collection node definitions (arch.md, "Node definition, collection scope").
1176
+ * Collection node definitions.
1177
1177
  *
1178
1178
  * A node definition compiles itself: from its params and the types of its inputs it returns
1179
1179
  * its output type and a closure, in one step, so type checking and compilation stay the same
@@ -1208,7 +1208,7 @@ function fail(message, extra = {}) {
1208
1208
  * `aggregate`: one record per group of `group_by` (one record overall for `group_by: []`,
1209
1209
  * even on empty input). Every aggregation takes an optional `where`, making it a filtered
1210
1210
  * aggregate. Results never depend on input order: `sum` adds in sorted order, `collect_set`
1211
- * is sorted, and `collect_list`, `first`, `last` require `order_by` (limitation #17).
1211
+ * is sorted, and `collect_list`, `first`, `last` require `order_by`.
1212
1212
  */
1213
1213
  const orderBy = z.array(z.union([z.string().min(1), z.strictObject({
1214
1214
  path: z.string().min(1),
@@ -1444,7 +1444,7 @@ const aggregateNode = defineNode({
1444
1444
  //#endregion
1445
1445
  //#region src/mapping/ops/def.ts
1446
1446
  /**
1447
- * Record op definitions (arch.md, "Op definition, record scope").
1447
+ * Record op definitions.
1448
1448
  *
1449
1449
  * An op is a params schema, a type function and a curried implementation. `signature` is
1450
1450
  * the validator's half; `run` binds params once per compiled version and returns the
@@ -1599,7 +1599,7 @@ function versionJsonSchema() {
1599
1599
  * - `unique`: `keys` unique.
1600
1600
  * - `rows`: every row satisfies `cond`.
1601
1601
  * - `acyclic`: the edges `edge.from -> edge.to` of `in` contain no cycle. With `edge.key` the
1602
- * edges belong to groups (an article's own routing graph) and each group is checked on its
1602
+ * edges belong to groups (each product's own supply graph) and each group is checked on its
1603
1603
  * own; with `per`, every distinct `per.key` of `per.in` is checked against all edges. Either
1604
1604
  * way a group's rows of `remove.in` are taken out first. Cycle detection is iterative, so
1605
1605
  * it is engine code rather than a composition of DAG nodes.
@@ -2025,7 +2025,7 @@ function tableJsonSchema(table, system) {
2025
2025
  description: c.description
2026
2026
  })));
2027
2027
  }
2028
- /** The pinned schema of an internal entity read as a source (structural rule 4). */
2028
+ /** The pinned schema of an internal entity read as a source. */
2029
2029
  function entityJsonSchema(entity) {
2030
2030
  return objectSchema(`internal.${entity.name}`, entity.fields.map((f) => ({
2031
2031
  name: f.name,
@@ -2216,7 +2216,7 @@ const filterNode = defineNode({
2216
2216
  * with the same name on both sides merges, and any other collision is a save error. Null keys
2217
2217
  * never match.
2218
2218
  *
2219
- * `cardinality` is declared and enforced while the hash table is built (limitation #16):
2219
+ * `cardinality` is declared and enforced while the hash table is built:
2220
2220
  * `n:1` right keys unique, `1:n` left keys unique, `1:1` both, `n:m` unrestricted but needs a
2221
2221
  * `count` expectation on this node.
2222
2222
  */
@@ -2940,7 +2940,7 @@ const requireOp = defineOp({
2940
2940
  });
2941
2941
  /**
2942
2942
  * Terminal. A null leaves the field out of the record (and so out of the write) instead of
2943
- * setting it to null (limitation #8). Readers downstream see the field as nullable.
2943
+ * setting it to null. Readers downstream see the field as nullable.
2944
2944
  */
2945
2945
  const omitIfNull = defineOp({
2946
2946
  name: "omit_if_null",
@@ -3347,7 +3347,7 @@ const slug = defineOp({
3347
3347
  //#endregion
3348
3348
  //#region src/mapping/ops/when.ts
3349
3349
  /**
3350
- * `when`: the one general conditional (arch.md limitation #12). First matching case wins;
3350
+ * `when`: the one general conditional. First matching case wins;
3351
3351
  * `then` and `else` are a literal, `null`, or `{ "pass": true }` to forward the input. `else`
3352
3352
  * defaults to pass-through. As a pipeline's first step there is no input: every case must
3353
3353
  * test a `path`, and `else` is required.
@@ -3458,7 +3458,7 @@ const opRegistry = Object.fromEntries(all$1.map((op) => [op.name, op]));
3458
3458
  //#endregion
3459
3459
  //#region src/mapping/sugar.ts
3460
3460
  /**
3461
- * Sugar, kept for readability and expanded on save (arch.md, "Conditional" and "Lookup").
3461
+ * Sugar, kept for readability and expanded on save.
3462
3462
  *
3463
3463
  * - `{ op: "if_<predicate>", <operand>, then, else? }` expands to a one-case `when`, e.g.
3464
3464
  * `{ op: "if_lte", value: 0, then: null }` to
@@ -3672,8 +3672,8 @@ function rewriteLookups(node, env, sources, issues) {
3672
3672
  }
3673
3673
  /**
3674
3674
  * The form a version is stored and compiled in: sugar expanded, `lookup_entity` rewritten
3675
- * into graph nodes, and every internal source's schema pinned from the target model
3676
- * (structural rule 4). Idempotent.
3675
+ * into graph nodes, and every internal source's schema pinned from the target model.
3676
+ * Idempotent.
3677
3677
  */
3678
3678
  function normalizeVersion(version, env) {
3679
3679
  const issues = [];
@@ -4026,14 +4026,14 @@ function metric(node, name, rows) {
4026
4026
  * fails at save, blamed on the `map` field that produced it.
4027
4027
  *
4028
4028
  * A reference field listed in `refs` is written as a natural key of the entity it references
4029
- * (`product_id` as a SKU, a lane as its `(from, to)` pair) and typed by those fields, so a
4029
+ * (`product_id` as a SKU, a join-table row as its two keys) and typed by those fields, so a
4030
4030
  * key of the wrong shape fails at save; the host resolves it to an id when it writes. Key
4031
4031
  * fields must be non-null, except a nullable identity field of an entity whose identity
4032
- * matches NULLs as equal (the inbound supplier lane, `from_warehouse_id = NULL`).
4032
+ * matches NULLs as equal (a transfer with no origin, `from_warehouse_id = NULL`).
4033
4033
  *
4034
4034
  * `mode: 'replace'` makes the rows the complete set of the target for the company: the host
4035
4035
  * reconciles with `synchronize_all`, deleting rows this run no longer produces. That is what a
4036
- * presence-only entity needs (an exclusion row *is* the rule). Because a short source read
4036
+ * presence-only entity needs (a join-table row *is* the link). Because a short source read
4037
4037
  * would then delete real rows, `in` must carry a `count` expectation with a lower bound
4038
4038
  * (`min` or `shiftRatio.min`) whose action is `stop` or `skip_downstream`.
4039
4039
  *
@@ -4092,7 +4092,7 @@ const sinkNode = defineNode({
4092
4092
  op: "sink",
4093
4093
  label: "Write to target",
4094
4094
  category: "target",
4095
- description: "Writes rows into a Spherecast entity, updating records with the same key.",
4095
+ description: "Writes rows into a target entity, updating records with the same key.",
4096
4096
  slots: [{
4097
4097
  id: "in",
4098
4098
  label: "In",
@@ -4488,7 +4488,7 @@ function topologicalOrder(nodes) {
4488
4488
  //#endregion
4489
4489
  //#region src/mapping/compile.ts
4490
4490
  /**
4491
- * The graph compiler (arch.md, "The compiler"): walks nodes in topological order, threading
4491
+ * The graph compiler (arch.md, "Type checking"): walks nodes in topological order, threading
4492
4492
  * record types along edges, and compiles each node into a closure in the same pass. The
4493
4493
  * result is one plan per version; a run never touches JSON.
4494
4494
  *
@@ -4690,7 +4690,7 @@ function dedupe(issues) {
4690
4690
  return true;
4691
4691
  });
4692
4692
  }
4693
- /** One sink per target entity (structural rule 3): resolve overlaps with a union. */
4693
+ /** One sink per target entity (structural rule 4): resolve overlaps with a union. */
4694
4694
  function sinkIssues(compiled) {
4695
4695
  const writers = /* @__PURE__ */ new Map();
4696
4696
  for (const [id, c] of compiled) for (const entity of c.node.writes ?? []) writers.set(entity, [...writers.get(entity) ?? [], id]);
@@ -4701,8 +4701,8 @@ function sinkIssues(compiled) {
4701
4701
  }));
4702
4702
  }
4703
4703
  /**
4704
- * Limitation #20: a map that can drop records upstream of a join or aggregate can flip its
4705
- * result, so the first join or aggregate on every path from it must declare `tolerate_skips`.
4704
+ * A map that can drop records upstream of a join or aggregate can flip its result, so the
4705
+ * first join or aggregate on every path from it must declare `tolerate_skips`.
4706
4706
  */
4707
4707
  function skipIssues(version, compiled) {
4708
4708
  const next = consumers(version.nodes);