machinalayout 0.1.0 → 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.
Files changed (45) hide show
  1. package/README.md +280 -49
  2. package/dist/chunk-BJOQRPPX.js +382 -0
  3. package/dist/chunk-HU6XYOH7.js +133 -0
  4. package/dist/chunk-KYWOCAHK.js +205 -0
  5. package/dist/chunk-RJYRJ3LD.js +0 -0
  6. package/dist/chunk-TR24ERZT.js +66 -0
  7. package/dist/dispatch/index.d.ts +49 -0
  8. package/dist/dispatch/index.js +217 -0
  9. package/dist/index.d.ts +15 -238
  10. package/dist/index.js +596 -591
  11. package/dist/react/index.d.ts +33 -0
  12. package/dist/react/index.js +7 -0
  13. package/dist/react-native/index.d.ts +30 -0
  14. package/dist/react-native/index.js +83 -0
  15. package/dist/text/index.d.ts +10 -0
  16. package/dist/text/index.js +9 -0
  17. package/dist/text/react/index.d.ts +14 -0
  18. package/dist/text/react/index.js +7 -0
  19. package/dist/text/react-native/index.d.ts +16 -0
  20. package/dist/text/react-native/index.js +155 -0
  21. package/dist/text/vue/index.d.ts +113 -0
  22. package/dist/text/vue/index.js +202 -0
  23. package/dist/types-BudfpzZX.d.ts +184 -0
  24. package/dist/types-C4poVJpR.d.ts +74 -0
  25. package/dist/vue/index.d.ts +173 -0
  26. package/dist/vue/index.js +111 -0
  27. package/docs/adapter-packaging-a0-plan.md +352 -0
  28. package/docs/adapters.md +19 -0
  29. package/docs/api-coherence-m8-audit.md +397 -0
  30. package/docs/error-codes.md +84 -0
  31. package/docs/grid-arrange-m5a-contract.md +480 -0
  32. package/docs/grid-arrange.md +51 -0
  33. package/docs/layout-interpolation.md +52 -0
  34. package/docs/machina-dispatch-d0-contract.md +496 -0
  35. package/docs/machina-dispatch.md +143 -0
  36. package/docs/named-layers.md +40 -0
  37. package/docs/react-adapter.md +51 -69
  38. package/docs/react-native-adapter.md +56 -0
  39. package/docs/react-native-text-renderer.md +50 -0
  40. package/docs/reference-alignment-m7a-contract.md +384 -0
  41. package/docs/reference-alignment.md +44 -0
  42. package/docs/responsive-variants.md +54 -0
  43. package/docs/vue-adapter.md +55 -0
  44. package/docs/vue-text-renderer.md +55 -0
  45. package/package.json +60 -5
