@shwfed/config 2.3.3 → 2.3.5
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/module.json +1 -1
- package/dist/runtime/components/actions/components/group.d.vue.ts +12 -10
- package/dist/runtime/components/actions/components/group.vue +47 -13
- package/dist/runtime/components/actions/components/group.vue.d.ts +12 -10
- package/dist/runtime/components/actions/config.d.vue.ts +13 -11
- package/dist/runtime/components/actions/config.vue +252 -35
- package/dist/runtime/components/actions/config.vue.d.ts +13 -11
- package/dist/runtime/components/actions/items/2026-05-21/com.shwfed.actions.item.markdown/config.d.vue.ts +37 -0
- package/dist/runtime/components/actions/items/2026-05-21/com.shwfed.actions.item.markdown/config.vue +61 -0
- package/dist/runtime/components/actions/items/2026-05-21/com.shwfed.actions.item.markdown/config.vue.d.ts +37 -0
- package/dist/runtime/components/actions/items/2026-05-21/com.shwfed.actions.item.markdown/runtime.d.vue.ts +7 -0
- package/dist/runtime/components/actions/items/2026-05-21/com.shwfed.actions.item.markdown/runtime.vue +22 -0
- package/dist/runtime/components/actions/items/2026-05-21/com.shwfed.actions.item.markdown/runtime.vue.d.ts +7 -0
- package/dist/runtime/components/actions/items/2026-05-21/com.shwfed.actions.item.markdown/schema.d.ts +23 -0
- package/dist/runtime/components/actions/items/2026-05-21/com.shwfed.actions.item.markdown/schema.js +29 -0
- package/dist/runtime/components/actions/schema.d.ts +400 -193
- package/dist/runtime/components/actions/schema.js +22 -4
- package/dist/runtime/components/actions/utils/resolve.d.ts +15 -0
- package/dist/runtime/components/actions/utils/resolve.js +45 -0
- package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.actions/config.d.vue.ts +10 -8
- package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.actions/config.vue.d.ts +10 -8
- package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.actions/runtime.d.vue.ts +10 -8
- package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.actions/runtime.vue.d.ts +10 -8
- package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.actions/schema.d.ts +115 -57
- package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/config.d.vue.ts +10 -8
- package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/config.vue.d.ts +10 -8
- package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/runtime.d.vue.ts +10 -8
- package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/runtime.vue.d.ts +10 -8
- package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/schema.d.ts +120 -61
- package/dist/runtime/components/form/fields/2026-04-24/com.shwfed.form.field.actions/config.d.vue.ts +10 -8
- package/dist/runtime/components/form/fields/2026-04-24/com.shwfed.form.field.actions/config.vue.d.ts +10 -8
- package/dist/runtime/components/form/fields/2026-04-24/com.shwfed.form.field.actions/schema.d.ts +115 -57
- package/dist/runtime/components/table/columns/2026-04-14/com.shwfed.table.column.actions/schema.d.ts +115 -57
- package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.combobox-single/config.d.vue.ts +2 -2
- package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.combobox-single/config.vue.d.ts +2 -2
- package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.combobox-single/runtime.vue +4 -4
- package/dist/runtime/components/table/schema.d.ts +125 -65
- package/dist/runtime/components/table/utils/row-reorder.js +16 -5
- package/dist/runtime/vendor/cel-js/CLAUDE.md +5 -1
- package/dist/runtime/vendor/cel-js/PROMPT.md +19 -0
- package/dist/runtime/vendor/cel-js/lib/evaluator.js +2 -0
- package/dist/runtime/vendor/cel-js/lib/form-builtins.d.ts +2 -0
- package/dist/runtime/vendor/cel-js/lib/form-builtins.js +58 -0
- package/package.json +1 -1
|
@@ -132,7 +132,7 @@ export declare function TableConfig(configure: (env: Environment) => void): Sche
|
|
|
132
132
|
readonly size: "default" | "sm" | "xs";
|
|
133
133
|
readonly style?: string | undefined;
|
|
134
134
|
readonly gap: number;
|
|
135
|
-
readonly items: readonly ({
|
|
135
|
+
readonly items: readonly (import("../actions/schema.js").RegistryItemValue | {
|
|
136
136
|
readonly disabled?: string | undefined;
|
|
137
137
|
readonly id: string;
|
|
138
138
|
readonly title: readonly [{
|
|
@@ -165,8 +165,7 @@ export declare function TableConfig(configure: (env: Environment) => void): Sche
|
|
|
165
165
|
readonly message: string;
|
|
166
166
|
}[]];
|
|
167
167
|
readonly icon?: string | undefined;
|
|
168
|
-
readonly
|
|
169
|
-
readonly items: readonly {
|
|
168
|
+
readonly items: readonly (import("../actions/schema.js").RegistrySubItemValue | {
|
|
170
169
|
readonly disabled?: string | undefined;
|
|
171
170
|
readonly id: string;
|
|
172
171
|
readonly title: readonly [{
|
|
@@ -187,7 +186,9 @@ export declare function TableConfig(configure: (env: Environment) => void): Sche
|
|
|
187
186
|
}[]] | undefined;
|
|
188
187
|
readonly variant?: "default" | "link" | "destructive" | "primary" | "ghost" | undefined;
|
|
189
188
|
readonly action?: any;
|
|
190
|
-
}[];
|
|
189
|
+
})[];
|
|
190
|
+
readonly groupId: string;
|
|
191
|
+
readonly hideTitle?: boolean | undefined;
|
|
191
192
|
})[];
|
|
192
193
|
readonly groups: readonly {
|
|
193
194
|
readonly id: string;
|
|
@@ -310,63 +311,121 @@ export declare function TableConfig(configure: (env: Environment) => void): Sche
|
|
|
310
311
|
id: Schema.refine<string, typeof Schema.String>;
|
|
311
312
|
variant: Schema.optional<Schema.Literal<["default", "primary", "destructive", "ghost", "link"]>>;
|
|
312
313
|
}>>;
|
|
313
|
-
items: Schema.Array$<Schema.
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
title:
|
|
317
|
-
locale:
|
|
318
|
-
message:
|
|
319
|
-
}
|
|
320
|
-
locale:
|
|
321
|
-
message:
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
}
|
|
337
|
-
id:
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
id:
|
|
349
|
-
title:
|
|
350
|
-
locale:
|
|
351
|
-
message:
|
|
352
|
-
}
|
|
353
|
-
locale:
|
|
354
|
-
message:
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
314
|
+
items: Schema.Array$<Schema.Schema<import("../actions/schema.js").RegistryItemValue | {
|
|
315
|
+
readonly disabled?: string | undefined;
|
|
316
|
+
readonly id: string;
|
|
317
|
+
readonly title: readonly [{
|
|
318
|
+
readonly locale: "zh";
|
|
319
|
+
readonly message: string;
|
|
320
|
+
}, ...{
|
|
321
|
+
readonly locale: "en" | "ja" | "ko";
|
|
322
|
+
readonly message: string;
|
|
323
|
+
}[]];
|
|
324
|
+
readonly icon?: string | undefined;
|
|
325
|
+
readonly hidden?: string | undefined;
|
|
326
|
+
readonly tooltip?: readonly [{
|
|
327
|
+
readonly locale: "zh";
|
|
328
|
+
readonly message: string;
|
|
329
|
+
}, ...{
|
|
330
|
+
readonly locale: "en" | "ja" | "ko";
|
|
331
|
+
readonly message: string;
|
|
332
|
+
}[]] | undefined;
|
|
333
|
+
readonly variant?: "default" | "link" | "destructive" | "primary" | "ghost" | undefined;
|
|
334
|
+
readonly action?: any;
|
|
335
|
+
readonly groupId: string;
|
|
336
|
+
readonly hideTitle?: boolean | undefined;
|
|
337
|
+
} | {
|
|
338
|
+
readonly id: string;
|
|
339
|
+
readonly title: readonly [{
|
|
340
|
+
readonly locale: "zh";
|
|
341
|
+
readonly message: string;
|
|
342
|
+
}, ...{
|
|
343
|
+
readonly locale: "en" | "ja" | "ko";
|
|
344
|
+
readonly message: string;
|
|
345
|
+
}[]];
|
|
346
|
+
readonly icon?: string | undefined;
|
|
347
|
+
readonly items: readonly (import("../actions/schema.js").RegistrySubItemValue | {
|
|
348
|
+
readonly disabled?: string | undefined;
|
|
349
|
+
readonly id: string;
|
|
350
|
+
readonly title: readonly [{
|
|
351
|
+
readonly locale: "zh";
|
|
352
|
+
readonly message: string;
|
|
353
|
+
}, ...{
|
|
354
|
+
readonly locale: "en" | "ja" | "ko";
|
|
355
|
+
readonly message: string;
|
|
356
|
+
}[]];
|
|
357
|
+
readonly icon?: string | undefined;
|
|
358
|
+
readonly hidden?: string | undefined;
|
|
359
|
+
readonly tooltip?: readonly [{
|
|
360
|
+
readonly locale: "zh";
|
|
361
|
+
readonly message: string;
|
|
362
|
+
}, ...{
|
|
363
|
+
readonly locale: "en" | "ja" | "ko";
|
|
364
|
+
readonly message: string;
|
|
365
|
+
}[]] | undefined;
|
|
366
|
+
readonly variant?: "default" | "link" | "destructive" | "primary" | "ghost" | undefined;
|
|
367
|
+
readonly action?: any;
|
|
368
|
+
})[];
|
|
369
|
+
readonly groupId: string;
|
|
370
|
+
readonly hideTitle?: boolean | undefined;
|
|
371
|
+
}, import("../actions/schema.js").RegistryItemValue | {
|
|
372
|
+
readonly id: string;
|
|
373
|
+
readonly title: readonly [{
|
|
374
|
+
readonly locale: "zh";
|
|
375
|
+
readonly message: string;
|
|
376
|
+
}, ...{
|
|
377
|
+
readonly locale: "en" | "ja" | "ko";
|
|
378
|
+
readonly message: string;
|
|
379
|
+
}[]];
|
|
380
|
+
readonly groupId: string;
|
|
381
|
+
readonly disabled?: string | undefined;
|
|
382
|
+
readonly icon?: string | undefined;
|
|
383
|
+
readonly hidden?: string | undefined;
|
|
384
|
+
readonly tooltip?: readonly [{
|
|
385
|
+
readonly locale: "zh";
|
|
386
|
+
readonly message: string;
|
|
387
|
+
}, ...{
|
|
388
|
+
readonly locale: "en" | "ja" | "ko";
|
|
389
|
+
readonly message: string;
|
|
390
|
+
}[]] | undefined;
|
|
391
|
+
readonly variant?: "default" | "link" | "destructive" | "primary" | "ghost" | undefined;
|
|
392
|
+
readonly action?: any;
|
|
393
|
+
readonly hideTitle?: boolean | undefined;
|
|
394
|
+
} | {
|
|
395
|
+
readonly id: string;
|
|
396
|
+
readonly title: readonly [{
|
|
397
|
+
readonly locale: "zh";
|
|
398
|
+
readonly message: string;
|
|
399
|
+
}, ...{
|
|
400
|
+
readonly locale: "en" | "ja" | "ko";
|
|
401
|
+
readonly message: string;
|
|
402
|
+
}[]];
|
|
403
|
+
readonly items: readonly (import("../actions/schema.js").RegistrySubItemValue | {
|
|
404
|
+
readonly id: string;
|
|
405
|
+
readonly title: readonly [{
|
|
406
|
+
readonly locale: "zh";
|
|
407
|
+
readonly message: string;
|
|
408
|
+
}, ...{
|
|
409
|
+
readonly locale: "en" | "ja" | "ko";
|
|
410
|
+
readonly message: string;
|
|
411
|
+
}[]];
|
|
412
|
+
readonly disabled?: string | undefined;
|
|
413
|
+
readonly icon?: string | undefined;
|
|
414
|
+
readonly hidden?: string | undefined;
|
|
415
|
+
readonly tooltip?: readonly [{
|
|
416
|
+
readonly locale: "zh";
|
|
417
|
+
readonly message: string;
|
|
418
|
+
}, ...{
|
|
419
|
+
readonly locale: "en" | "ja" | "ko";
|
|
420
|
+
readonly message: string;
|
|
421
|
+
}[]] | undefined;
|
|
422
|
+
readonly variant?: "default" | "link" | "destructive" | "primary" | "ghost" | undefined;
|
|
423
|
+
readonly action?: any;
|
|
424
|
+
})[];
|
|
425
|
+
readonly groupId: string;
|
|
426
|
+
readonly icon?: string | undefined;
|
|
427
|
+
readonly hideTitle?: boolean | undefined;
|
|
428
|
+
}, never>>;
|
|
370
429
|
}>>;
|
|
371
430
|
query: Schema.optional<Schema.refine<{
|
|
372
431
|
readonly initial?: {
|
|
@@ -512,7 +571,7 @@ export declare function createTableConfig(body: Omit<Schema.Schema.Type<ReturnTy
|
|
|
512
571
|
readonly size: "default" | "sm" | "xs";
|
|
513
572
|
readonly style?: string | undefined;
|
|
514
573
|
readonly gap: number;
|
|
515
|
-
readonly items: readonly ({
|
|
574
|
+
readonly items: readonly (import("../actions/schema.js").RegistryItemValue | {
|
|
516
575
|
readonly disabled?: string | undefined;
|
|
517
576
|
readonly id: string;
|
|
518
577
|
readonly title: readonly [{
|
|
@@ -545,8 +604,7 @@ export declare function createTableConfig(body: Omit<Schema.Schema.Type<ReturnTy
|
|
|
545
604
|
readonly message: string;
|
|
546
605
|
}[]];
|
|
547
606
|
readonly icon?: string | undefined;
|
|
548
|
-
readonly
|
|
549
|
-
readonly items: readonly {
|
|
607
|
+
readonly items: readonly (import("../actions/schema.js").RegistrySubItemValue | {
|
|
550
608
|
readonly disabled?: string | undefined;
|
|
551
609
|
readonly id: string;
|
|
552
610
|
readonly title: readonly [{
|
|
@@ -567,7 +625,9 @@ export declare function createTableConfig(body: Omit<Schema.Schema.Type<ReturnTy
|
|
|
567
625
|
}[]] | undefined;
|
|
568
626
|
readonly variant?: "default" | "link" | "destructive" | "primary" | "ghost" | undefined;
|
|
569
627
|
readonly action?: any;
|
|
570
|
-
}[];
|
|
628
|
+
})[];
|
|
629
|
+
readonly groupId: string;
|
|
630
|
+
readonly hideTitle?: boolean | undefined;
|
|
571
631
|
})[];
|
|
572
632
|
readonly groups: readonly {
|
|
573
633
|
readonly id: string;
|
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
dropTargetForElements
|
|
6
6
|
} from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
|
|
7
7
|
import { setCustomNativeDragPreview } from "@atlaskit/pragmatic-drag-and-drop/element/set-custom-native-drag-preview";
|
|
8
|
-
import { preserveOffsetOnSource } from "@atlaskit/pragmatic-drag-and-drop/element/preserve-offset-on-source";
|
|
9
8
|
import {
|
|
10
9
|
attachInstruction,
|
|
11
10
|
extractInstruction
|
|
@@ -49,10 +48,22 @@ export function useRowReorder(options) {
|
|
|
49
48
|
onGenerateDragPreview: ({ nativeSetDragImage, location, source }) => {
|
|
50
49
|
setCustomNativeDragPreview({
|
|
51
50
|
nativeSetDragImage,
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
// Anchor the preview to the grip's rect, not the row's. The row is
|
|
52
|
+
// virtualized (`position: absolute` + `translate3d`) so its
|
|
53
|
+
// bounding box shifts with vertical scroll and `preserveOffsetOnSource`
|
|
54
|
+
// pulls the preview off the pointer once the table has been
|
|
55
|
+
// scrolled. The grip is always rendered exactly where the user
|
|
56
|
+
// clicked AND sits at the row's leading edge, so offsetting from
|
|
57
|
+
// it keeps the start of the line pinned under the cursor in
|
|
58
|
+
// every scroll state.
|
|
59
|
+
getOffset: ({ container }) => {
|
|
60
|
+
const containerRect = container.getBoundingClientRect();
|
|
61
|
+
const handleRect = handleEl.getBoundingClientRect();
|
|
62
|
+
return {
|
|
63
|
+
x: Math.max(0, Math.min(location.current.input.clientX - handleRect.left, containerRect.width)),
|
|
64
|
+
y: Math.max(0, Math.min(location.current.input.clientY - handleRect.top, containerRect.height))
|
|
65
|
+
};
|
|
66
|
+
},
|
|
56
67
|
render: ({ container }) => {
|
|
57
68
|
const src = source.element;
|
|
58
69
|
const rect = src.getBoundingClientRect();
|
|
@@ -22,6 +22,9 @@ The custom `Optional` class has been replaced with Effect's `Option` type (`impo
|
|
|
22
22
|
|
|
23
23
|
An `http` built-in has been added (`http-builtins.ts`, `http-builder.ts`) — not from upstream. It registers the `http` constant and the `http` / `HttpRequest` types on `globalRegistry`, so `http.get(url).header(...).body(...)` expressions type-check and evaluate in every `Environment`. A CEL expression only builds an `HttpRequestBuilder` — a pure description of a request; it never issues one. Both terminal methods, `.json()` and `.file()`, are dispatched by the host on the returned builder (neither is a CEL method), so expression evaluation stays free of IO. Endpoints must be absolute URLs — there is no base-URL resolution. Depends on `fx-fetch`. The host can register process-wide default headers via `HttpRequestBuilder.setDefaultHeader(name, valueOrGetter)` / `clearDefaultHeader(name)`; they are merged in at `#buildRequest()` time, with case-insensitive precedence to an explicit `.header(...)` on the builder. A getter that returns `null` / `undefined` / `''` skips the header for that request, so the host can source values from live refs (e.g. the active i18n locale for `Accept-Language`) without baking in stale snapshots.
|
|
24
24
|
|
|
25
|
+
A `form(dyn): FormData` built-in has been added (`form-builtins.ts`) — not from upstream. It registers the `FormData` type on `globalRegistry` and a global `form` function that turns a CEL map into a native `FormData`, so authors can write `http.post(url).body(form({"file": myFile, "name": "Alice"}))` (`.body()` already passes `FormData` through verbatim and `fetch` sets the multipart boundary). Value coercion mirrors `.query()`'s flat-record semantics: `null` / `undefined` / `Option.None` skip the key; `Option.Some(x)` recurses on `x`; `File` / `Blob` pass through (preserves the `File` filename); `Decimal` → `.toString()` (preserves precision past safe-integer); `Date` (`TZDate`) → `.toISOString()`; `bool` → `'true'` / `'false'`; arrays append one entry per element; nested objects throw (`multipart/form-data` is flat — same rule as `.query(dyn)`). Top-level input must be a map literal / record (Map or plain object); passing an array or scalar throws.
|
|
26
|
+
The `__proto__` / `constructor` / `prototype` keys are skipped on the top-level record, mirroring `safeFromEntries`.
|
|
27
|
+
|
|
25
28
|
A `JSON` built-in namespace has been added (`json-builtins.ts`) — not from upstream. It registers a `JSON` brand class and a same-named constant on `globalRegistry`, so `JSON.parse(string): dyn` and `JSON.stringify(dyn): string` resolve in every `Environment`. The CEL-side surface mirrors the JS globals deliberately. `JSON.parse` is a thin wrapper over `globalThis.JSON.parse` — numbers come back as plain JS `number` (the evaluator coerces via `Decimal.from` at use time). `JSON.stringify` pre-walks the value via `normalizeForJSON` before handing it to `globalThis.JSON.stringify`: `Decimal` → JS number (via `.toNumber()` — loses precision past safe-integer range, deliberate trade-off for idiomatic JSON output), `Option.Some(x)` → `x`, `Option.None` → `null`, `Map` → plain object, `Date`/`TZDate` passes through so its built-in `toJSON` emits ISO 8601. The pre-walk is required because Effect's `Option` defines its own `toJSON()` that runs before any replacer would see it.
|
|
26
29
|
|
|
27
30
|
Spread syntax (`...expr`) inside list and map literals — not from upstream. A new `ELLIPSIS` token (3-char lookahead in the lexer) and a `spread` AST op let `[1, ...a, 4]` and `{...a, "x": 1, ...b}` desugar inside the parent literal. The spread node's own `check`/`evaluate` are defensive stubs — `parsePrimary` never produces one, so `...` outside a list/map is an "unexpected token" parse error. List `args` shape is unchanged (`IASTNode[]`, with spread elements detected via `op === 'spread'`); map `args` becomes `([IASTNode, IASTNode] | [IASTNode])[]` — a length-1 tuple represents a spread entry, preserving positional ordering for override semantics (later writes win). The fast path is preserved: the `list`/`map` `check` swaps `evaluate` meta to `evaluateSpreadList`/`evaluateSpreadMap` only when a spread child is present; literals without spreads run the original `resolveAstArray`/`safeFromEntries` paths byte-for-byte. Spread map sources may be plain objects, `Map` instances, or registered message types (typed as `map<string, dyn>`); the `__proto__`/`constructor`/`prototype` skip from `safeFromEntries` is applied to spread sources too. Runtime errors fire on non-list/non-map sources (including `null`). `maxListElements` / `maxMapEntries` count a spread as one entry — runtime expansion bypasses those caps (deliberate trade-off). Call-argument spread (`f(...args)`) is **not** supported.
|
|
@@ -42,6 +45,7 @@ lib/
|
|
|
42
45
|
├── http-builtins.ts — `http` constant + http/HttpRequest types (local addition)
|
|
43
46
|
├── http-builder.ts — `HttpRequestBuilder` — what an http.* expression returns
|
|
44
47
|
├── json-builtins.ts — `JSON` constant + `JSON.parse` / `JSON.stringify` (local addition)
|
|
48
|
+
├── form-builtins.ts — `form(map): FormData` global + `FormData` type (local addition)
|
|
45
49
|
├── decimal.ts — Decimal class (arbitrary precision)
|
|
46
50
|
├── optional.ts — Optional type support (uses Effect Option)
|
|
47
51
|
├── serialize.ts — AST back to CEL string
|
|
@@ -73,7 +77,7 @@ Internal-only (not re-exported from `index.js`): `parse`, standalone `evaluate`/
|
|
|
73
77
|
|
|
74
78
|
Primitives: `string`, `bool`, `number`, `bytes`, `null_type`, `type`
|
|
75
79
|
Composites: `list<T>`, `map<K, V>`, `optional<T>`, `dyn`, `message`
|
|
76
|
-
Custom: `Date` (backed by `TZDate` from `@date-fns/tz`), `URL` (backed by the native `URL`; method `URL.param(string): string` reads a search param), `File` (native), `http` / `HttpRequest` (local addition — see `http-builtins.ts`)
|
|
80
|
+
Custom: `Date` (backed by `TZDate` from `@date-fns/tz`), `URL` (backed by the native `URL`; method `URL.param(string): string` reads a search param), `File` (native), `FormData` (native — see `form-builtins.ts`), `http` / `HttpRequest` (local addition — see `http-builtins.ts`)
|
|
77
81
|
|
|
78
82
|
## Function Signature Format
|
|
79
83
|
|
|
@@ -174,6 +174,25 @@ unwrapped first so the output is plain JSON — numbers (CEL's `Decimal`) become
|
|
|
174
174
|
JS numbers, optionals become their value (or `null` when empty), and `Date`
|
|
175
175
|
values become ISO 8601 strings.
|
|
176
176
|
|
|
177
|
+
### FormData
|
|
178
|
+
```cel
|
|
179
|
+
// Build multipart/form-data for an upload:
|
|
180
|
+
http.post("https://api.example.com/files")
|
|
181
|
+
.body(form({"file": myFile, "name": "Alice", "tags": ["a", "b"]}))
|
|
182
|
+
```
|
|
183
|
+
`form(map): FormData` turns a flat record into a native `FormData`. Pass it
|
|
184
|
+
straight to `.body(...)` — `fetch` sets the multipart boundary, so you don't
|
|
185
|
+
need a `Content-Type` header.
|
|
186
|
+
|
|
187
|
+
Value coercion is flat (same rules as `.query(...)`):
|
|
188
|
+
- `null` / `undefined` / absent `.?` values → key is skipped
|
|
189
|
+
- `Option.Some(x)` → unwrapped to `x`
|
|
190
|
+
- `File` / `Blob` → appended as-is (the `File` filename is preserved)
|
|
191
|
+
- numbers / booleans / dates → coerced to string (`Date` → ISO 8601)
|
|
192
|
+
- arrays → one entry per element under the same key
|
|
193
|
+
- nested objects / maps → error (multipart is flat — use `JSON.stringify(...)`
|
|
194
|
+
on the value if you need a structured field)
|
|
195
|
+
|
|
177
196
|
### Dates
|
|
178
197
|
```cel
|
|
179
198
|
date("2023-06-15") // create a Date
|
|
@@ -4,6 +4,7 @@ import { evaluationError } from "./errors.js";
|
|
|
4
4
|
import { registerFunctions } from "./functions.js";
|
|
5
5
|
import { registerHttpBuiltins } from "./http-builtins.js";
|
|
6
6
|
import { registerJSONBuiltins } from "./json-builtins.js";
|
|
7
|
+
import { registerFormBuiltins } from "./form-builtins.js";
|
|
7
8
|
import { registerMacros } from "./macros.js";
|
|
8
9
|
import { registerOverloads } from "./overloads.js";
|
|
9
10
|
import { TypeChecker } from "./type-checker.js";
|
|
@@ -16,6 +17,7 @@ registerOverloads(globalRegistry);
|
|
|
16
17
|
registerMacros(globalRegistry);
|
|
17
18
|
registerHttpBuiltins(globalRegistry);
|
|
18
19
|
registerJSONBuiltins(globalRegistry);
|
|
20
|
+
registerFormBuiltins(globalRegistry);
|
|
19
21
|
class Environment {
|
|
20
22
|
#registry;
|
|
21
23
|
#evaluator;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Option } from "effect";
|
|
2
|
+
import { evaluationError } from "./errors.js";
|
|
3
|
+
import { Decimal } from "./decimal.js";
|
|
4
|
+
function coerceScalar(value) {
|
|
5
|
+
if (value instanceof Blob) return value;
|
|
6
|
+
if (value instanceof Decimal) return value.toString();
|
|
7
|
+
if (value instanceof Date) return value.toISOString();
|
|
8
|
+
if (typeof value === "string") return value;
|
|
9
|
+
if (typeof value === "boolean") return value ? "true" : "false";
|
|
10
|
+
if (typeof value === "number" || typeof value === "bigint") return String(value);
|
|
11
|
+
throw evaluationError(
|
|
12
|
+
"invalid_form_value",
|
|
13
|
+
`form(): unsupported value type ${Object.prototype.toString.call(value)}`
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
function appendEntry(fd, key, value) {
|
|
17
|
+
if (value === null || value === void 0) return;
|
|
18
|
+
if (Option.isOption(value)) {
|
|
19
|
+
if (Option.isSome(value)) appendEntry(fd, key, value.value);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
if (Array.isArray(value)) {
|
|
23
|
+
for (const item of value) appendEntry(fd, key, item);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (value instanceof Blob) {
|
|
27
|
+
fd.append(key, value);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (typeof value === "object" && !(value instanceof Decimal) && !(value instanceof Date)) {
|
|
31
|
+
throw evaluationError(
|
|
32
|
+
"invalid_form_value",
|
|
33
|
+
`form(): nested object values are not supported (key: "${key}")`
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
fd.append(key, coerceScalar(value));
|
|
37
|
+
}
|
|
38
|
+
export function registerFormBuiltins(registry) {
|
|
39
|
+
registry.registerType("FormData", FormData);
|
|
40
|
+
registry.registerFunctionOverload("form(dyn): FormData", (record) => {
|
|
41
|
+
if (record === null || record === void 0) {
|
|
42
|
+
throw evaluationError("invalid_form_value", "form(): expected a map, got null");
|
|
43
|
+
}
|
|
44
|
+
const fd = new FormData();
|
|
45
|
+
if (record instanceof Map) {
|
|
46
|
+
for (const [k, v] of record) appendEntry(fd, String(k), v);
|
|
47
|
+
return fd;
|
|
48
|
+
}
|
|
49
|
+
if (typeof record !== "object" || Array.isArray(record)) {
|
|
50
|
+
throw evaluationError("invalid_form_value", "form(): expected a map");
|
|
51
|
+
}
|
|
52
|
+
for (const key of Object.keys(record)) {
|
|
53
|
+
if (key === "__proto__" || key === "constructor" || key === "prototype") continue;
|
|
54
|
+
appendEntry(fd, key, record[key]);
|
|
55
|
+
}
|
|
56
|
+
return fd;
|
|
57
|
+
});
|
|
58
|
+
}
|