@weft-js/start 0.1.1 → 0.1.2

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 (213) hide show
  1. package/README.md +36 -3
  2. package/crate/Cargo.lock +1347 -0
  3. package/crate/Cargo.toml +72 -0
  4. package/crate/rust-toolchain.toml +3 -0
  5. package/crate/src/build/emit.rs +103 -0
  6. package/crate/src/build.rs +367 -0
  7. package/crate/src/ctx.rs +186 -0
  8. package/crate/src/envelope.rs +163 -0
  9. package/crate/src/functions.rs +173 -0
  10. package/crate/src/lib.rs +118 -0
  11. package/crate/src/native.rs +212 -0
  12. package/crate/src/otel.rs +62 -0
  13. package/crate/src/server.rs +203 -0
  14. package/package.json +46 -37
  15. package/src/app.mjs +38 -8
  16. package/src/asset-receipt.d.mts +13 -0
  17. package/src/asset-receipt.mjs +61 -0
  18. package/src/auth/better-auth.ts +215 -17
  19. package/src/auth/drizzle.ts +1 -1
  20. package/src/auth.ts +21 -6
  21. package/src/boot.ts +19 -8
  22. package/src/bus/postgres.ts +32 -19
  23. package/src/bus.ts +7 -4
  24. package/src/{capture.mjs → capture.ts} +34 -1
  25. package/src/changes-sql.d.mts +8 -2
  26. package/src/changes-sql.mjs +26 -10
  27. package/src/changes.ts +165 -66
  28. package/src/channel/connections.ts +216 -0
  29. package/src/channel/socket.ts +396 -0
  30. package/src/channel-fake.ts +19 -4
  31. package/src/channel.ts +120 -492
  32. package/src/client.tsx +9 -2
  33. package/src/contract/describe.mjs +231 -0
  34. package/src/contract.mjs +144 -157
  35. package/src/data/cache.ts +354 -0
  36. package/src/data/definitions.ts +94 -0
  37. package/src/data/draft.ts +104 -0
  38. package/src/data/foundation.ts +95 -0
  39. package/src/data/live-api.ts +22 -0
  40. package/src/data/mutation.ts +138 -0
  41. package/src/data/primitives.ts +135 -0
  42. package/src/data/query.ts +106 -0
  43. package/src/data/transport.ts +121 -0
  44. package/src/data/types.ts +303 -0
  45. package/src/data.ts +69 -894
  46. package/src/db/bun-sqlite.d.ts +4 -1
  47. package/src/db/changes-mysql.ts +61 -21
  48. package/src/db/changes-postgres.ts +197 -118
  49. package/src/db/changes-sqlite.ts +131 -64
  50. package/src/db/core.ts +38 -13
  51. package/src/db/index.ts +20 -4
  52. package/src/db/mysql.ts +22 -6
  53. package/src/db/postgres.ts +49 -13
  54. package/src/db/sqlite.ts +53 -27
  55. package/src/derive.d.mts +4 -1
  56. package/src/derive.mjs +136 -39
  57. package/src/dev-runtime.mjs +8 -4
  58. package/src/exec/errors.ts +57 -0
  59. package/src/exec/landing.ts +66 -0
  60. package/src/exec/scope.ts +174 -0
  61. package/src/exec/touch.ts +80 -0
  62. package/src/exec/validation.ts +176 -0
  63. package/src/exec.ts +191 -341
  64. package/src/extension.ts +8 -2
  65. package/src/first-paint.ts +26 -9
  66. package/src/form/value.ts +15 -0
  67. package/src/form.tsx +132 -43
  68. package/src/hub/channels.ts +102 -0
  69. package/src/hub/connections.ts +147 -0
  70. package/src/hub/delivery.ts +62 -0
  71. package/src/hub/feed.ts +168 -0
  72. package/src/hub/presence.ts +64 -0
  73. package/src/hub/route.ts +158 -0
  74. package/src/hub/subscriptions.ts +212 -0
  75. package/src/hub/topic.ts +98 -0
  76. package/src/hub/tracked.ts +12 -0
  77. package/src/hub/types.ts +190 -0
  78. package/src/hub/view.ts +147 -0
  79. package/src/hub.ts +132 -758
  80. package/src/index.ts +84 -10
  81. package/src/inline.mjs +356 -0
  82. package/src/inspect/compare.mjs +162 -0
  83. package/src/inspect/routes.mjs +154 -0
  84. package/src/inspect/samples.mjs +59 -0
  85. package/src/inspect/url.mjs +2 -0
  86. package/src/inspect.mjs +146 -187
  87. package/src/link/election.ts +178 -0
  88. package/src/link/lines.ts +27 -0
  89. package/src/link.ts +90 -166
  90. package/src/list-drizzle.ts +104 -18
  91. package/src/list.ts +127 -36
  92. package/src/listen.ts +32 -6
  93. package/src/live/api.ts +18 -0
  94. package/src/live/backoff.ts +6 -0
  95. package/src/live/frames.ts +42 -0
  96. package/src/live/read.ts +77 -0
  97. package/src/live/sub-types.ts +24 -0
  98. package/src/live/transport.ts +86 -0
  99. package/src/live/types.ts +52 -0
  100. package/src/live-fake.ts +20 -5
  101. package/src/live-target.mjs +112 -37
  102. package/src/live.ts +116 -225
  103. package/src/media.ts +8 -1
  104. package/src/migrations.mjs +14 -6
  105. package/src/mux.ts +161 -64
  106. package/src/og/DefaultCard.tsx +40 -9
  107. package/src/og/fonts.mjs +16 -8
  108. package/src/og/images.mjs +43 -22
  109. package/src/og/index.mjs +119 -43
  110. package/src/og/native.mjs +11 -4
  111. package/src/range.mjs +15 -3
  112. package/src/router.ts +56 -6
  113. package/src/routes.mjs +73 -22
  114. package/src/run/admission.ts +80 -0
  115. package/src/run/boot.tsx +115 -0
  116. package/src/run/cache-presentation.ts +67 -0
  117. package/src/run/cache.ts +254 -0
  118. package/src/run/helpers.ts +197 -0
  119. package/src/run/listeners.ts +155 -0
  120. package/src/run/navigation.ts +382 -0
  121. package/src/run/page-skeleton.tsx +35 -0
  122. package/src/run/prefetch.ts +128 -0
  123. package/src/run/retain-view.ts +18 -0
  124. package/src/run/shape-preparation.ts +97 -0
  125. package/src/run/shape-styles.ts +72 -0
  126. package/src/run/source-context/pin.ts +124 -0
  127. package/src/run/source-context.ts +311 -0
  128. package/src/run.tsx +50 -178
  129. package/src/rust/common.ts +163 -0
  130. package/src/rust/native.ts +282 -0
  131. package/src/rust.ts +103 -229
  132. package/src/server/card.tsx +65 -0
  133. package/src/server/compression.ts +115 -0
  134. package/src/server/core.ts +70 -0
  135. package/src/server/dispatch.ts +70 -0
  136. package/src/server/form.ts +85 -0
  137. package/src/server/functions.ts +129 -0
  138. package/src/server/handler.ts +39 -0
  139. package/src/server/head.ts +96 -0
  140. package/src/server/media.ts +60 -0
  141. package/src/server/navigation-cache.ts +80 -0
  142. package/src/server/navigation.tsx +85 -0
  143. package/src/server/page-cold.ts +95 -0
  144. package/src/server/page-stream.tsx +181 -0
  145. package/src/server/page-types.ts +51 -0
  146. package/src/server/page-warm.ts +130 -0
  147. package/src/server/page.ts +69 -0
  148. package/src/server/payload.ts +89 -0
  149. package/src/server/prerender.ts +60 -0
  150. package/src/server/preview.tsx +67 -0
  151. package/src/server/render.ts +167 -0
  152. package/src/server/request.ts +98 -0
  153. package/src/server/resolver.ts +228 -0
  154. package/src/server/routes.ts +150 -0
  155. package/src/server/services.ts +69 -0
  156. package/src/server/text.ts +29 -0
  157. package/src/server/types.ts +160 -0
  158. package/src/server.tsx +82 -971
  159. package/src/shape.mjs +30 -6
  160. package/src/share/display.ts +50 -0
  161. package/src/share/network.ts +119 -0
  162. package/src/share/receive.ts +70 -0
  163. package/src/share/state.ts +30 -0
  164. package/src/share/tab-queue.ts +28 -0
  165. package/src/share-server.ts +128 -43
  166. package/src/share.ts +103 -209
  167. package/src/sheets.mjs +30 -18
  168. package/src/sidecar/build.mjs +170 -0
  169. package/src/sidecar/cargo.mjs +164 -0
  170. package/src/sidecar/common.mjs +17 -0
  171. package/src/sidecar/dev-build.mjs +91 -0
  172. package/src/sidecar/dev-watch.mjs +88 -0
  173. package/src/sidecar/manifest.mjs +19 -0
  174. package/src/sidecar/process.mjs +58 -0
  175. package/src/sidecar/refresh.mjs +19 -0
  176. package/src/sidecar.mjs +75 -416
  177. package/src/socket/check.ts +50 -0
  178. package/src/socket/deliver.ts +16 -0
  179. package/src/socket/publish.ts +56 -0
  180. package/src/socket/rooms.ts +41 -0
  181. package/src/socket/route.ts +79 -0
  182. package/src/socket/shared.ts +26 -0
  183. package/src/socket/types.ts +48 -0
  184. package/src/socket.ts +205 -194
  185. package/src/stream.ts +10 -1
  186. package/src/verify/app.ts +39 -0
  187. package/src/verify/cleanup.ts +27 -0
  188. package/src/verify/database.ts +11 -0
  189. package/src/verify/live.ts +63 -0
  190. package/src/verify/notify.ts +35 -0
  191. package/src/verify/skip.ts +19 -0
  192. package/src/verify/support.ts +149 -0
  193. package/src/verify/timing.ts +15 -0
  194. package/src/verify/trigger.ts +14 -0
  195. package/src/verify.ts +77 -176
  196. package/src/view.tsx +96 -11
  197. package/src/vite/asset-bytes.mjs +95 -0
  198. package/src/vite/assets.mjs +117 -0
  199. package/src/vite/config-hooks.mjs +166 -0
  200. package/src/vite/dev-hooks.mjs +103 -0
  201. package/src/vite/dev.mjs +102 -0
  202. package/src/vite/entries.mjs +145 -0
  203. package/src/vite/module-hooks.mjs +288 -0
  204. package/src/vite/route-modules.mjs +289 -0
  205. package/src/vite/server-functions.mjs +222 -0
  206. package/src/vite/shape-entry-css.mjs +187 -0
  207. package/src/vite/shape-override.mjs +226 -0
  208. package/src/vite/shared.mjs +51 -0
  209. package/src/vite/sheets.mjs +92 -0
  210. package/src/vite/state.mjs +89 -0
  211. package/src/vite/syntax.mjs +139 -0
  212. package/src/vite.mjs +152 -853
  213. package/src/capture.d.mts +0 -1
