@cosmicdrift/kumiko-headless 0.192.0 → 0.193.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-headless",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.193.1",
|
|
4
4
|
"description": "Headless UI logic for Kumiko — Dispatcher contract, Form-Controller, View-Model, Nav-Resolver. Plattform- und React-frei; jeder Renderer (renderer, renderer-web, renderer-native, …) komponiert darauf.",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
}
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@cosmicdrift/kumiko-framework": "0.
|
|
39
|
+
"@cosmicdrift/kumiko-framework": "0.193.1",
|
|
40
40
|
"temporal-polyfill": "^0.3.2",
|
|
41
41
|
"zod": "^4.4.3"
|
|
42
42
|
},
|
|
@@ -214,6 +214,23 @@ describe("createFormController — submit()", () => {
|
|
|
214
214
|
expect(disp.writeSpy).toHaveBeenCalledWith("app:write:task:create", { note: "" });
|
|
215
215
|
});
|
|
216
216
|
|
|
217
|
+
test("payloadMode: 'values' with stripEmptySeeds: false — keeps an untouched empty-string field", async () => {
|
|
218
|
+
// Same setup as the strip test above, but the caller opts out — the
|
|
219
|
+
// untouched "" seed must survive unchanged in the payload.
|
|
220
|
+
const disp = makeDispatcher();
|
|
221
|
+
const form = createFormController({
|
|
222
|
+
initial: { title: "hello", dueDate: "" },
|
|
223
|
+
submit: { dispatcher: disp, type: "app:write:task:create", stripEmptySeeds: false },
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
await form.submit();
|
|
227
|
+
|
|
228
|
+
expect(disp.writeSpy).toHaveBeenCalledWith("app:write:task:create", {
|
|
229
|
+
title: "hello",
|
|
230
|
+
dueDate: "",
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
|
|
217
234
|
test("stale-submit race: edits during the in-flight write stay dirty after success", async () => {
|
|
218
235
|
// User submits "hello", the network takes 50ms. During those 50ms the
|
|
219
236
|
// user types "world" into the same field. The server sees "hello"
|
|
@@ -325,6 +325,8 @@ export function createFormController<TValues extends FormValues, TCtx = unknown>
|
|
|
325
325
|
};
|
|
326
326
|
}
|
|
327
327
|
payload = submittedSnapshot.changes;
|
|
328
|
+
} else if (submitCfg.stripEmptySeeds === false) {
|
|
329
|
+
payload = submittedValues;
|
|
328
330
|
} else {
|
|
329
331
|
payload = stripUntouchedEmptyStrings(submittedValues, submittedSnapshot.initial);
|
|
330
332
|
}
|
package/src/form/types.ts
CHANGED
|
@@ -231,8 +231,14 @@ export type FormControllerOptions<TValues extends FormValues, TCtx = unknown> =
|
|
|
231
231
|
// objects — but that assumes an update flow. For creates, `changes ===
|
|
232
232
|
// values` in practice because initial is empty.
|
|
233
233
|
//
|
|
234
|
-
// - "values" — send the
|
|
234
|
+
// - "values" — send the current `values` object. Right for create
|
|
235
235
|
// handlers whose schema expects a full entity payload.
|
|
236
|
+
// Untouched fields that are still "" (the seed
|
|
237
|
+
// buildInitialValues gives controlled optional inputs)
|
|
238
|
+
// are stripped from the payload before it's sent, because
|
|
239
|
+
// `.optional()` server schemas accept `undefined` but not
|
|
240
|
+
// `""`. Set `stripEmptySeeds: false` on SubmitConfig to
|
|
241
|
+
// opt out and send those fields as `""` unchanged.
|
|
236
242
|
// - "changes" — send only the `changes` delta. Right for update
|
|
237
243
|
// handlers; noop when the form is un-dirty (submit
|
|
238
244
|
// short-circuits into a no-network success).
|
|
@@ -246,6 +252,12 @@ export type SubmitConfig<TValues extends FormValues = FormValues> = {
|
|
|
246
252
|
// Qualified write-handler name (e.g. "orders:write:order:create").
|
|
247
253
|
readonly type: string;
|
|
248
254
|
readonly payloadMode?: SubmitPayloadMode;
|
|
255
|
+
/** Only applies to payloadMode "values". Controls whether untouched
|
|
256
|
+
* empty-string fields (still equal to their initial "" seed) are
|
|
257
|
+
* stripped from the payload before dispatch — see SubmitPayloadMode's
|
|
258
|
+
* "values" doc for why the stripping exists. Default `true` (current
|
|
259
|
+
* behavior). Set `false` to send those fields as `""` unchanged. */
|
|
260
|
+
readonly stripEmptySeeds?: boolean;
|
|
249
261
|
// Optional payload transformer — overrides payloadMode. Used for
|
|
250
262
|
// nested-writes: the submit path calls buildPayload(snapshot) once at
|
|
251
263
|
// submit-time and sends the result. The snapshot is the one captured
|