@@ -0,0 +1,205 @@
1
+ import {
2
+ parseMachinaText
3
+ } from "./chunk-BJOQRPPX.js";
4
+
5
+ // src/text/react/MachinaTextView.tsx
6
+ import React from "react";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
+ var DEFAULT_POLICY = {
9
+ variant: "body",
10
+ wrap: "word",
11
+ overflow: "clip",
12
+ align: "start",
13
+ leading: "normal",
14
+ blockGap: 8,
15
+ listGap: 2,
16
+ valign: "top"
17
+ };
18
+ var INLINE_CODE_FONT = 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace';
19
+ var VARIANT_STYLE = {
20
+ body: { fontSize: "14px", fontWeight: 400, lineHeight: 1.4 },
21
+ label: { fontSize: "12px", fontWeight: 500, lineHeight: 1.3 },
22
+ caption: { fontSize: "11px", fontWeight: 400, lineHeight: 1.25, opacity: 0.8 },
23
+ title: { fontSize: "18px", fontWeight: 700, lineHeight: 1.25 },
24
+ mono: { fontSize: "12px", lineHeight: 1.35, fontFamily: INLINE_CODE_FONT }
25
+ };
26
+ function isMachinaTextDocument(value) {
27
+ return typeof value === "object" && value !== null && "blocks" in value;
28
+ }
29
+ function isMachinaTextSpec(value) {
30
+ return typeof value === "object" && value !== null && "kind" in value && value.kind === "text";
31
+ }
32
+ function normalizePositive(value, fallback) {
33
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : fallback;
34
+ }
35
+ function normalizeNonNegative(value, fallback) {
36
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : fallback;
37
+ }
38
+ function normalizeLeading(value) {
39
+ if (value === void 0) return DEFAULT_POLICY.leading;
40
+ if (value === "tight" || value === "normal" || value === "loose") return value;
41
+ return normalizePositive(value, resolveLineHeight(DEFAULT_POLICY));
42
+ }
43
+ function normalizeSpecPolicy(spec) {
44
+ return {
45
+ variant: spec.variant ?? DEFAULT_POLICY.variant,
46
+ wrap: spec.wrap ?? DEFAULT_POLICY.wrap,
47
+ overflow: spec.overflow ?? DEFAULT_POLICY.overflow,
48
+ align: spec.align ?? DEFAULT_POLICY.align,
49
+ leading: normalizeLeading(spec.leading),
50
+ blockGap: normalizeNonNegative(spec.blockGap, DEFAULT_POLICY.blockGap),
51
+ listGap: normalizeNonNegative(spec.listGap, DEFAULT_POLICY.listGap),
52
+ valign: spec.valign ?? DEFAULT_POLICY.valign
53
+ };
54
+ }
55
+ function normalizeText(text) {
56
+ if (isMachinaTextDocument(text))
57
+ return { document: text, diagnostics: [], policy: DEFAULT_POLICY };
58
+ if (isMachinaTextSpec(text)) {
59
+ const result2 = parseMachinaText(text.source);
60
+ return {
61
+ document: result2.document,
62
+ diagnostics: result2.diagnostics,
63
+ policy: normalizeSpecPolicy(text)
64
+ };
65
+ }
66
+ const result = parseMachinaText(typeof text === "string" ? { kind: "machina-text", text } : text);
67
+ return { document: result.document, diagnostics: result.diagnostics, policy: DEFAULT_POLICY };
68
+ }
69
+ function resolveLineHeight(policy) {
70
+ if (policy.leading === "tight") return 1.15;
71
+ if (policy.leading === "loose") return 1.6;
72
+ if (typeof policy.leading === "number") return policy.leading;
73
+ return VARIANT_STYLE[policy.variant].lineHeight;
74
+ }
75
+ function policyStyle(policy) {
76
+ const wrapStyle = {
77
+ word: { whiteSpace: "normal", overflowWrap: "anywhere" },
78
+ none: { whiteSpace: "nowrap" }
79
+ };
80
+ const overflowStyle = {
81
+ clip: { overflow: "hidden" },
82
+ ellipsis: { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
83
+ scroll: { overflow: "auto" }
84
+ };
85
+ const alignStyle = {
86
+ start: { textAlign: "left" },
87
+ center: { textAlign: "center" },
88
+ end: { textAlign: "right" }
89
+ };
90
+ const justifyContent = {
91
+ top: "flex-start",
92
+ center: "center",
93
+ bottom: "flex-end"
94
+ };
95
+ return {
96
+ width: "100%",
97
+ height: "100%",
98
+ boxSizing: "border-box",
99
+ display: "flex",
100
+ flexDirection: "column",
101
+ justifyContent: justifyContent[policy.valign],
102
+ minWidth: 0,
103
+ ...VARIANT_STYLE[policy.variant],
104
+ lineHeight: resolveLineHeight(policy),
105
+ ...wrapStyle[policy.wrap],
106
+ ...overflowStyle[policy.overflow],
107
+ ...alignStyle[policy.align]
108
+ };
109
+ }
110
+ function renderInline(inline, key, props) {
111
+ switch (inline.kind) {
112
+ case "text":
113
+ return /* @__PURE__ */ jsx(React.Fragment, { children: inline.text }, key);
114
+ case "strong":
115
+ return /* @__PURE__ */ jsx("strong", { children: inline.children.map((c, i) => renderInline(c, `${key}-s-${i}`, props)) }, key);
116
+ case "emphasis":
117
+ return /* @__PURE__ */ jsx("em", { children: inline.children.map((c, i) => renderInline(c, `${key}-e-${i}`, props)) }, key);
118
+ case "code":
119
+ return /* @__PURE__ */ jsx(
120
+ "code",
121
+ {
122
+ style: {
123
+ fontFamily: INLINE_CODE_FONT,
124
+ backgroundColor: "rgba(127, 127, 127, 0.15)",
125
+ borderRadius: 3,
126
+ padding: "0 0.25em"
127
+ },
128
+ children: inline.text
129
+ },
130
+ key
131
+ );
132
+ case "link": {
133
+ const rel = props.linkTarget === "_blank" ? "noreferrer noopener" : void 0;
134
+ return /* @__PURE__ */ jsx(
135
+ "a",
136
+ {
137
+ href: inline.href,
138
+ target: props.linkTarget,
139
+ rel,
140
+ onClick: (event) => props.onLinkClick?.(inline.href, event),
141
+ children: inline.children.map((c, i) => renderInline(c, `${key}-l-${i}`, props))
142
+ },
143
+ key
144
+ );
145
+ }
146
+ }
147
+ }
148
+ function renderBulletItem(item, path, props, listGap) {
149
+ return /* @__PURE__ */ jsxs("li", { style: { marginBottom: listGap }, children: [
150
+ item.inline.map((i, idx) => renderInline(i, `${path}-i-${idx}`, props)),
151
+ item.children?.length ? /* @__PURE__ */ jsx("ul", { style: { margin: "0.25em 0 0 0", paddingLeft: "1.25em" }, children: item.children.map((c, idx) => renderBulletItem(c, `${path}-c-${idx}`, props, listGap)) }) : null
152
+ ] }, path);
153
+ }
154
+ function MachinaTextView(props) {
155
+ const normalized = normalizeText(props.text);
156
+ return /* @__PURE__ */ jsx("div", { className: props.className, style: { ...policyStyle(normalized.policy), ...props.style }, children: /* @__PURE__ */ jsxs("div", { style: { minWidth: 0 }, children: [
157
+ normalized.document.blocks.map(
158
+ (block, index) => block.kind === "paragraph" ? /* @__PURE__ */ jsx(
159
+ "p",
160
+ {
161
+ style: {
162
+ margin: index === normalized.document.blocks.length - 1 ? "0" : `0 0 ${normalized.policy.blockGap}px 0`
163
+ },
164
+ children: block.inline.map((i, idx) => renderInline(i, `b-${index}-${idx}`, props))
165
+ },
166
+ `b-${index}`
167
+ ) : /* @__PURE__ */ jsx(
168
+ "ul",
169
+ {
170
+ style: {
171
+ margin: index === normalized.document.blocks.length - 1 ? "0" : `0 0 ${normalized.policy.blockGap}px 0`,
172
+ paddingLeft: "1.25em"
173
+ },
174
+ children: block.items.map(
175
+ (item, itemIndex) => renderBulletItem(
176
+ item,
177
+ `b-${index}-item-${itemIndex}`,
178
+ props,
179
+ normalized.policy.listGap
180
+ )
181
+ )
182
+ },
183
+ `b-${index}`
184
+ )
185
+ ),
186
+ props.showDiagnostics && normalized.diagnostics.length > 0 ? /* @__PURE__ */ jsx(
187
+ "pre",
188
+ {
189
+ style: {
190
+ margin: `${normalized.policy.blockGap}px 0 0 0`,
191
+ padding: "0.5em",
192
+ fontSize: "11px",
193
+ fontFamily: INLINE_CODE_FONT,
194
+ whiteSpace: "pre-wrap",
195
+ background: "rgba(127, 127, 127, 0.12)"
196
+ },
197
+ children: normalized.diagnostics.map((d) => `${d.code} (${d.line}:${d.column}) ${d.message}`).join("\n")
198
+ }
199
+ ) : null
200
+ ] }) });
201
+ }
202
+
203
+ export {
204
+ MachinaTextView
205
+ };
File without changes
@@ -0,0 +1,66 @@
1
+ // src/errors.ts
2
+ var MachinaLayoutError = class extends Error {
3
+ code;
4
+ constructor(code, message) {
5
+ super(message);
6
+ this.name = "MachinaLayoutError";
7
+ this.code = code;
8
+ }
9
+ };
10
+
11
+ // src/toResolvedTree.ts
12
+ function toResolvedTree(document) {
13
+ const root = document.nodes[document.rootId];
14
+ if (!root) {
15
+ throw new MachinaLayoutError("MissingRoot", `root node '${document.rootId}' is missing`);
16
+ }
17
+ const visiting = /* @__PURE__ */ new Set();
18
+ const visited = /* @__PURE__ */ new Set();
19
+ const build = (node) => {
20
+ if (visiting.has(node.id)) {
21
+ throw new MachinaLayoutError("Cycle", `cycle detected at '${node.id}'`);
22
+ }
23
+ visiting.add(node.id);
24
+ visited.add(node.id);
25
+ const childIds = document.children[node.id] ?? [];
26
+ const children = childIds.map((childId) => {
27
+ const child = document.nodes[childId];
28
+ if (!child) {
29
+ throw new MachinaLayoutError(
30
+ "UnknownParent",
31
+ `missing child node '${childId}' referenced by '${node.id}'`
32
+ );
33
+ }
34
+ return build(child);
35
+ });
36
+ visiting.delete(node.id);
37
+ return {
38
+ id: node.id,
39
+ z: node.z,
40
+ rect: { ...node.rect },
41
+ frame: node.frame,
42
+ arrange: node.arrange,
43
+ view: node.view,
44
+ slot: node.slot,
45
+ debugLabel: node.debugLabel,
46
+ layer: node.layer,
47
+ offset: node.offset,
48
+ children
49
+ };
50
+ };
51
+ const tree = build(root);
52
+ for (const nodeId of Object.keys(document.nodes)) {
53
+ if (!visited.has(nodeId)) {
54
+ throw new MachinaLayoutError(
55
+ "UnreachableNode",
56
+ `node '${nodeId}' is unreachable from root '${document.rootId}'`
57
+ );
58
+ }
59
+ }
60
+ return tree;
61
+ }
62
+
63
+ export {
64
+ MachinaLayoutError,
65
+ toResolvedTree
66
+ };
@@ -0,0 +1,49 @@
1
+ type SetDispatchTable<TState> = {
2
+ events: readonly string[];
3
+ fields: readonly (keyof TState)[];
4
+ values: readonly unknown[];
5
+ };
6
+ type ToggleDispatchTable<TState> = {
7
+ events: readonly string[];
8
+ fields: readonly (keyof TState)[];
9
+ };
10
+ type IncrementDispatchTable<TState> = {
11
+ events: readonly string[];
12
+ fields: readonly (keyof TState)[];
13
+ by?: readonly number[];
14
+ };
15
+ type PrefixSetDispatchTable<TState> = {
16
+ prefixes: readonly string[];
17
+ fields: readonly (keyof TState)[];
18
+ allowedSuffixes?: readonly (readonly string[] | undefined)[];
19
+ };
20
+ type PrefixIncrementDispatchTable<TState> = {
21
+ prefixes: readonly string[];
22
+ fields: readonly (keyof TState)[];
23
+ by?: readonly number[];
24
+ allowedSuffixes?: readonly (readonly string[] | undefined)[];
25
+ };
26
+ type MachinaDispatchTables<TState> = {
27
+ set?: SetDispatchTable<TState>;
28
+ toggle?: ToggleDispatchTable<TState>;
29
+ increment?: IncrementDispatchTable<TState>;
30
+ setSuffix?: PrefixSetDispatchTable<TState>;
31
+ incrementSuffix?: PrefixIncrementDispatchTable<TState>;
32
+ };
33
+
34
+ declare function dispatchEvent<TState extends Record<string, unknown>>(state: TState, event: string, tables: MachinaDispatchTables<TState>): TState;
35
+
36
+ type MachinaDispatchErrorCode = "InvalidDispatchTable" | "InvalidDispatchField" | "InvalidDispatchValue" | "InvalidDispatchEvent";
37
+ declare class MachinaDispatchError extends Error {
38
+ readonly code: MachinaDispatchErrorCode;
39
+ constructor(code: MachinaDispatchErrorCode, message: string);
40
+ }
41
+
42
+ declare function defineDispatchTables<TState>(tables: MachinaDispatchTables<TState>): MachinaDispatchTables<TState>;
43
+ declare function resolveEventValue<TValue>(event: string, table: {
44
+ events: readonly string[];
45
+ values: readonly TValue[];
46
+ }): TValue | undefined;
47
+ declare function matchEventPrefix(event: string, prefix: string, allowedSuffixes?: readonly string[]): string | undefined;
48
+
49
+ export { type IncrementDispatchTable, MachinaDispatchError, type MachinaDispatchErrorCode, type MachinaDispatchTables, type PrefixIncrementDispatchTable, type PrefixSetDispatchTable, type SetDispatchTable, type ToggleDispatchTable, defineDispatchTables, dispatchEvent, matchEventPrefix, resolveEventValue };
@@ -0,0 +1,217 @@
1
+ // src/dispatch/errors.ts
2
+ var MachinaDispatchError = class extends Error {
3
+ code;
4
+ constructor(code, message) {
5
+ super(message);
6
+ this.name = "MachinaDispatchError";
7
+ this.code = code;
8
+ }
9
+ };
10
+
11
+ // src/dispatch/helpers.ts
12
+ var isStringArray = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string");
13
+ var validateLengths = (name, ...columns) => {
14
+ const expected = columns[0]?.length ?? 0;
15
+ if (!columns.every((column) => Array.isArray(column) && column.length === expected)) {
16
+ throw new MachinaDispatchError("InvalidDispatchTable", `${name} column lengths must match`);
17
+ }
18
+ };
19
+ var validateAllowedSuffixes = (allowedSuffixes, expectedLength, tableName) => {
20
+ if (allowedSuffixes === void 0) return;
21
+ if (!Array.isArray(allowedSuffixes) || allowedSuffixes.length !== expectedLength) {
22
+ throw new MachinaDispatchError(
23
+ "InvalidDispatchTable",
24
+ `${tableName}.allowedSuffixes length mismatch`
25
+ );
26
+ }
27
+ for (const row of allowedSuffixes) {
28
+ if (row !== void 0 && !isStringArray(row)) {
29
+ throw new MachinaDispatchError(
30
+ "InvalidDispatchTable",
31
+ `${tableName}.allowedSuffixes must be string arrays`
32
+ );
33
+ }
34
+ }
35
+ };
36
+ var validateBy = (by, expectedLength, tableName) => {
37
+ if (by === void 0) return;
38
+ if (!Array.isArray(by) || by.length !== expectedLength) {
39
+ throw new MachinaDispatchError("InvalidDispatchTable", `${tableName}.by length mismatch`);
40
+ }
41
+ for (const value of by) {
42
+ if (typeof value !== "number" || !Number.isFinite(value)) {
43
+ throw new MachinaDispatchError(
44
+ "InvalidDispatchTable",
45
+ `${tableName}.by must contain finite numbers`
46
+ );
47
+ }
48
+ }
49
+ };
50
+ var validateSetTable = (table) => {
51
+ validateLengths("set", table.events, table.fields, table.values);
52
+ if (!isStringArray(table.events))
53
+ throw new MachinaDispatchError("InvalidDispatchTable", "set.events must be strings");
54
+ };
55
+ var validateToggleTable = (table) => {
56
+ validateLengths("toggle", table.events, table.fields);
57
+ if (!isStringArray(table.events))
58
+ throw new MachinaDispatchError("InvalidDispatchTable", "toggle.events must be strings");
59
+ };
60
+ var validateIncrementTable = (table) => {
61
+ validateLengths("increment", table.events, table.fields);
62
+ if (!isStringArray(table.events))
63
+ throw new MachinaDispatchError("InvalidDispatchTable", "increment.events must be strings");
64
+ validateBy(table.by, table.events.length, "increment");
65
+ };
66
+ var validatePrefixSetTable = (table) => {
67
+ validateLengths("setSuffix", table.prefixes, table.fields);
68
+ if (!isStringArray(table.prefixes))
69
+ throw new MachinaDispatchError("InvalidDispatchTable", "setSuffix.prefixes must be strings");
70
+ validateAllowedSuffixes(table.allowedSuffixes, table.prefixes.length, "setSuffix");
71
+ };
72
+ var validatePrefixIncrementTable = (table) => {
73
+ validateLengths("incrementSuffix", table.prefixes, table.fields);
74
+ if (!isStringArray(table.prefixes))
75
+ throw new MachinaDispatchError(
76
+ "InvalidDispatchTable",
77
+ "incrementSuffix.prefixes must be strings"
78
+ );
79
+ validateBy(table.by, table.prefixes.length, "incrementSuffix");
80
+ validateAllowedSuffixes(table.allowedSuffixes, table.prefixes.length, "incrementSuffix");
81
+ };
82
+ function defineDispatchTables(tables) {
83
+ if (tables.set) validateSetTable(tables.set);
84
+ if (tables.toggle) validateToggleTable(tables.toggle);
85
+ if (tables.increment) validateIncrementTable(tables.increment);
86
+ if (tables.setSuffix) validatePrefixSetTable(tables.setSuffix);
87
+ if (tables.incrementSuffix) validatePrefixIncrementTable(tables.incrementSuffix);
88
+ return tables;
89
+ }
90
+ function resolveEventValue(event, table) {
91
+ validateLengths("resolveEventValue", table.events, table.values);
92
+ if (!isStringArray(table.events))
93
+ throw new MachinaDispatchError("InvalidDispatchTable", "events must be strings");
94
+ for (let i = 0; i < table.events.length; i += 1) {
95
+ if (table.events[i] === event) return table.values[i];
96
+ }
97
+ return void 0;
98
+ }
99
+ function matchEventPrefix(event, prefix, allowedSuffixes) {
100
+ if (typeof event !== "string" || typeof prefix !== "string") {
101
+ throw new MachinaDispatchError("InvalidDispatchEvent", "event and prefix must be strings");
102
+ }
103
+ if (allowedSuffixes !== void 0 && !isStringArray(allowedSuffixes)) {
104
+ throw new MachinaDispatchError(
105
+ "InvalidDispatchTable",
106
+ "allowedSuffixes must be a string array"
107
+ );
108
+ }
109
+ if (!event.startsWith(prefix)) return void 0;
110
+ const suffix = event.slice(prefix.length);
111
+ if (allowedSuffixes && !allowedSuffixes.includes(suffix)) return void 0;
112
+ return suffix;
113
+ }
114
+
115
+ // src/dispatch/dispatchEvent.ts
116
+ var hasOwn = (state, field) => Object.hasOwn(state, field);
117
+ function dispatchEvent(state, event, tables) {
118
+ if (typeof event !== "string") {
119
+ throw new MachinaDispatchError("InvalidDispatchEvent", "event must be a string");
120
+ }
121
+ const base = state;
122
+ if (tables.set) {
123
+ validateSetTable(tables.set);
124
+ for (let i = 0; i < tables.set.events.length; i += 1) {
125
+ if (tables.set.events[i] !== event) continue;
126
+ const field = tables.set.fields[i];
127
+ if (!hasOwn(base, field))
128
+ throw new MachinaDispatchError("InvalidDispatchField", `missing field: ${String(field)}`);
129
+ const nextValue = tables.set.values[i];
130
+ if (Object.is(base[field], nextValue)) return state;
131
+ return { ...state, [field]: nextValue };
132
+ }
133
+ }
134
+ if (tables.toggle) {
135
+ validateToggleTable(tables.toggle);
136
+ for (let i = 0; i < tables.toggle.events.length; i += 1) {
137
+ if (tables.toggle.events[i] !== event) continue;
138
+ const field = tables.toggle.fields[i];
139
+ if (!hasOwn(base, field))
140
+ throw new MachinaDispatchError("InvalidDispatchField", `missing field: ${String(field)}`);
141
+ const current = base[field];
142
+ if (typeof current !== "boolean")
143
+ throw new MachinaDispatchError(
144
+ "InvalidDispatchValue",
145
+ `field must be boolean: ${String(field)}`
146
+ );
147
+ return { ...state, [field]: !current };
148
+ }
149
+ }
150
+ if (tables.increment) {
151
+ validateIncrementTable(tables.increment);
152
+ for (let i = 0; i < tables.increment.events.length; i += 1) {
153
+ if (tables.increment.events[i] !== event) continue;
154
+ const field = tables.increment.fields[i];
155
+ if (!hasOwn(base, field))
156
+ throw new MachinaDispatchError("InvalidDispatchField", `missing field: ${String(field)}`);
157
+ const current = base[field];
158
+ if (typeof current !== "number")
159
+ throw new MachinaDispatchError(
160
+ "InvalidDispatchValue",
161
+ `field must be number: ${String(field)}`
162
+ );
163
+ const delta = tables.increment.by?.[i] ?? 1;
164
+ if (!Number.isFinite(delta))
165
+ throw new MachinaDispatchError("InvalidDispatchValue", "increment delta must be finite");
166
+ return { ...state, [field]: current + delta };
167
+ }
168
+ }
169
+ if (tables.setSuffix) {
170
+ validatePrefixSetTable(tables.setSuffix);
171
+ for (let i = 0; i < tables.setSuffix.prefixes.length; i += 1) {
172
+ const suffix = matchEventPrefix(
173
+ event,
174
+ tables.setSuffix.prefixes[i],
175
+ tables.setSuffix.allowedSuffixes?.[i]
176
+ );
177
+ if (suffix === void 0) continue;
178
+ const field = tables.setSuffix.fields[i];
179
+ if (!hasOwn(base, field))
180
+ throw new MachinaDispatchError("InvalidDispatchField", `missing field: ${String(field)}`);
181
+ if (Object.is(base[field], suffix)) return state;
182
+ return { ...state, [field]: suffix };
183
+ }
184
+ }
185
+ if (tables.incrementSuffix) {
186
+ validatePrefixIncrementTable(tables.incrementSuffix);
187
+ for (let i = 0; i < tables.incrementSuffix.prefixes.length; i += 1) {
188
+ const suffix = matchEventPrefix(
189
+ event,
190
+ tables.incrementSuffix.prefixes[i],
191
+ tables.incrementSuffix.allowedSuffixes?.[i]
192
+ );
193
+ if (suffix === void 0) continue;
194
+ const field = tables.incrementSuffix.fields[i];
195
+ if (!hasOwn(base, field))
196
+ throw new MachinaDispatchError("InvalidDispatchField", `missing field: ${String(field)}`);
197
+ const current = base[field];
198
+ if (typeof current !== "number")
199
+ throw new MachinaDispatchError(
200
+ "InvalidDispatchValue",
201
+ `field must be number: ${String(field)}`
202
+ );
203
+ const delta = tables.incrementSuffix.by?.[i] ?? 1;
204
+ if (!Number.isFinite(delta))
205
+ throw new MachinaDispatchError("InvalidDispatchValue", "increment delta must be finite");
206
+ return { ...state, [field]: current + delta };
207
+ }
208
+ }
209
+ return state;
210
+ }
211
+ export {
212
+ MachinaDispatchError,
213
+ defineDispatchTables,
214
+ dispatchEvent,
215
+ matchEventPrefix,
216
+ resolveEventValue
217
+ };