@cosmicdrift/kumiko-headless 0.327.0 → 0.328.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.
Files changed (109) hide show
  1. package/dist/apex/css.d.ts +3 -0
  2. package/{src/apex/css.ts → dist/apex/css.js} +11 -24
  3. package/dist/apex/index.d.ts +229 -0
  4. package/dist/apex/index.js +360 -0
  5. package/dist/apex/lightbox.d.ts +4 -0
  6. package/{src/apex/lightbox.ts → dist/apex/lightbox.js} +0 -4
  7. package/dist/contracts/asset.d.ts +15 -0
  8. package/dist/contracts/asset.js +17 -0
  9. package/dist/contracts/index.d.ts +3 -0
  10. package/dist/contracts/index.js +1 -0
  11. package/dist/contracts/locale.d.ts +7 -0
  12. package/dist/contracts/locale.js +11 -0
  13. package/dist/contracts/primitives.d.ts +93 -0
  14. package/dist/contracts/primitives.js +21 -0
  15. package/dist/dispatcher/index.d.ts +2 -0
  16. package/dist/dispatcher/index.js +1 -0
  17. package/dist/dispatcher/types.d.ts +86 -0
  18. package/dist/dispatcher/types.js +34 -0
  19. package/dist/form/form-controller.d.ts +2 -0
  20. package/dist/form/form-controller.js +404 -0
  21. package/dist/form/index.d.ts +3 -0
  22. package/dist/form/index.js +2 -0
  23. package/dist/form/types.d.ts +71 -0
  24. package/dist/form/types.js +1 -0
  25. package/dist/form/zod-bridge.d.ts +5 -0
  26. package/{src/form/zod-bridge.ts → dist/form/zod-bridge.js} +52 -58
  27. package/dist/format/escape.d.ts +5 -0
  28. package/{src/format/escape.ts → dist/format/escape.js} +43 -47
  29. package/dist/format/html-template.d.ts +9 -0
  30. package/dist/format/html-template.js +39 -0
  31. package/dist/format/index.d.ts +10 -0
  32. package/dist/format/index.js +192 -0
  33. package/dist/format/money.d.ts +1 -0
  34. package/{src/format/money.ts → dist/format/money.js} +6 -5
  35. package/dist/index.d.ts +14 -0
  36. package/dist/index.js +9 -0
  37. package/dist/locale-routing/index.d.ts +39 -0
  38. package/dist/locale-routing/index.js +143 -0
  39. package/dist/nav/index.d.ts +2 -0
  40. package/dist/nav/index.js +1 -0
  41. package/dist/nav/resolve.d.ts +2 -0
  42. package/dist/nav/resolve.js +58 -0
  43. package/dist/nav/types.d.ts +26 -0
  44. package/dist/nav/types.js +1 -0
  45. package/dist/store/create-store.d.ts +2 -0
  46. package/{src/store/create-store.ts → dist/store/create-store.js} +21 -23
  47. package/dist/store/equality.d.ts +1 -0
  48. package/{src/store/equality.ts → dist/store/equality.js} +19 -17
  49. package/dist/store/index.d.ts +3 -0
  50. package/dist/store/index.js +2 -0
  51. package/dist/store/types.d.ts +7 -0
  52. package/dist/store/types.js +10 -0
  53. package/dist/view-model/edit.d.ts +10 -0
  54. package/dist/view-model/edit.js +392 -0
  55. package/dist/view-model/embedded-list.d.ts +27 -0
  56. package/dist/view-model/embedded-list.js +63 -0
  57. package/dist/view-model/index.d.ts +7 -0
  58. package/dist/view-model/index.js +3 -0
  59. package/dist/view-model/list.d.ts +16 -0
  60. package/dist/view-model/list.js +201 -0
  61. package/dist/view-model/types.d.ts +300 -0
  62. package/dist/view-model/types.js +1 -0
  63. package/package.json +31 -12
  64. package/src/changes.json +7 -0
  65. package/src/apex/__tests__/lightbox.test.ts +0 -20
  66. package/src/apex/__tests__/page-head.test.ts +0 -63
  67. package/src/apex/__tests__/render.test.ts +0 -401
  68. package/src/apex/index.ts +0 -667
  69. package/src/contracts/__tests__/contracts.test.ts +0 -123
  70. package/src/contracts/__tests__/field-issue-compat.test.ts +0 -19
  71. package/src/contracts/asset.ts +0 -48
  72. package/src/contracts/index.ts +0 -23
  73. package/src/contracts/locale.ts +0 -33
  74. package/src/contracts/primitives.ts +0 -158
  75. package/src/dispatcher/__tests__/contract.test.ts +0 -163
  76. package/src/dispatcher/index.ts +0 -16
  77. package/src/dispatcher/types.ts +0 -217
  78. package/src/form/__tests__/conditional-fields.test.ts +0 -177
  79. package/src/form/__tests__/form-controller.test.ts +0 -231
  80. package/src/form/__tests__/submit.test.ts +0 -575
  81. package/src/form/__tests__/validation.test.ts +0 -274
  82. package/src/form/form-controller.ts +0 -458
  83. package/src/form/index.ts +0 -15
  84. package/src/form/types.ts +0 -297
  85. package/src/format/__tests__/escape.test.ts +0 -79
  86. package/src/format/__tests__/format.test.ts +0 -281
  87. package/src/format/__tests__/html-template.test.ts +0 -47
  88. package/src/format/html-template.ts +0 -49
  89. package/src/format/index.ts +0 -219
  90. package/src/index.ts +0 -118
  91. package/src/locale-routing/__tests__/locale-routing.test.ts +0 -276
  92. package/src/locale-routing/index.ts +0 -218
  93. package/src/nav/__tests__/resolve.test.ts +0 -279
  94. package/src/nav/index.ts +0 -8
  95. package/src/nav/resolve.ts +0 -60
  96. package/src/nav/types.ts +0 -70
  97. package/src/store/__tests__/create-store.test.ts +0 -139
  98. package/src/store/__tests__/equality.test.ts +0 -66
  99. package/src/store/index.ts +0 -3
  100. package/src/store/types.ts +0 -27
  101. package/src/view-model/__tests__/edit.test.ts +0 -1072
  102. package/src/view-model/__tests__/embedded-list.test.ts +0 -96
  103. package/src/view-model/__tests__/field-labels.test.ts +0 -62
  104. package/src/view-model/__tests__/list.test.ts +0 -428
  105. package/src/view-model/edit.ts +0 -641
  106. package/src/view-model/embedded-list.ts +0 -89
  107. package/src/view-model/index.ts +0 -43
  108. package/src/view-model/list.ts +0 -277
  109. package/src/view-model/types.ts +0 -392
