@zerotal/flow 1.8.1 → 1.10.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.
- package/CHANGELOG.md +57 -0
- package/api-surface.md +17 -195
- package/package.json +6 -6
- package/src/Component.ts +13 -10
- package/src/Form.ts +5 -1
- package/src/browser/FlowBrowser.ts +3 -2
- package/src/browser/cdp.ts +2 -0
- package/src/client/confirm.ts +6 -6
- package/src/client/enhance.ts +205 -0
- package/src/client/enhanceSupport.ts +99 -0
- package/src/client/url.ts +2 -2
- package/src/compiler/helpers.ts +10 -2
- package/src/config.ts +5 -1
- package/src/decorators.ts +6 -4
- package/src/durable.ts +4 -0
- package/src/enhanceTag.ts +36 -0
- package/src/index.ts +1 -0
- package/src/provider/FlowProvider.ts +94 -0
- package/src/registry.ts +4 -0
- package/src/shared.ts +4 -0
- package/src/synths/ModelSynth.ts +77 -1
- package/src/synths/index.ts +4 -0
- package/src/types.ts +20 -0
- package/src/urlSafety.ts +0 -0
- package/src/utils.ts +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,63 @@ change.
|
|
|
11
11
|
|
|
12
12
|
## [Unreleased]
|
|
13
13
|
|
|
14
|
+
## [1.9.0] — 2026-08-29
|
|
15
|
+
|
|
16
|
+
- **The component and flash types are documented.** Every built-in component exports the type of
|
|
17
|
+
its own props — `ModalProps`, `ForProps`, `VirtualizeProps` and eighteen more — and none was
|
|
18
|
+
named anywhere a reader would look, though wrapping a component is the usual reason to want
|
|
19
|
+
one. The flash API's shapes are documented alongside them: `FlashBuilder` and the eight types
|
|
20
|
+
around it, for a helper that composes a message rather than writing one inline.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- **INTERNAL: 35 exports are marked `@internal`.** The compiler's output helpers (`__esc`,
|
|
25
|
+
`__escAttr`, `jsLiteral`), the URL-safety internals, the wire-protocol frame types
|
|
26
|
+
(`PatchFrame`, `EventFrame`, `RedirectFrame`, …), the registration functions, the durable and
|
|
27
|
+
shared store accessors, and `FlowBrowser`'s Chrome discovery.
|
|
28
|
+
|
|
29
|
+
**Nothing is removed and nothing breaks.** The frame types are the clearest case: they
|
|
30
|
+
describe what crosses the socket, an app never constructs one, and naming them in the promised
|
|
31
|
+
surface implied a stability guarantee about a protocol that is free to change.
|
|
32
|
+
|
|
33
|
+
- **A bound field the model will not accept now says so.** `flow:model` on a column missing from
|
|
34
|
+
`fillable` was dropped in silence: the form submitted, nothing was written, nothing failed, and
|
|
35
|
+
there was no clue. The drop stays — the same path receives whatever a browser sends, so an
|
|
36
|
+
unknown key must not write a column and must not be able to 500 the page — but a developer's
|
|
37
|
+
typo produced exactly the same silence as a hostile payload. In development it now warns once
|
|
38
|
+
per class per field, naming the field, the current `fillable`, and both ways to fix it.
|
|
39
|
+
|
|
40
|
+
- **The client entries are held to the `@zerotal/orm` boundary.** Every orm import in this
|
|
41
|
+
package is `import type` — deliberately, because a root import once dragged
|
|
42
|
+
`await import("bun")` into a browser bundle and broke it at _resolution_ time, before
|
|
43
|
+
tree-shaking could drop it. One exception exists: `ModelSynth` imports a value. It is safe only
|
|
44
|
+
because no client entry reaches a synth, and that condition had been verified by hand, once,
|
|
45
|
+
with nothing holding it. A test now walks the import graph from each client entry and fails
|
|
46
|
+
naming the module that crossed.
|
|
47
|
+
|
|
48
|
+
- **The plain-form enhancement bundle is built on first request, not at boot.** Most apps never
|
|
49
|
+
serve a page that uses `<form data-enhance>`, so building it at boot spends time on an artefact
|
|
50
|
+
nobody asked for, in every process.
|
|
51
|
+
|
|
52
|
+
### Added
|
|
53
|
+
|
|
54
|
+
- **`<form data-enhance>` — plain-form enhancement, no component required.** A page that is
|
|
55
|
+
just server-rendered HTML can now have a form that submits without the page flashing: it
|
|
56
|
+
posts through `fetch` and the matching form in the response replaces it in place, so a
|
|
57
|
+
validation error lands where the person is looking and nothing else on the page moves.
|
|
58
|
+
|
|
59
|
+
Delivered as its own dependency-free bundle at `/__flow/enhance.js`, because the whole point
|
|
60
|
+
is a page with no Flow component on it — such a page is never served `/__flow/runtime.js`,
|
|
61
|
+
and pulling Alpine and a WebSocket bridge onto it would be paying the framework's entire
|
|
62
|
+
client cost for a submit handler. Add it with `flowEnhanceTag()` in a non-Flow layout.
|
|
63
|
+
|
|
64
|
+
Every path degrades: a network failure re-submits natively rather than losing what was
|
|
65
|
+
typed, a redirect is followed and `pushState`d so the address bar agrees, and no JavaScript
|
|
66
|
+
at all is an ordinary form post — `data-enhance` is additive on a form that already works,
|
|
67
|
+
so there is no fallback path that can rot.
|
|
68
|
+
|
|
69
|
+
This completes ACTION 8, whose other two items turned out to be already shipped.
|
|
70
|
+
|
|
14
71
|
## [1.8.0] — 2026-08-24
|
|
15
72
|
|
|
16
73
|
### Added
|
package/api-surface.md
CHANGED
|
@@ -174,9 +174,7 @@ class WebSocketDisconnected = {
|
|
|
174
174
|
readonly url: string
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
const DEFAULT_PERSISTENT_MIDDLEWARE = (string | MiddlewareClass)[]
|
|
177
|
+
const FLOW_ENHANCE_PATH = '/__flow/enhance.js'
|
|
180
178
|
|
|
181
179
|
const locked = (value: unknown, context: ClassFieldDecoratorContext) => void
|
|
182
180
|
|
|
@@ -186,8 +184,6 @@ const reactive = (value: unknown, context: ClassFieldDecoratorContext) => void
|
|
|
186
184
|
|
|
187
185
|
const transient = (value: unknown, context: ClassFieldDecoratorContext) => void
|
|
188
186
|
|
|
189
|
-
const URL_ATTRIBUTES = ReadonlySet<string>
|
|
190
|
-
|
|
191
187
|
function Accordion = (props: AccordionProps) => HtmlNode
|
|
192
188
|
|
|
193
189
|
function Alert = (props: AlertProps) => HtmlNode
|
|
@@ -230,26 +226,16 @@ function Flash = (props?: FlashProps) => HtmlNode
|
|
|
230
226
|
|
|
231
227
|
function flow = <T>(target: T) => FlowChain<T>
|
|
232
228
|
|
|
233
|
-
function flowActiveConnections = () => number
|
|
234
|
-
|
|
235
229
|
function FlowConfig = (options?: Partial<FlowConfigShape>) => FlowConfigShape
|
|
236
230
|
|
|
237
|
-
function
|
|
231
|
+
function flowEnhanceTag = () => string
|
|
238
232
|
|
|
239
233
|
function For = <T>(props: ForProps<T>) => HtmlNode
|
|
240
234
|
|
|
241
|
-
function getDurableStore = () => DurableStore
|
|
242
|
-
|
|
243
|
-
function getSharedStore = () => SharedStore
|
|
244
|
-
|
|
245
235
|
function Head = (props: { children?: unknown;}) => HtmlNode
|
|
246
236
|
|
|
247
237
|
function InfiniteScroll = (props: InfiniteScrollProps) => HtmlNode
|
|
248
238
|
|
|
249
|
-
function isSafeUrl = (url: string) => boolean
|
|
250
|
-
|
|
251
|
-
function jsLiteral = (value: unknown) => string
|
|
252
|
-
|
|
253
239
|
function Label = (props: LabelProps) => HtmlNode
|
|
254
240
|
|
|
255
241
|
function Legend = (props: LegendProps) => HtmlNode
|
|
@@ -280,18 +266,10 @@ function presence = (channel: PresenceChannel) => (value: unknown, context: Clas
|
|
|
280
266
|
|
|
281
267
|
function RadioGroup = (props: RadioGroupProps) => HtmlNode
|
|
282
268
|
|
|
283
|
-
function registerComponent = (PageClass: typeof Component, path?: string) => void
|
|
284
|
-
|
|
285
269
|
function registerFlowEvent = <K extends EventName>(name: K, guard: (payload: unknown) => payload is FlowEvents[K]) => void
|
|
286
270
|
|
|
287
|
-
function registerForm = (FormClass: new () => Form) => void
|
|
288
|
-
|
|
289
|
-
function registerSynth = (synth: Synth) => void
|
|
290
|
-
|
|
291
271
|
function renderless = (_fn: unknown, context: ClassMethodDecoratorContext) => void
|
|
292
272
|
|
|
293
|
-
function sanitizeUrl = (url: string) => string
|
|
294
|
-
|
|
295
273
|
function SectionContent = (props: SectionContentProps) => HtmlNode
|
|
296
274
|
|
|
297
275
|
function SectionOutlet = (props: SectionOutletProps) => HtmlNode
|
|
@@ -371,15 +349,6 @@ interface CalendarProps = {
|
|
|
371
349
|
weekdayClass?: string
|
|
372
350
|
}
|
|
373
351
|
|
|
374
|
-
interface CallFrame = {
|
|
375
|
-
args: unknown[]
|
|
376
|
-
component: string
|
|
377
|
-
method: string
|
|
378
|
-
snapshot: Snapshot
|
|
379
|
-
type: 'call'
|
|
380
|
-
updates: Record<string, unknown>
|
|
381
|
-
}
|
|
382
|
-
|
|
383
352
|
interface CheckboxProps = {
|
|
384
353
|
[key: string]: unknown
|
|
385
354
|
bind: unknown
|
|
@@ -387,12 +356,6 @@ interface CheckboxProps = {
|
|
|
387
356
|
class?: string
|
|
388
357
|
}
|
|
389
358
|
|
|
390
|
-
interface ChildMemo = {
|
|
391
|
-
id: string
|
|
392
|
-
key: string
|
|
393
|
-
name: string
|
|
394
|
-
}
|
|
395
|
-
|
|
396
359
|
interface ComboboxOption = {
|
|
397
360
|
label: string
|
|
398
361
|
value: string | number
|
|
@@ -440,13 +403,6 @@ interface DisclosureProps = {
|
|
|
440
403
|
trigger?: unknown
|
|
441
404
|
}
|
|
442
405
|
|
|
443
|
-
interface DownloadFrame = {
|
|
444
|
-
content: string
|
|
445
|
-
filename: string
|
|
446
|
-
mime: string
|
|
447
|
-
type: 'download'
|
|
448
|
-
}
|
|
449
|
-
|
|
450
406
|
interface DrawerProps = {
|
|
451
407
|
[key: string]: unknown
|
|
452
408
|
children?: unknown
|
|
@@ -469,12 +425,6 @@ interface DropdownProps = {
|
|
|
469
425
|
trigger?: unknown
|
|
470
426
|
}
|
|
471
427
|
|
|
472
|
-
interface DurableStore = {
|
|
473
|
-
delete: (key: string) => Promise<void> | void
|
|
474
|
-
get: (key: string) => Promise<Snapshot | undefined> | Snapshot | undefined
|
|
475
|
-
set: (key: string, value: Snapshot, ttlMs?: number) => Promise<void> | void
|
|
476
|
-
}
|
|
477
|
-
|
|
478
428
|
interface ErrorBoundaryProps = {
|
|
479
429
|
[key: string]: unknown
|
|
480
430
|
children?: unknown
|
|
@@ -484,15 +434,6 @@ interface ErrorBoundaryProps = {
|
|
|
484
434
|
tag?: string
|
|
485
435
|
}
|
|
486
436
|
|
|
487
|
-
interface ErrorFrame = {
|
|
488
|
-
action?: string
|
|
489
|
-
component: string
|
|
490
|
-
message: string
|
|
491
|
-
name?: string
|
|
492
|
-
stack?: string
|
|
493
|
-
type: 'error'
|
|
494
|
-
}
|
|
495
|
-
|
|
496
437
|
interface ErrorProps = {
|
|
497
438
|
[key: string]: unknown
|
|
498
439
|
class?: string
|
|
@@ -505,20 +446,6 @@ interface ErrorsProps = {
|
|
|
505
446
|
only?: string | string[]
|
|
506
447
|
}
|
|
507
448
|
|
|
508
|
-
interface ErrorsProxy = {
|
|
509
|
-
[key: string]: ErrorField | ((...args: never[]) => unknown)
|
|
510
|
-
add: { (field: string, message: string): void; (errors: Record<string, string | string[]>): void;}
|
|
511
|
-
any: () => boolean
|
|
512
|
-
clear: (field?: string) => void
|
|
513
|
-
has: (field: string) => boolean
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
interface EventFrame = {
|
|
517
|
-
data: Record<string, unknown>
|
|
518
|
-
name: string
|
|
519
|
-
type: 'event'
|
|
520
|
-
}
|
|
521
|
-
|
|
522
449
|
interface FieldProps = {
|
|
523
450
|
[key: string]: unknown
|
|
524
451
|
children?: unknown
|
|
@@ -583,20 +510,6 @@ interface FlashCallback = {
|
|
|
583
510
|
method: string
|
|
584
511
|
}
|
|
585
512
|
|
|
586
|
-
interface FlashFrame = {
|
|
587
|
-
actions?: FlashAction[]
|
|
588
|
-
dismissible?: boolean
|
|
589
|
-
duration?: number
|
|
590
|
-
icon?: string | false
|
|
591
|
-
level: 'success' | 'error' | 'warning' | 'info'
|
|
592
|
-
message: string
|
|
593
|
-
onClose?: FlashCallback
|
|
594
|
-
position?: FlashPosition
|
|
595
|
-
progressBar?: boolean
|
|
596
|
-
title?: string
|
|
597
|
-
type: 'flash'
|
|
598
|
-
}
|
|
599
|
-
|
|
600
513
|
interface FlashMessage = {
|
|
601
514
|
actions?: FlashAction[]
|
|
602
515
|
dismissible?: boolean
|
|
@@ -611,16 +524,16 @@ interface FlashMessage = {
|
|
|
611
524
|
}
|
|
612
525
|
|
|
613
526
|
interface FlashOptions = {
|
|
614
|
-
actions?: FlashAction[]
|
|
615
|
-
dismissible?: boolean
|
|
616
|
-
duration?: number
|
|
617
|
-
icon?: string | false
|
|
618
|
-
level?: 'success' | 'error' | 'warning' | 'info'
|
|
619
|
-
onClose?: FlashCallback
|
|
620
|
-
position?: FlashPosition
|
|
621
|
-
progressBar?: boolean
|
|
622
|
-
title?: string
|
|
623
|
-
type?: 'success' | 'error' | 'warning' | 'info'
|
|
527
|
+
actions?: FlashAction[] | undefined
|
|
528
|
+
dismissible?: boolean | undefined
|
|
529
|
+
duration?: number | undefined
|
|
530
|
+
icon?: string | false | undefined
|
|
531
|
+
level?: 'success' | 'error' | 'warning' | 'info' | undefined
|
|
532
|
+
onClose?: FlashCallback | undefined
|
|
533
|
+
position?: FlashPosition | undefined
|
|
534
|
+
progressBar?: boolean | undefined
|
|
535
|
+
title?: string | undefined
|
|
536
|
+
type?: 'success' | 'error' | 'warning' | 'info' | undefined
|
|
624
537
|
}
|
|
625
538
|
|
|
626
539
|
interface FlashProps = {
|
|
@@ -658,26 +571,6 @@ interface FlowConfigShape = {
|
|
|
658
571
|
persistentMiddleware: (string | MiddlewareClass)[]
|
|
659
572
|
}
|
|
660
573
|
|
|
661
|
-
interface FlowConnection = {
|
|
662
|
-
actions: number
|
|
663
|
-
connectedAt: number
|
|
664
|
-
id: string
|
|
665
|
-
ip: string
|
|
666
|
-
lastActivityAt: number
|
|
667
|
-
user: string | null
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
interface FlowEffects = {
|
|
671
|
-
downloads: { filename: string; content: string; mime: string;}[]
|
|
672
|
-
errors: Record<string, string[]>
|
|
673
|
-
events: { name: string; data: Record<string, unknown>; to?: string; self?: boolean;}[]
|
|
674
|
-
flashes: FlashMessage[]
|
|
675
|
-
redirectUrl: string | null
|
|
676
|
-
scripts: string[]
|
|
677
|
-
shouldRefresh: boolean
|
|
678
|
-
title: string | null
|
|
679
|
-
}
|
|
680
|
-
|
|
681
574
|
interface FlowEvents = {}
|
|
682
575
|
|
|
683
576
|
interface FlowStore = {}
|
|
@@ -757,16 +650,6 @@ interface ModalProps = {
|
|
|
757
650
|
title?: unknown
|
|
758
651
|
}
|
|
759
652
|
|
|
760
|
-
interface PageClassWithMeta = {
|
|
761
|
-
new (): Component
|
|
762
|
-
head?: string
|
|
763
|
-
interactive?: boolean
|
|
764
|
-
layout?: new () => Layout
|
|
765
|
-
models?: Record<string, typeof BaseModel>
|
|
766
|
-
name: string
|
|
767
|
-
title?: string | ((component: never) => string)
|
|
768
|
-
}
|
|
769
|
-
|
|
770
653
|
interface PagerProps = {
|
|
771
654
|
[key: string]: unknown
|
|
772
655
|
activeClass?: string
|
|
@@ -792,22 +675,6 @@ interface Paginator = {
|
|
|
792
675
|
total: number
|
|
793
676
|
}
|
|
794
677
|
|
|
795
|
-
interface PatchFrame = {
|
|
796
|
-
actionError?: boolean
|
|
797
|
-
checksum?: string
|
|
798
|
-
component: string
|
|
799
|
-
dataDelta?: SnapshotData
|
|
800
|
-
dataRemoved?: string[]
|
|
801
|
-
errors?: Record<string, string[]>
|
|
802
|
-
html?: string
|
|
803
|
-
memo?: SnapshotMemo
|
|
804
|
-
partial?: boolean
|
|
805
|
-
scripts?: string[]
|
|
806
|
-
snapshot?: Snapshot
|
|
807
|
-
title?: string
|
|
808
|
-
type: 'patch'
|
|
809
|
-
}
|
|
810
|
-
|
|
811
678
|
interface PopoverProps = {
|
|
812
679
|
[key: string]: unknown
|
|
813
680
|
buttonClass?: string
|
|
@@ -844,12 +711,6 @@ interface RedirectFlash = {
|
|
|
844
711
|
withWarning: (message: string) => RedirectFlash
|
|
845
712
|
}
|
|
846
713
|
|
|
847
|
-
interface RedirectFrame = {
|
|
848
|
-
sessionToken?: string
|
|
849
|
-
type: 'redirect'
|
|
850
|
-
url: string
|
|
851
|
-
}
|
|
852
|
-
|
|
853
714
|
interface SectionContentProps = {
|
|
854
715
|
children?: unknown
|
|
855
716
|
name: string
|
|
@@ -874,14 +735,8 @@ interface SelectProps = {
|
|
|
874
735
|
}
|
|
875
736
|
|
|
876
737
|
interface SessionOptions = {
|
|
877
|
-
key?: string
|
|
878
|
-
scoped?: boolean
|
|
879
|
-
}
|
|
880
|
-
|
|
881
|
-
interface SharedStore = {
|
|
882
|
-
get: (key: string) => unknown | undefined
|
|
883
|
-
has: (key: string) => boolean
|
|
884
|
-
set: (key: string, value: unknown) => void
|
|
738
|
+
key?: string | undefined
|
|
739
|
+
scoped?: boolean | undefined
|
|
885
740
|
}
|
|
886
741
|
|
|
887
742
|
interface SkeletonProps = {
|
|
@@ -913,19 +768,6 @@ interface Snapshot = {
|
|
|
913
768
|
memo: SnapshotMemo
|
|
914
769
|
}
|
|
915
770
|
|
|
916
|
-
interface SnapshotMemo = {
|
|
917
|
-
children: string[]
|
|
918
|
-
iat?: number | undefined
|
|
919
|
-
id: string
|
|
920
|
-
listeners?: Record<string, string> | undefined
|
|
921
|
-
name: string
|
|
922
|
-
path: string
|
|
923
|
-
presence?: { prop: string; channel: string;}[] | undefined
|
|
924
|
-
shared?: { prop: string; channel: string;}[] | undefined
|
|
925
|
-
slots?: Record<string, string> | undefined
|
|
926
|
-
sub?: string | undefined
|
|
927
|
-
}
|
|
928
|
-
|
|
929
771
|
interface SwitchProps = {
|
|
930
772
|
[key: string]: unknown
|
|
931
773
|
bind: unknown
|
|
@@ -933,13 +775,6 @@ interface SwitchProps = {
|
|
|
933
775
|
class?: string
|
|
934
776
|
}
|
|
935
777
|
|
|
936
|
-
interface Synth = {
|
|
937
|
-
dehydrate: (value: T, meta: Record<string, unknown>) => unknown
|
|
938
|
-
hydrate: (data: unknown, meta: Record<string, unknown>) => T | Promise<T>
|
|
939
|
-
key: string
|
|
940
|
-
match: (value: unknown) => value is T
|
|
941
|
-
}
|
|
942
|
-
|
|
943
778
|
interface TabItem = {
|
|
944
779
|
content: unknown
|
|
945
780
|
label: unknown
|
|
@@ -1010,8 +845,8 @@ interface UploadRef = {
|
|
|
1010
845
|
}
|
|
1011
846
|
|
|
1012
847
|
interface UrlOptions = {
|
|
1013
|
-
as?: string
|
|
1014
|
-
history?: 'replace' | 'push'
|
|
848
|
+
as?: string | undefined
|
|
849
|
+
history?: 'replace' | 'push' | undefined
|
|
1015
850
|
}
|
|
1016
851
|
|
|
1017
852
|
interface VirtualizeProps = {
|
|
@@ -1048,10 +883,6 @@ type Mixin = (Base: TIn) => TOut
|
|
|
1048
883
|
|
|
1049
884
|
type PresenceChannel = string | ((self: Record<string, any>) => string)
|
|
1050
885
|
|
|
1051
|
-
type SharedChannel = string | ((self: Record<string, any>) => string)
|
|
1052
|
-
|
|
1053
|
-
type SnapshotData = { [x: string]: SnapshotTuple;}
|
|
1054
|
-
|
|
1055
886
|
type ValidationRules = { [x: string]: (rule: RuleBuilder) => FieldRule;}
|
|
1056
887
|
|
|
1057
888
|
## ./browser `(./src/browser/index.ts)`
|
|
@@ -1076,18 +907,9 @@ class FlowBrowser = {
|
|
|
1076
907
|
waitUntil: (expression: string, label: string, timeoutMs?: number) => Promise<void>
|
|
1077
908
|
}
|
|
1078
909
|
|
|
1079
|
-
function findChrome = () => string | null
|
|
1080
|
-
|
|
1081
|
-
interface OpenOptions = {
|
|
1082
|
-
timeout?: number
|
|
1083
|
-
waitForConnection?: boolean
|
|
1084
|
-
}
|
|
1085
|
-
|
|
1086
910
|
## ./compiler-helpers `(./src/compiler/helpers.ts)`
|
|
1087
911
|
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
function __escAttr = (v: unknown) => string
|
|
912
|
+
_(no exports)_
|
|
1091
913
|
|
|
1092
914
|
## ./store `(./src/store.ts)`
|
|
1093
915
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zerotal/flow",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"maturity": "stable",
|
|
6
6
|
"private": false,
|
|
@@ -47,15 +47,15 @@
|
|
|
47
47
|
"@alpinejs/morph": "^3.14.9",
|
|
48
48
|
"@alpinejs/persist": "^3.14.9",
|
|
49
49
|
"@alpinejs/resize": "^3.14.9",
|
|
50
|
-
"@zerotal/client": "1.
|
|
51
|
-
"@zerotal/core": "1.
|
|
52
|
-
"@zerotal/orm": "1.
|
|
53
|
-
"@zerotal/validator": "1.
|
|
50
|
+
"@zerotal/client": "1.10.0",
|
|
51
|
+
"@zerotal/core": "1.10.0",
|
|
52
|
+
"@zerotal/orm": "1.10.0",
|
|
53
|
+
"@zerotal/validator": "1.10.0",
|
|
54
54
|
"alpinejs": "^3.14.9",
|
|
55
55
|
"typescript": "^5.8.0"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
|
-
"@zerotal/broadcasting": "1.
|
|
58
|
+
"@zerotal/broadcasting": "1.10.0",
|
|
59
59
|
"@types/alpinejs": "^3.13.10",
|
|
60
60
|
"@types/alpinejs__anchor": "^3.13.1",
|
|
61
61
|
"@types/alpinejs__collapse": "^3.13.4",
|
package/src/Component.ts
CHANGED
|
@@ -68,6 +68,7 @@ export class ErrorField {
|
|
|
68
68
|
// this.errors.has("field") → boolean
|
|
69
69
|
// this.errors.field → ErrorField (for error={this.errors.field})
|
|
70
70
|
|
|
71
|
+
/** @internal */
|
|
71
72
|
export interface ErrorsProxy {
|
|
72
73
|
/** True when the given field has at least one error. */
|
|
73
74
|
has(field: string): boolean;
|
|
@@ -265,6 +266,8 @@ function _autoChildKey(
|
|
|
265
266
|
* The bag of side effects an action queued (flashes, a redirect, client scripts, events,
|
|
266
267
|
* downloads, a title, and the current error bag). Drained by {@link Component._drainEffects}
|
|
267
268
|
* and read by the WebSocket handler to build the response frames sent to the client.
|
|
269
|
+
*
|
|
270
|
+
* @internal
|
|
268
271
|
*/
|
|
269
272
|
export interface FlowEffects {
|
|
270
273
|
flashes: FlashMessage[];
|
|
@@ -303,21 +306,21 @@ export interface RedirectFlash {
|
|
|
303
306
|
* for `level` (Tailwind/Notyf parlance).
|
|
304
307
|
*/
|
|
305
308
|
export interface FlashOptions {
|
|
306
|
-
level?: FlashLevel;
|
|
309
|
+
level?: FlashLevel | undefined;
|
|
307
310
|
/** Alias for `level`. */
|
|
308
|
-
type?: FlashLevel;
|
|
309
|
-
title?: string;
|
|
310
|
-
position?: FlashPosition;
|
|
311
|
+
type?: FlashLevel | undefined;
|
|
312
|
+
title?: string | undefined;
|
|
313
|
+
position?: FlashPosition | undefined;
|
|
311
314
|
/** Auto-dismiss after N ms. `0` keeps the toast until dismissed. */
|
|
312
|
-
duration?: number;
|
|
313
|
-
dismissible?: boolean;
|
|
315
|
+
duration?: number | undefined;
|
|
316
|
+
dismissible?: boolean | undefined;
|
|
314
317
|
/** Custom icon (emoji/text), or `false` to hide the status icon. */
|
|
315
|
-
icon?: string | false;
|
|
316
|
-
progressBar?: boolean;
|
|
318
|
+
icon?: string | false | undefined;
|
|
319
|
+
progressBar?: boolean | undefined;
|
|
317
320
|
/** Action buttons that each invoke a `@expose` method on click. */
|
|
318
|
-
actions?: FlashAction[];
|
|
321
|
+
actions?: FlashAction[] | undefined;
|
|
319
322
|
/** A `@expose` method invoked when the toast is dismissed. */
|
|
320
|
-
onClose?: FlashCallback;
|
|
323
|
+
onClose?: FlashCallback | undefined;
|
|
321
324
|
}
|
|
322
325
|
|
|
323
326
|
/**
|
package/src/Form.ts
CHANGED
|
@@ -29,7 +29,11 @@ import type { FieldRule, Schema } from "@zerotal/validator";
|
|
|
29
29
|
/** Registry of Form subclasses by name, used to reconstruct forms on hydration. */
|
|
30
30
|
const _formClasses = new Map<string, new () => Form>();
|
|
31
31
|
|
|
32
|
-
/**
|
|
32
|
+
/**
|
|
33
|
+
* Register a Form subclass so it can be rebuilt from a snapshot. Called automatically.
|
|
34
|
+
*
|
|
35
|
+
* @internal
|
|
36
|
+
*/
|
|
33
37
|
export function registerForm(FormClass: new () => Form): void {
|
|
34
38
|
_formClasses.set(FormClass.name, FormClass);
|
|
35
39
|
}
|
|
@@ -29,14 +29,15 @@ import { CdpSession, findChrome, launchBrowser, type LaunchedBrowser } from "./c
|
|
|
29
29
|
/** How long to wait for a condition driven by a server round-trip. */
|
|
30
30
|
const DEFAULT_TIMEOUT_MS = 10_000;
|
|
31
31
|
|
|
32
|
+
/** @internal */
|
|
32
33
|
export interface OpenOptions {
|
|
33
34
|
/** Milliseconds to wait for the page to load and Flow to connect. */
|
|
34
|
-
timeout?: number;
|
|
35
|
+
timeout?: number | undefined;
|
|
35
36
|
/**
|
|
36
37
|
* Wait for Flow's WebSocket to report itself online before returning.
|
|
37
38
|
* Default: true. Set false for a page with no Flow component on it.
|
|
38
39
|
*/
|
|
39
|
-
waitForConnection?: boolean;
|
|
40
|
+
waitForConnection?: boolean | undefined;
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
export class FlowBrowser {
|
package/src/browser/cdp.ts
CHANGED
|
@@ -42,6 +42,8 @@ const CHROME_PATHS: Record<string, string[]> = {
|
|
|
42
42
|
* Returning `null` rather than throwing lets a suite skip itself on a machine
|
|
43
43
|
* without a browser instead of failing — a browser test that cannot run is not
|
|
44
44
|
* the same as one that failed.
|
|
45
|
+
*
|
|
46
|
+
* @internal
|
|
45
47
|
*/
|
|
46
48
|
export function findChrome(): string | null {
|
|
47
49
|
const override = Bun.env["CHROME_PATH"];
|
package/src/client/confirm.ts
CHANGED
|
@@ -15,17 +15,17 @@ export interface ConfirmDialogOptions {
|
|
|
15
15
|
/** Body text (the description, or the heading when no `title` is given). */
|
|
16
16
|
message: string;
|
|
17
17
|
/** Bold heading above the message. */
|
|
18
|
-
title?: string;
|
|
18
|
+
title?: string | undefined;
|
|
19
19
|
/** Confirm button label (default: "Confirm", or "Delete" for danger). */
|
|
20
|
-
confirmLabel?: string;
|
|
20
|
+
confirmLabel?: string | undefined;
|
|
21
21
|
/** Cancel button label (default: "Cancel"). */
|
|
22
|
-
cancelLabel?: string;
|
|
22
|
+
cancelLabel?: string | undefined;
|
|
23
23
|
/** `danger` = destructive (red); `primary` = neutral/indigo (default). */
|
|
24
|
-
variant?: "danger" | "primary";
|
|
24
|
+
variant?: "danger" | "primary" | undefined;
|
|
25
25
|
/** Custom icon (emoji/text/SVG markup), or `false` to hide the icon entirely. */
|
|
26
|
-
icon?: string | false;
|
|
26
|
+
icon?: string | false | undefined;
|
|
27
27
|
/** Show a type-to-confirm input; Confirm unlocks only when the value matches this. */
|
|
28
|
-
prompt?: string;
|
|
28
|
+
prompt?: string | undefined;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
const ICONS = {
|