@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/presence.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// ── @presence support (server side) ───────────────────────────────────────────
|
|
2
|
+
//
|
|
3
|
+
// `@presence` props are filled with the live member list of a broadcast presence
|
|
4
|
+
// channel. Broadcasting is an OPTIONAL peer: flow does not depend on it, so we
|
|
5
|
+
// resolve `Broadcast` through a variable-specifier dynamic import (not type-checked,
|
|
6
|
+
// so no hard dependency) and no-op gracefully when it isn't installed.
|
|
7
|
+
|
|
8
|
+
import type { Component } from "./Component.ts";
|
|
9
|
+
import { getPresenceProps, resolvePresenceChannel } from "./decorators.ts";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* One member of a broadcast presence channel — the shape used to populate a
|
|
13
|
+
* `@presence` prop (the live "who's here" list).
|
|
14
|
+
*
|
|
15
|
+
* Each member carries a required `id` plus whatever additional member data the
|
|
16
|
+
* broadcasting layer attached when the client joined (name, avatar, etc.).
|
|
17
|
+
*
|
|
18
|
+
* @remarks
|
|
19
|
+
* `@presence` props are server-controlled: flow refills them with the current
|
|
20
|
+
* member list before render and on each join/leave, so they are read-only on the
|
|
21
|
+
* client. Presence requires the optional `@zerotal/broadcasting` peer; without
|
|
22
|
+
* it, populating presence is a no-op and props keep their default.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```tsx
|
|
26
|
+
* class RoomHeader extends Component {
|
|
27
|
+
* @locked roomId = "";
|
|
28
|
+
* @presence((self) => `room.${self.roomId}`)
|
|
29
|
+
* members: PresenceMember[] = [];
|
|
30
|
+
*
|
|
31
|
+
* async render() {
|
|
32
|
+
* return <div>{this.members.length} online</div>;
|
|
33
|
+
* }
|
|
34
|
+
* }
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export interface PresenceMember {
|
|
38
|
+
/** Stable identifier for the member, as reported by broadcasting. */
|
|
39
|
+
id: string | number;
|
|
40
|
+
/** Arbitrary extra member data supplied when the client joined the channel. */
|
|
41
|
+
[key: string]: unknown;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface BroadcastFacade {
|
|
45
|
+
getMembers(channel: string): PresenceMember[];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
let _broadcast: BroadcastFacade | null | undefined;
|
|
49
|
+
|
|
50
|
+
async function _resolveBroadcast(): Promise<BroadcastFacade | null> {
|
|
51
|
+
if (_broadcast !== undefined) return _broadcast;
|
|
52
|
+
try {
|
|
53
|
+
// Variable specifier → TypeScript doesn't require the module to exist (optional peer).
|
|
54
|
+
const spec = "@zerotal/broadcasting";
|
|
55
|
+
const mod = (await import(spec)) as { Broadcast?: BroadcastFacade };
|
|
56
|
+
_broadcast = mod.Broadcast ?? null;
|
|
57
|
+
} catch {
|
|
58
|
+
_broadcast = null; // broadcasting not installed → presence is a no-op
|
|
59
|
+
}
|
|
60
|
+
return _broadcast;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Fill every `@presence` prop on `page` with the current member list for its channel.
|
|
65
|
+
* Called before render on the initial GET and on each `$presence` action (join/leave).
|
|
66
|
+
* No-op when the component has no presence props or broadcasting isn't installed.
|
|
67
|
+
*/
|
|
68
|
+
export async function populatePresence(page: Component): Promise<void> {
|
|
69
|
+
const props = getPresenceProps(page);
|
|
70
|
+
if (props.size === 0) return;
|
|
71
|
+
const Broadcast = await _resolveBroadcast();
|
|
72
|
+
if (!Broadcast) return;
|
|
73
|
+
for (const [prop, channel] of props) {
|
|
74
|
+
const name = resolvePresenceChannel(channel, page);
|
|
75
|
+
try {
|
|
76
|
+
(page as unknown as Record<string, unknown>)[prop] = Broadcast.getMembers(`presence-${name}`);
|
|
77
|
+
} catch {
|
|
78
|
+
/* leave the prop at its default on any lookup error */
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|