@zerotal/flow 1.0.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 +61 -0
- package/LICENSE +21 -0
- package/README.md +148 -0
- package/package.json +84 -0
- package/src/Component.ts +1498 -0
- package/src/Form.ts +189 -0
- package/src/Layout.ts +47 -0
- package/src/augment.ts +94 -0
- package/src/client/FlowClientError.ts +12 -0
- package/src/client/FlowComponent.ts +96 -0
- package/src/client/alpinejs-morph.d.ts +5 -0
- package/src/client/bridge.ts +3463 -0
- package/src/client/confirm.ts +338 -0
- package/src/client/cspAdapter.ts +79 -0
- package/src/client/cspEvaluator.ts +494 -0
- package/src/client/errorOverlay.ts +124 -0
- package/src/client/headless.ts +948 -0
- package/src/client/index.csp.ts +47 -0
- package/src/client/index.ts +56 -0
- package/src/client/optimistic.ts +52 -0
- package/src/client/timeline.ts +160 -0
- package/src/client/url.ts +51 -0
- package/src/commands/MakeFlowCommand.ts +290 -0
- package/src/compiler/cache.ts +101 -0
- package/src/compiler/directives.ts +107 -0
- package/src/compiler/helpers.ts +26 -0
- package/src/compiler/index.ts +172 -0
- package/src/compiler/transform.ts +2070 -0
- package/src/components.ts +1465 -0
- package/src/config.ts +52 -0
- package/src/decorators.ts +978 -0
- package/src/dehydrate.ts +333 -0
- package/src/durable.ts +243 -0
- package/src/events.ts +118 -0
- package/src/frameworkEvents.ts +40 -0
- package/src/headless.ts +1147 -0
- package/src/index.ts +267 -0
- package/src/jsx-dev-runtime.ts +14 -0
- package/src/jsx-runtime.ts +1272 -0
- package/src/mixins.ts +199 -0
- package/src/observability.ts +167 -0
- package/src/pagination.ts +300 -0
- package/src/presence.ts +81 -0
- package/src/provider/FlowProvider.ts +1401 -0
- package/src/registry.ts +118 -0
- package/src/renderContext.ts +55 -0
- package/src/router.ts +401 -0
- package/src/shared.ts +226 -0
- package/src/store.ts +116 -0
- package/src/synths/CarbonIntervalSynth.ts +22 -0
- package/src/synths/CarbonSynth.ts +20 -0
- package/src/synths/CollectionSynth.ts +62 -0
- package/src/synths/DateSynth.ts +24 -0
- package/src/synths/ModelSynth.ts +78 -0
- package/src/synths/TemporaryUploadedFileSynth.ts +19 -0
- package/src/synths/index.ts +148 -0
- package/src/testing.ts +361 -0
- package/src/types.ts +315 -0
- package/src/uploads/FileUploads.ts +94 -0
- package/src/uploads/TemporaryUploadedFile.ts +274 -0
- package/src/uploads/sign.ts +18 -0
- package/src/urlSafety.ts +0 -0
- package/src/utils.ts +216 -0
- package/src/validation.ts +55 -0
package/src/events.ts
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// ── Type-safe real-time events ─────────────────────────────────────────────────
|
|
2
|
+
//
|
|
3
|
+
// `dispatch` / `dispatchTo` / `dispatchSelf` and `@on` are string-keyed at runtime, but their
|
|
4
|
+
// payloads can be type-checked end-to-end by declaring a contract — an app-augmentable interface
|
|
5
|
+
// that maps each event name to its payload type. No codegen: augment `FlowEvents` once and every
|
|
6
|
+
// dispatch site is checked, from server actions and client expressions alike.
|
|
7
|
+
//
|
|
8
|
+
// // app/flow-events.d.ts
|
|
9
|
+
// declare module "@zerotal/flow" {
|
|
10
|
+
// interface FlowEvents {
|
|
11
|
+
// "post-created": { id: number; title: string };
|
|
12
|
+
// "cart-cleared": void; // no payload
|
|
13
|
+
// }
|
|
14
|
+
// }
|
|
15
|
+
//
|
|
16
|
+
// Unknown event names still work (a `string` fallback overload keeps `echo:…` broadcasts and
|
|
17
|
+
// gradual adoption compiling), they're just untyped. Payloads are object shapes or `void`.
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The realtime event contract: maps each event name to its payload type. Empty by default; apps
|
|
21
|
+
* augment it via TypeScript module augmentation so every dispatch site and `@on` handler is
|
|
22
|
+
* type-checked end-to-end, with no codegen. `keyof FlowEvents` drives dispatch/`@on`
|
|
23
|
+
* autocomplete.
|
|
24
|
+
*
|
|
25
|
+
* @remarks
|
|
26
|
+
* Payloads are object shapes or `void` (for events with no payload). Unknown event names still
|
|
27
|
+
* compile (a `string` fallback keeps `echo:…` broadcasts and gradual adoption working); they are
|
|
28
|
+
* simply untyped.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* // app/flow-events.d.ts
|
|
33
|
+
* declare module "@zerotal/flow" {
|
|
34
|
+
* interface FlowEvents {
|
|
35
|
+
* "post-created": { id: number; title: string };
|
|
36
|
+
* "cart-cleared": void; // no payload
|
|
37
|
+
* }
|
|
38
|
+
* }
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* @category Realtime
|
|
42
|
+
*/
|
|
43
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
|
44
|
+
export interface FlowEvents {}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* A known event name — a key of the augmented {@link FlowEvents} contract.
|
|
48
|
+
*
|
|
49
|
+
* @category Realtime
|
|
50
|
+
*/
|
|
51
|
+
export type EventName = keyof FlowEvents;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The payload type for a known event `K`, resolved from the {@link FlowEvents} contract. Use it
|
|
55
|
+
* to type an `@on` handler's parameter, e.g. `EventPayload<"post-created">`.
|
|
56
|
+
*
|
|
57
|
+
* @category Realtime
|
|
58
|
+
*/
|
|
59
|
+
export type EventPayload<K extends EventName> = FlowEvents[K];
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The trailing argument(s) of a dispatch call for payload `P`: none when `P` is `void`
|
|
63
|
+
* (`dispatch("cart-cleared")`), otherwise a single typed payload.
|
|
64
|
+
*/
|
|
65
|
+
export type EventArgs<P> = [P] extends [void] ? [] : [payload: P];
|
|
66
|
+
|
|
67
|
+
/** An event name loosened to accept any string (autocomplete for known names, `echo:…` too). */
|
|
68
|
+
export type LooseEventName = EventName | (string & {});
|
|
69
|
+
|
|
70
|
+
// ── Optional runtime shape guards ──────────────────────────────────────────────
|
|
71
|
+
// The types cover dispatch sites at compile time; a guard adds a runtime check for payloads that
|
|
72
|
+
// arrive from an untrusted source (a client-originated dispatch) or to catch a refactor that the
|
|
73
|
+
// types missed. Opt in per event; a failed guard throws from dispatch().
|
|
74
|
+
|
|
75
|
+
type Guard = (payload: unknown) => boolean;
|
|
76
|
+
const _guards = new Map<string, Guard>();
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Registers a runtime guard for a known event's payload.
|
|
80
|
+
*
|
|
81
|
+
* @remarks
|
|
82
|
+
* The compile-time {@link FlowEvents} contract covers dispatch sites at build time; this adds a
|
|
83
|
+
* runtime check for payloads arriving from an untrusted source (a client-originated dispatch) or
|
|
84
|
+
* to catch a refactor the types missed. Opt in per event. When a guard is set, `dispatch` (and
|
|
85
|
+
* its variants) throw a `TypeError` if the payload doesn't satisfy it — surfacing a malformed
|
|
86
|
+
* payload loudly instead of letting it flow to listeners. Registering the same event again
|
|
87
|
+
* replaces its guard.
|
|
88
|
+
*
|
|
89
|
+
* @param name - The known event name to guard (a key of {@link FlowEvents}).
|
|
90
|
+
* @param guard - A type-guard predicate that narrows an `unknown` payload to the event's type.
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```ts
|
|
94
|
+
* registerFlowEvent("post-created", (p): p is { id: number } =>
|
|
95
|
+
* typeof (p as { id?: unknown })?.id === "number");
|
|
96
|
+
* ```
|
|
97
|
+
*
|
|
98
|
+
* @category Realtime
|
|
99
|
+
*/
|
|
100
|
+
export function registerFlowEvent<K extends EventName>(
|
|
101
|
+
name: K,
|
|
102
|
+
guard: (payload: unknown) => payload is FlowEvents[K],
|
|
103
|
+
): void {
|
|
104
|
+
_guards.set(name as string, guard as Guard);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** @internal Validate a dispatched payload against its registered guard; throws on mismatch. */
|
|
108
|
+
export function _validateEventPayload(name: string, payload: unknown): void {
|
|
109
|
+
const guard = _guards.get(name);
|
|
110
|
+
if (guard && !guard(payload)) {
|
|
111
|
+
throw new TypeError(`[Flow] dispatch("${name}") payload failed its registered guard.`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** @internal Test hook — clear all registered guards. */
|
|
116
|
+
export function _resetEventGuards(): void {
|
|
117
|
+
_guards.clear();
|
|
118
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flow's realtime **framework** events, emitted on core's {@link FrameworkEvents}
|
|
3
|
+
* bus (distinct from the app-facing `FlowEvents` realtime contract in
|
|
4
|
+
* `./events.ts`). Observability packages subscribe to them by kind (their class name).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Emitted when a Flow (reactive SSR) WebSocket connection opens.
|
|
9
|
+
* @category Realtime
|
|
10
|
+
*/
|
|
11
|
+
export class WebSocketConnected {
|
|
12
|
+
constructor(readonly url: string) {}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Emitted when a Flow WebSocket connection closes.
|
|
17
|
+
* @category Realtime
|
|
18
|
+
*/
|
|
19
|
+
export class WebSocketDisconnected {
|
|
20
|
+
constructor(readonly url: string) {}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Emitted after a Flow component action round-trips over the WebSocket. Carries
|
|
25
|
+
* the same rich context an HTTP request does: `ip`, and the request-scoped `ctx`
|
|
26
|
+
* (an HttpContext with `user` set by re-run middleware, and the SQL queries that
|
|
27
|
+
* ran during the action correlated to it).
|
|
28
|
+
*
|
|
29
|
+
* @category Realtime
|
|
30
|
+
*/
|
|
31
|
+
export class FlowActionHandled {
|
|
32
|
+
constructor(
|
|
33
|
+
readonly component: string,
|
|
34
|
+
readonly action: string,
|
|
35
|
+
readonly durationMs: number,
|
|
36
|
+
readonly ok: boolean,
|
|
37
|
+
readonly ip: string | null,
|
|
38
|
+
readonly ctx: object | undefined,
|
|
39
|
+
) {}
|
|
40
|
+
}
|