@@ -1,274 +0,0 @@
1
- import { describe, expect, mock, test } from "bun:test";
2
- import * as z from "zod";
3
- import { createFormController } from "../form-controller";
4
- import { groupIssuesByPath, zodErrorToFieldIssues } from "../zod-bridge";
5
-
6
- describe("zodErrorToFieldIssues", () => {
7
- test("flattens zod issues to FieldIssue with dotted paths", () => {
8
- const schema = z.object({
9
- title: z.string().min(1),
10
- address: z.object({ city: z.string().min(1) }),
11
- tags: z.array(z.string().min(1)),
12
- });
13
- const result = schema.safeParse({ title: "", address: { city: "" }, tags: ["ok", ""] });
14
-
15
- expect(result.success).toBe(false);
16
- if (result.success) return;
17
- const issues = zodErrorToFieldIssues(result.error);
18
-
19
- const paths = issues.map((i) => i.path).sort();
20
- expect(paths).toContain("title");
21
- expect(paths).toContain("address.city");
22
- expect(paths).toContain("tags.1");
23
- });
24
-
25
- test("top-level issues get path='(root)' — matches server zod-bridge", () => {
26
- const schema = z.object({ foo: z.string() });
27
- // Pass a non-object → zod raises an issue with path=[].
28
- const result = schema.safeParse("not-an-object");
29
-
30
- expect(result.success).toBe(false);
31
- if (result.success) return;
32
- const issues = zodErrorToFieldIssues(result.error);
33
-
34
- expect(issues[0]?.path).toBe("(root)");
35
- });
36
-
37
- test("surfaces zod params (minimum/maximum/expected) under issue.params", () => {
38
- const schema = z.object({ count: z.number().min(10).max(100) });
39
- const result = schema.safeParse({ count: 3 });
40
-
41
- expect(result.success).toBe(false);
42
- if (result.success) return;
43
- const issues = zodErrorToFieldIssues(result.error);
44
-
45
- expect(issues[0]?.params).toBeDefined();
46
- expect(issues[0]?.params?.["minimum"]).toBe(10);
47
- });
48
-
49
- test("custom issue with params.i18nKey overrides the mechanical errors.validation.custom key", () => {
50
- const schema = z.object({ name: z.string() }).superRefine((values, ctx) => {
51
- if (values.name === "") {
52
- ctx.addIssue({
53
- code: "custom",
54
- path: ["name"],
55
- message: '"name" is required.',
56
- params: { i18nKey: "kumiko.validation.required" },
57
- });
58
- }
59
- });
60
- const result = schema.safeParse({ name: "" });
61
-
62
- expect(result.success).toBe(false);
63
- if (result.success) return;
64
- const issues = zodErrorToFieldIssues(result.error);
65
-
66
- expect(issues[0]?.i18nKey).toBe("kumiko.validation.required");
67
- });
68
-
69
- test("custom issue without params.i18nKey still falls back to errors.validation.custom", () => {
70
- const schema = z.object({ name: z.string() }).superRefine((values, ctx) => {
71
- if (values.name === "bad") {
72
- ctx.addIssue({ code: "custom", path: ["name"], message: "not allowed" });
73
- }
74
- });
75
- const result = schema.safeParse({ name: "bad" });
76
-
77
- expect(result.success).toBe(false);
78
- if (result.success) return;
79
- const issues = zodErrorToFieldIssues(result.error);
80
-
81
- expect(issues[0]?.i18nKey).toBe("errors.validation.custom");
82
- });
83
- });
84
-
85
- describe("groupIssuesByPath", () => {
86
- test("groups multiple issues on the same path into one bucket", () => {
87
- const grouped = groupIssuesByPath([
88
- { path: "title", code: "too_small", i18nKey: "x" },
89
- { path: "title", code: "invalid_format", i18nKey: "x" },
90
- { path: "age", code: "invalid_type", i18nKey: "x" },
91
- ]);
92
-
93
- expect(grouped["title"]).toHaveLength(2);
94
- expect(grouped["age"]).toHaveLength(1);
95
- });
96
- });
97
-
98
- describe("createFormController — validate()", () => {
99
- test("without a schema: validate() is a no-op that returns true", () => {
100
- const form = createFormController({ initial: { title: "" } });
101
- const listener = mock();
102
- form.subscribe(listener);
103
-
104
- const ok = form.validate();
105
-
106
- expect(ok).toBe(true);
107
- expect(form.getSnapshot().errors).toEqual({});
108
- expect(listener).not.toHaveBeenCalled(); // no-op
109
- });
110
-
111
- test("with a schema: validate() runs it and populates errors on failure", () => {
112
- const schema = z.object({ title: z.string().min(3), age: z.number().int() });
113
- const form = createFormController({
114
- initial: { title: "a", age: 1.5 },
115
- schema,
116
- });
117
-
118
- const ok = form.validate();
119
- const snap = form.getSnapshot();
120
-
121
- expect(ok).toBe(false);
122
- expect(snap.errors["title"]).toBeDefined();
123
- expect(snap.errors["age"]).toBeDefined();
124
- });
125
-
126
- test("validate() returns true when values match the schema", () => {
127
- const schema = z.object({ title: z.string().min(1) });
128
- const form = createFormController({ initial: { title: "hello" }, schema });
129
-
130
- expect(form.validate()).toBe(true);
131
- expect(form.getSnapshot().errors).toEqual({});
132
- });
133
-
134
- test("validate() clears previous errors on subsequent success", () => {
135
- // Common flow: user submits, sees errors, fixes fields, hits validate
136
- // again — old errors must disappear.
137
- const schema = z.object({ title: z.string().min(3) });
138
- const form = createFormController({ initial: { title: "a" }, schema });
139
-
140
- form.validate();
141
- expect(form.getSnapshot().errors["title"]).toBeDefined();
142
-
143
- form.setField("title", "hello");
144
- form.validate();
145
-
146
- expect(form.getSnapshot().errors).toEqual({});
147
- });
148
-
149
- test("validate() with nested values: errors keyed by dotted path", () => {
150
- const schema = z.object({
151
- address: z.object({ city: z.string().min(1) }),
152
- });
153
- const form = createFormController({ initial: { address: { city: "" } }, schema });
154
-
155
- form.validate();
156
-
157
- expect(form.getSnapshot().errors["address.city"]).toBeDefined();
158
- });
159
- });
160
-
161
- describe("createFormController — validate(scope)", () => {
162
- test("scoped run reports only fields inside scope, drops issues outside it", () => {
163
- const schema = z.object({
164
- a: z.string().min(3),
165
- b: z.string().min(3),
166
- });
167
- const form = createFormController({ initial: { a: "x", b: "y" }, schema });
168
-
169
- const ok = form.validate(["a"]);
170
- const snap = form.getSnapshot();
171
-
172
- expect(ok).toBe(false);
173
- expect(snap.errors["a"]).toBeDefined();
174
- expect(snap.errors["b"]).toBeUndefined();
175
- });
176
-
177
- test("scoped run returns true when every field in scope is valid, even if fields outside scope are not", () => {
178
- const schema = z.object({
179
- a: z.string().min(1),
180
- b: z.string().min(3),
181
- });
182
- const form = createFormController({ initial: { a: "ok", b: "y" }, schema });
183
-
184
- const ok = form.validate(["a"]);
185
-
186
- expect(ok).toBe(true);
187
- expect(form.getSnapshot().errors).toEqual({});
188
- });
189
-
190
- test("nested scope path matches issue root segment (#1898)", () => {
191
- const schema = z.object({
192
- address: z.object({ city: z.string().min(1) }),
193
- });
194
- const form = createFormController({
195
- initial: { address: { city: "" } },
196
- schema,
197
- });
198
- expect(form.validate(["address.city"])).toBe(false);
199
- expect(form.getSnapshot().errors["address.city"]).toBeDefined();
200
- });
201
-
202
- // Hard rule (kumiko-framework#1885): an object-level .refine() issue has
203
- // path "(root)", which never matches a field name — every scoped
204
- // validate() call must ignore it, and only the unscoped final-submit
205
- // validate() may surface it. Otherwise a step-boundary check would block
206
- // a wizard step forever on an invariant no single step owns.
207
- describe("root-path .refine() issues", () => {
208
- const schema = z
209
- .object({
210
- start: z.string(),
211
- end: z.string(),
212
- })
213
- .refine((v) => v.end >= v.start, { message: "end must be >= start" });
214
-
215
- test("scoped validate() ignores the root issue regardless of which fields are in scope", () => {
216
- const form = createFormController({ initial: { start: "b", end: "a" }, schema });
217
-
218
- expect(form.validate(["start"])).toBe(true);
219
- expect(form.validate(["end"])).toBe(true);
220
- expect(form.validate(["start", "end"])).toBe(true);
221
- expect(form.getSnapshot().errors).toEqual({});
222
- });
223
-
224
- test("unscoped validate() (final submit) reports the root issue under '(root)'", () => {
225
- const form = createFormController({ initial: { start: "b", end: "a" }, schema });
226
-
227
- const ok = form.validate();
228
-
229
- expect(ok).toBe(false);
230
- expect(form.getSnapshot().errors["(root)"]).toBeDefined();
231
- });
232
- });
233
-
234
- test("hidden fields stay filtered in scoped mode even when the hidden field is itself in scope", () => {
235
- // Discriminating case: field `b` is both hidden AND inside `scope`, so
236
- // the assertion actually exercises the hidden-field filter — not just
237
- // the scope filter dropping an out-of-scope field.
238
- const schema = z.object({
239
- a: z.string().min(3),
240
- b: z.string().min(3),
241
- });
242
- const form = createFormController({
243
- initial: { a: "x", b: "y", showB: false },
244
- schema,
245
- fields: { b: { visible: (values) => values.showB } },
246
- });
247
-
248
- const ok = form.validate(["a", "b"]);
249
- const snap = form.getSnapshot();
250
-
251
- expect(ok).toBe(false);
252
- expect(snap.errors["a"]).toBeDefined();
253
- expect(snap.errors["b"]).toBeUndefined();
254
- });
255
-
256
- test("hidden fields stay filtered in unscoped mode (regression guard alongside the scoped case)", () => {
257
- const schema = z.object({
258
- a: z.string().min(3),
259
- b: z.string().min(3),
260
- });
261
- const form = createFormController({
262
- initial: { a: "x", b: "y", showB: false },
263
- schema,
264
- fields: { b: { visible: (values) => values.showB } },
265
- });
266
-
267
- const ok = form.validate();
268
- const snap = form.getSnapshot();
269
-
270
- expect(ok).toBe(false);
271
- expect(snap.errors["a"]).toBeDefined();
272
- expect(snap.errors["b"]).toBeUndefined();
273
- });
274
- });