@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.
Files changed (64) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/LICENSE +21 -0
  3. package/README.md +148 -0
  4. package/package.json +84 -0
  5. package/src/Component.ts +1498 -0
  6. package/src/Form.ts +189 -0
  7. package/src/Layout.ts +47 -0
  8. package/src/augment.ts +94 -0
  9. package/src/client/FlowClientError.ts +12 -0
  10. package/src/client/FlowComponent.ts +96 -0
  11. package/src/client/alpinejs-morph.d.ts +5 -0
  12. package/src/client/bridge.ts +3463 -0
  13. package/src/client/confirm.ts +338 -0
  14. package/src/client/cspAdapter.ts +79 -0
  15. package/src/client/cspEvaluator.ts +494 -0
  16. package/src/client/errorOverlay.ts +124 -0
  17. package/src/client/headless.ts +948 -0
  18. package/src/client/index.csp.ts +47 -0
  19. package/src/client/index.ts +56 -0
  20. package/src/client/optimistic.ts +52 -0
  21. package/src/client/timeline.ts +160 -0
  22. package/src/client/url.ts +51 -0
  23. package/src/commands/MakeFlowCommand.ts +290 -0
  24. package/src/compiler/cache.ts +101 -0
  25. package/src/compiler/directives.ts +107 -0
  26. package/src/compiler/helpers.ts +26 -0
  27. package/src/compiler/index.ts +172 -0
  28. package/src/compiler/transform.ts +2070 -0
  29. package/src/components.ts +1465 -0
  30. package/src/config.ts +52 -0
  31. package/src/decorators.ts +978 -0
  32. package/src/dehydrate.ts +333 -0
  33. package/src/durable.ts +243 -0
  34. package/src/events.ts +118 -0
  35. package/src/frameworkEvents.ts +40 -0
  36. package/src/headless.ts +1147 -0
  37. package/src/index.ts +267 -0
  38. package/src/jsx-dev-runtime.ts +14 -0
  39. package/src/jsx-runtime.ts +1272 -0
  40. package/src/mixins.ts +199 -0
  41. package/src/observability.ts +167 -0
  42. package/src/pagination.ts +300 -0
  43. package/src/presence.ts +81 -0
  44. package/src/provider/FlowProvider.ts +1401 -0
  45. package/src/registry.ts +118 -0
  46. package/src/renderContext.ts +55 -0
  47. package/src/router.ts +401 -0
  48. package/src/shared.ts +226 -0
  49. package/src/store.ts +116 -0
  50. package/src/synths/CarbonIntervalSynth.ts +22 -0
  51. package/src/synths/CarbonSynth.ts +20 -0
  52. package/src/synths/CollectionSynth.ts +62 -0
  53. package/src/synths/DateSynth.ts +24 -0
  54. package/src/synths/ModelSynth.ts +78 -0
  55. package/src/synths/TemporaryUploadedFileSynth.ts +19 -0
  56. package/src/synths/index.ts +148 -0
  57. package/src/testing.ts +361 -0
  58. package/src/types.ts +315 -0
  59. package/src/uploads/FileUploads.ts +94 -0
  60. package/src/uploads/TemporaryUploadedFile.ts +274 -0
  61. package/src/uploads/sign.ts +18 -0
  62. package/src/urlSafety.ts +0 -0
  63. package/src/utils.ts +216 -0
  64. 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
+ }