@@ -0,0 +1,12 @@
1
+ /** A message with its cursor: `yield tracked(row.id, row)`, and a resumed `run` gets it as `lastEventId`. */
2
+ export class Tracked<M> {
3
+ constructor(
4
+ public id: string,
5
+ public value: M,
6
+ ) {}
7
+ }
8
+ /** A message with an id, so a stream can resume after it. */
9
+ export const tracked = <M>(id: string, value: M) => new Tracked(id, value)
10
+
11
+ /** Thrown out of `run` when its cursor can't resume: the stream restarts without one. */
12
+ export const RESET = new Error('weft.reset')
@@ -0,0 +1,190 @@
1
+ import type { Bus } from '../bus'
2
+ import type { Source } from '../changes'
3
+ import type { Fn, LiveMode, StartError } from '../data'
4
+ import type { Scope, ScopeOptions, Store } from '../exec'
5
+ import type { ShareRule } from '../share-server'
6
+
7
+ /** What one connection may cost; every number has a default in `LIMITS`. */
8
+ export interface Limits {
9
+ subscriptions: number
10
+ /** Per signed-in user. */
11
+ connections: number
12
+ /** Bytes of one request (client→server). */
13
+ body: number
14
+ /** Bytes of one message (server→client). */
15
+ message: number
16
+ /** Bytes one connection may hold before it's closed. */
17
+ queue: number
18
+ /** Events a ring keeps. */
19
+ ring: number
20
+ /** Ms a ring keeps them. */
21
+ window: number
22
+ /** Ms between `: ping`s. */
23
+ heartbeat: number
24
+ /** Ms between session re-checks of `auth` subscriptions and channel joins. */
25
+ session: number
26
+ /** Channel messages a client may send per second, per socket. */
27
+ perSecond: number
28
+ /** The burst `perSecond` allows. */
29
+ burst: number
30
+ /** Keys one socket may `share`. They cost no joins: a 200-row list is one. */
31
+ shareKeys: number
32
+ }
33
+
34
+ /** `realtime()`: the options of an app's `src/realtime.server.ts`. */
35
+ export interface RealtimeOptions {
36
+ /** Default: `memoryBus()`, one instance only (R002). */
37
+ bus?: Bus
38
+ /** Origins allowed besides the app's own, exact: scheme, host and port. */
39
+ origins?: string[]
40
+ limits?: Partial<Limits>
41
+ /** Seconds the platform lets a response live: streams end 10 s earlier, and the client resumes. */
42
+ maxDuration?: number
43
+ /** Writes from outside the app: `changes: dbChanges(db, [todos])`, or a source of your own. */
44
+ changes?: Source
45
+ /**
46
+ * Who may share or see a key of `interaction({ share })` with other clients. Left out, nobody: tabs still work. One
47
+ * key at a time, `({ ctx, key }) => boolean`, or `{ keys: ({ ctx, keys }) => allowed }` to answer a whole join at once.
48
+ */
49
+ share?: ShareRule | undefined
50
+ }
51
+
52
+ /** What the app gets from `configure`, beside `src/realtime.server.ts`. */
53
+ export interface HubOptions extends RealtimeOptions {
54
+ build?: string | undefined
55
+ dev?: boolean | undefined
56
+ /** Queries marked `live: { required: true }`: without a real feed the server refuses to run (R015). */
57
+ required?: string[] | undefined
58
+ store?: Store | undefined
59
+ onDiagnostic?: ScopeOptions['onDiagnostic'] | undefined
60
+ }
61
+
62
+ /** What `.live` reports, and a health route reads. */
63
+ export interface LiveState {
64
+ mode: LiveMode
65
+ via: string
66
+ why?: string | undefined
67
+ fix?: string | undefined
68
+ /** The feed works in general and is out right now. */
69
+ down?: string | undefined
70
+ }
71
+
72
+ /** Where a connection's frames go. `data` is JSON. */
73
+ export interface Sink {
74
+ write(event: string, data: string): void
75
+ ping?(): void
76
+ /** Bytes written and not yet taken by the client. */
77
+ backlog(): number
78
+ close(): void
79
+ }
80
+
81
+ /** What the client sends (research 30 §4): `[n, kind, id, input, cursor, buffer]`. */
82
+ export type SubEntry = [
83
+ n: number,
84
+ kind: 'q' | 's',
85
+ id: string,
86
+ input?: unknown,
87
+ cursor?: string,
88
+ buffer?: number,
89
+ ]
90
+ /** The body of `/_weft/rt`: the cursor, subscriptions to add, and subscriptions to drop. */
91
+ export interface Body {
92
+ k?: string
93
+ c?: string | undefined
94
+ s?: SubEntry[]
95
+ d?: number[]
96
+ }
97
+
98
+ /** One message in a channel's ring. */
99
+ export interface Ev {
100
+ seq: number
101
+ at: number
102
+ raw: string
103
+ /** Parsed once, for every socket of a room. */ p?: unknown
104
+ }
105
+ /** One hub channel: its replay ring and who's listening on this instance. */
106
+ export interface Channel {
107
+ name: string
108
+ ring: Ev[]
109
+ floor: number
110
+ listeners: Set<(ev: Ev | null) => void>
111
+ /** Take a message as if the bus had brought it: a change feed reaches this instance's connections only. */
112
+ got: (raw: string) => void
113
+ ready: Promise<void>
114
+ stop?: () => void
115
+ idle?: ReturnType<typeof setTimeout> | undefined
116
+ }
117
+
118
+ export interface Sub {
119
+ n: number
120
+ kind: 'q' | 's'
121
+ fn: Fn
122
+ input: unknown
123
+ tags: string[]
124
+ buffer: number
125
+ q: { raw: string; size: number }[]
126
+ ac: AbortController
127
+ request: Request
128
+ options: ScopeOptions
129
+ }
130
+
131
+ /** One client's realtime connection. */
132
+ export interface Conn {
133
+ k: string
134
+ sink: Sink
135
+ user?: string | undefined
136
+ subs: Map<number, Sub>
137
+ tags: Map<string, number>
138
+ held: Set<Sub>
139
+ pokes?: { t: Set<string>; c: string } | undefined
140
+ bytes: number
141
+ timers: ReturnType<typeof setTimeout>[]
142
+ }
143
+
144
+ /** The realtime server of this process: channels, connections and the change feed. */
145
+ export interface Hub {
146
+ readonly bus: Bus
147
+ readonly limits: Limits
148
+ /** `realtime({ share })`. */
149
+ readonly share?: ShareRule | undefined
150
+ poke(tags: string[], origin?: string | null): Promise<void>
151
+ cursor(): string | undefined
152
+ /** Listening for pokes, or 250 ms: a page waits this long for a cursor, and goes without one. */
153
+ ready(): Promise<unknown>
154
+ cursorOf(seq: number): string
155
+ channel(name: string): Channel
156
+ replay(ch: Channel, after: string): Ev[] | undefined
157
+ unlisten(ch: Channel, on: (ev: Ev | null) => void): void
158
+ /** `line`: one tab on a shared socket (mux.ts), which `limits.connections` counted already. */
159
+ connect(request: Request, scope: Scope, body: Body, sink: Sink, line?: boolean): Promise<Conn>
160
+ change(c: Conn, request: Request, options: ScopeOptions, body: Body): Promise<void>
161
+ drain(c: Conn): void
162
+ close(c: Conn): void
163
+ find(k: string): Conn | undefined
164
+ connections(): Conn[]
165
+ /** @weft-js/core/test: a message straight to every reader of a stream. */
166
+ push(id: string, msg: unknown): number
167
+ /** Server-side counts for tests and `weft browser`. */
168
+ stats(): { connections: number; subscriptions: number; channels: number }
169
+ /** The live mode this server settled on: `.liveMode` on the client, and health routes. */
170
+ live(): LiveState
171
+ required: boolean
172
+ /** Settled once the source has been probed. */
173
+ probed: Promise<unknown>
174
+ /** R015: required live queries, and no feed. Requests get this instead of stale data. */
175
+ refused?: StartError | undefined
176
+ shutdown(): Promise<void>
177
+ /** The position a new listener resumes from. */
178
+ position(): string
179
+ /** Every poked tag, on this instance: socket.ts's `session:<user>` re-check. */
180
+ watch(fn: (tags: string[]) => void): void
181
+ /** Run at shutdown: socket.ts closes its sockets. */
182
+ closing: Set<() => void>
183
+ /** `room.publish` checks its messages (C002). */
184
+ dev?: boolean | undefined
185
+ /** The instance's name in a shared value's stamp. */
186
+ epoch: string
187
+ /** Seconds a connection may live (`RealtimeOptions.maxDuration`). */
188
+ maxDuration?: number | undefined
189
+ build?: string | undefined
190
+ }
@@ -0,0 +1,147 @@
1
+ import type { Bus } from '../bus'
2
+ import type { createChannels } from './channels'
3
+ import type { createConnections } from './connections'
4
+ import type { createDelivery } from './delivery'
5
+ import type { createFeedState } from './feed'
6
+ import type { createSubscriptions } from './subscriptions'
7
+ import type { Channel, Conn, Hub, HubOptions, Limits } from './types'
8
+
9
+ const unref = <T>(t: T) => ((t as { unref?(): void } | null | undefined)?.unref?.(), t)
10
+ interface View {
11
+ bus: Bus
12
+ limits: Limits
13
+ epoch: string
14
+ options: HubOptions
15
+ channels: Map<string, Channel>
16
+ conns: Map<string, Conn>
17
+ watchers: Set<(tags: string[]) => void>
18
+ closing: Set<() => void>
19
+ cursorOf: (n: number) => string
20
+ channel: ReturnType<typeof createChannels>['channel']
21
+ replay: ReturnType<typeof createChannels>['replay']
22
+ unlisten: ReturnType<typeof createChannels>['unlisten']
23
+ seq: () => number
24
+ pokes: () => Channel
25
+ want: ReturnType<typeof createFeedState>['want']
26
+ rest: ReturnType<typeof createFeedState>['rest']
27
+ feedState: ReturnType<typeof createFeedState>
28
+ feed: ReturnType<typeof createFeedState>['feed']
29
+ probed: Promise<unknown>
30
+ deliver: ReturnType<typeof createDelivery>['deliver']
31
+ drain: ReturnType<typeof createDelivery>['drain']
32
+ connect: ReturnType<typeof createConnections>['connect']
33
+ close: ReturnType<typeof createConnections>['close']
34
+ change: ReturnType<typeof createSubscriptions>['change']
35
+ }
36
+
37
+ export function createHubView(deps: View): Hub {
38
+ const {
39
+ bus,
40
+ limits,
41
+ epoch,
42
+ options,
43
+ channels,
44
+ conns,
45
+ watchers,
46
+ closing,
47
+ cursorOf,
48
+ channel,
49
+ replay,
50
+ unlisten,
51
+ seq,
52
+ pokes,
53
+ feedState,
54
+ probed,
55
+ drain,
56
+ connect,
57
+ close,
58
+ change,
59
+ } = deps
60
+ const { ready, poke, push, shutdown } = createOperations(deps)
61
+ return {
62
+ bus,
63
+ limits,
64
+ cursorOf,
65
+ channel,
66
+ replay,
67
+ unlisten,
68
+ connect,
69
+ change,
70
+ drain,
71
+ close,
72
+ closing,
73
+ epoch,
74
+ dev: options.dev,
75
+ maxDuration: options.maxDuration,
76
+ build: options.build,
77
+ share: options.share,
78
+ position: () => cursorOf(seq()),
79
+ watch(fn) {
80
+ watchers.add(fn)
81
+ void pokes()
82
+ },
83
+ find: (k) => conns.get(k),
84
+ connections: () => [...conns.values()],
85
+ cursor: () => (pokes().stop ? cursorOf(seq()) : undefined),
86
+ ready,
87
+ poke,
88
+ push,
89
+ stats: () => ({
90
+ connections: conns.size,
91
+ subscriptions: [...conns.values()].reduce((n, c) => n + c.subs.size, 0),
92
+ channels: channels.size,
93
+ }),
94
+ live: feedState.live,
95
+ probed,
96
+ required: !!options.required?.length,
97
+ get refused() {
98
+ return feedState.refused()
99
+ },
100
+ shutdown,
101
+ }
102
+ }
103
+
104
+ function createOperations(deps: View) {
105
+ const { want, rest, pokes, feedState, feed, bus, conns, deliver, closing, close, channels } = deps
106
+ return {
107
+ ready: () => {
108
+ const on = Promise.resolve(want()).then(rest)
109
+ return Promise.race([
110
+ Promise.all([pokes().ready.catch(() => {}), on]),
111
+ new Promise((ok) => {
112
+ unref(setTimeout(ok, 250))
113
+ }),
114
+ ])
115
+ },
116
+ async poke(this: void, tags: string[], origin?: string | null) {
117
+ if (!tags.length) return
118
+ // In this order: a client's refetch must start after what the barrier claims is covered.
119
+ if (feedState.live().mode === 'feed' || feedState.live().mode === 'external')
120
+ await feed?.settle(tags)
121
+ await bus.publish('weft:poke', JSON.stringify({ t: tags, ...(origin && { o: origin }) }))
122
+ },
123
+ push(this: void, id: string, msg: unknown) {
124
+ let hit = 0
125
+ for (const c of conns.values()) {
126
+ for (const s of c.subs.values()) {
127
+ if (s.fn.$id !== id) continue
128
+ hit++
129
+ deliver(c, s, JSON.stringify([s.n, null, msg]))
130
+ }
131
+ }
132
+ return hit
133
+ },
134
+ async shutdown(this: void) {
135
+ feedState.clear()
136
+ await feed?.stop()
137
+ for (const fn of closing) fn()
138
+ for (const c of conns.values()) close(c)
139
+ for (const ch of channels.values()) {
140
+ clearTimeout(ch.idle)
141
+ ch.stop?.()
142
+ }
143
+ channels.clear()
144
+ await bus.close?.()
145
+ },
146
+ }
147
+ }