@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.
- package/README.md +36 -3
- package/crate/Cargo.lock +1347 -0
- package/crate/Cargo.toml +72 -0
- package/crate/rust-toolchain.toml +3 -0
- package/crate/src/build/emit.rs +103 -0
- package/crate/src/build.rs +367 -0
- package/crate/src/ctx.rs +186 -0
- package/crate/src/envelope.rs +163 -0
- package/crate/src/functions.rs +173 -0
- package/crate/src/lib.rs +118 -0
- package/crate/src/native.rs +212 -0
- package/crate/src/otel.rs +62 -0
- package/crate/src/server.rs +203 -0
- package/package.json +46 -37
- package/src/app.mjs +38 -8
- package/src/asset-receipt.d.mts +13 -0
- package/src/asset-receipt.mjs +61 -0
- package/src/auth/better-auth.ts +215 -17
- package/src/auth/drizzle.ts +1 -1
- package/src/auth.ts +21 -6
- package/src/boot.ts +19 -8
- package/src/bus/postgres.ts +32 -19
- package/src/bus.ts +7 -4
- package/src/{capture.mjs → capture.ts} +34 -1
- package/src/changes-sql.d.mts +8 -2
- package/src/changes-sql.mjs +26 -10
- package/src/changes.ts +165 -66
- package/src/channel/connections.ts +216 -0
- package/src/channel/socket.ts +396 -0
- package/src/channel-fake.ts +19 -4
- package/src/channel.ts +120 -492
- package/src/client.tsx +9 -2
- package/src/contract/describe.mjs +231 -0
- package/src/contract.mjs +144 -157
- package/src/data/cache.ts +354 -0
- package/src/data/definitions.ts +94 -0
- package/src/data/draft.ts +104 -0
- package/src/data/foundation.ts +95 -0
- package/src/data/live-api.ts +22 -0
- package/src/data/mutation.ts +138 -0
- package/src/data/primitives.ts +135 -0
- package/src/data/query.ts +106 -0
- package/src/data/transport.ts +121 -0
- package/src/data/types.ts +303 -0
- package/src/data.ts +69 -894
- package/src/db/bun-sqlite.d.ts +4 -1
- package/src/db/changes-mysql.ts +61 -21
- package/src/db/changes-postgres.ts +197 -118
- package/src/db/changes-sqlite.ts +131 -64
- package/src/db/core.ts +38 -13
- package/src/db/index.ts +20 -4
- package/src/db/mysql.ts +22 -6
- package/src/db/postgres.ts +49 -13
- package/src/db/sqlite.ts +53 -27
- package/src/derive.d.mts +4 -1
- package/src/derive.mjs +136 -39
- package/src/dev-runtime.mjs +8 -4
- package/src/exec/errors.ts +57 -0
- package/src/exec/landing.ts +66 -0
- package/src/exec/scope.ts +174 -0
- package/src/exec/touch.ts +80 -0
- package/src/exec/validation.ts +176 -0
- package/src/exec.ts +191 -341
- package/src/extension.ts +8 -2
- package/src/first-paint.ts +26 -9
- package/src/form/value.ts +15 -0
- package/src/form.tsx +132 -43
- package/src/hub/channels.ts +102 -0
- package/src/hub/connections.ts +147 -0
- package/src/hub/delivery.ts +62 -0
- package/src/hub/feed.ts +168 -0
- package/src/hub/presence.ts +64 -0
- package/src/hub/route.ts +158 -0
- package/src/hub/subscriptions.ts +212 -0
- package/src/hub/topic.ts +98 -0
- package/src/hub/tracked.ts +12 -0
- package/src/hub/types.ts +190 -0
- package/src/hub/view.ts +147 -0
- package/src/hub.ts +132 -758
- package/src/index.ts +84 -10
- package/src/inline.mjs +356 -0
- package/src/inspect/compare.mjs +162 -0
- package/src/inspect/routes.mjs +154 -0
- package/src/inspect/samples.mjs +59 -0
- package/src/inspect/url.mjs +2 -0
- package/src/inspect.mjs +146 -187
- package/src/link/election.ts +178 -0
- package/src/link/lines.ts +27 -0
- package/src/link.ts +90 -166
- package/src/list-drizzle.ts +104 -18
- package/src/list.ts +127 -36
- package/src/listen.ts +32 -6
- package/src/live/api.ts +18 -0
- package/src/live/backoff.ts +6 -0
- package/src/live/frames.ts +42 -0
- package/src/live/read.ts +77 -0
- package/src/live/sub-types.ts +24 -0
- package/src/live/transport.ts +86 -0
- package/src/live/types.ts +52 -0
- package/src/live-fake.ts +20 -5
- package/src/live-target.mjs +112 -37
- package/src/live.ts +116 -225
- package/src/media.ts +8 -1
- package/src/migrations.mjs +14 -6
- package/src/mux.ts +161 -64
- package/src/og/DefaultCard.tsx +40 -9
- package/src/og/fonts.mjs +16 -8
- package/src/og/images.mjs +43 -22
- package/src/og/index.mjs +119 -43
- package/src/og/native.mjs +11 -4
- package/src/range.mjs +15 -3
- package/src/router.ts +56 -6
- package/src/routes.mjs +73 -22
- package/src/run/admission.ts +80 -0
- package/src/run/boot.tsx +115 -0
- package/src/run/cache-presentation.ts +67 -0
- package/src/run/cache.ts +254 -0
- package/src/run/helpers.ts +197 -0
- package/src/run/listeners.ts +155 -0
- package/src/run/navigation.ts +382 -0
- package/src/run/page-skeleton.tsx +35 -0
- package/src/run/prefetch.ts +128 -0
- package/src/run/retain-view.ts +18 -0
- package/src/run/shape-preparation.ts +97 -0
- package/src/run/shape-styles.ts +72 -0
- package/src/run/source-context/pin.ts +124 -0
- package/src/run/source-context.ts +311 -0
- package/src/run.tsx +50 -178
- package/src/rust/common.ts +163 -0
- package/src/rust/native.ts +282 -0
- package/src/rust.ts +103 -229
- package/src/server/card.tsx +65 -0
- package/src/server/compression.ts +115 -0
- package/src/server/core.ts +70 -0
- package/src/server/dispatch.ts +70 -0
- package/src/server/form.ts +85 -0
- package/src/server/functions.ts +129 -0
- package/src/server/handler.ts +39 -0
- package/src/server/head.ts +96 -0
- package/src/server/media.ts +60 -0
- package/src/server/navigation-cache.ts +80 -0
- package/src/server/navigation.tsx +85 -0
- package/src/server/page-cold.ts +95 -0
- package/src/server/page-stream.tsx +181 -0
- package/src/server/page-types.ts +51 -0
- package/src/server/page-warm.ts +130 -0
- package/src/server/page.ts +69 -0
- package/src/server/payload.ts +89 -0
- package/src/server/prerender.ts +60 -0
- package/src/server/preview.tsx +67 -0
- package/src/server/render.ts +167 -0
- package/src/server/request.ts +98 -0
- package/src/server/resolver.ts +228 -0
- package/src/server/routes.ts +150 -0
- package/src/server/services.ts +69 -0
- package/src/server/text.ts +29 -0
- package/src/server/types.ts +160 -0
- package/src/server.tsx +82 -971
- package/src/shape.mjs +30 -6
- package/src/share/display.ts +50 -0
- package/src/share/network.ts +119 -0
- package/src/share/receive.ts +70 -0
- package/src/share/state.ts +30 -0
- package/src/share/tab-queue.ts +28 -0
- package/src/share-server.ts +128 -43
- package/src/share.ts +103 -209
- package/src/sheets.mjs +30 -18
- package/src/sidecar/build.mjs +170 -0
- package/src/sidecar/cargo.mjs +164 -0
- package/src/sidecar/common.mjs +17 -0
- package/src/sidecar/dev-build.mjs +91 -0
- package/src/sidecar/dev-watch.mjs +88 -0
- package/src/sidecar/manifest.mjs +19 -0
- package/src/sidecar/process.mjs +58 -0
- package/src/sidecar/refresh.mjs +19 -0
- package/src/sidecar.mjs +75 -416
- package/src/socket/check.ts +50 -0
- package/src/socket/deliver.ts +16 -0
- package/src/socket/publish.ts +56 -0
- package/src/socket/rooms.ts +41 -0
- package/src/socket/route.ts +79 -0
- package/src/socket/shared.ts +26 -0
- package/src/socket/types.ts +48 -0
- package/src/socket.ts +205 -194
- package/src/stream.ts +10 -1
- package/src/verify/app.ts +39 -0
- package/src/verify/cleanup.ts +27 -0
- package/src/verify/database.ts +11 -0
- package/src/verify/live.ts +63 -0
- package/src/verify/notify.ts +35 -0
- package/src/verify/skip.ts +19 -0
- package/src/verify/support.ts +149 -0
- package/src/verify/timing.ts +15 -0
- package/src/verify/trigger.ts +14 -0
- package/src/verify.ts +77 -176
- package/src/view.tsx +96 -11
- package/src/vite/asset-bytes.mjs +95 -0
- package/src/vite/assets.mjs +117 -0
- package/src/vite/config-hooks.mjs +166 -0
- package/src/vite/dev-hooks.mjs +103 -0
- package/src/vite/dev.mjs +102 -0
- package/src/vite/entries.mjs +145 -0
- package/src/vite/module-hooks.mjs +288 -0
- package/src/vite/route-modules.mjs +289 -0
- package/src/vite/server-functions.mjs +222 -0
- package/src/vite/shape-entry-css.mjs +187 -0
- package/src/vite/shape-override.mjs +226 -0
- package/src/vite/shared.mjs +51 -0
- package/src/vite/sheets.mjs +92 -0
- package/src/vite/state.mjs +89 -0
- package/src/vite/syntax.mjs +139 -0
- package/src/vite.mjs +152 -853
- package/src/capture.d.mts +0 -1
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import type { Derived, Resource } from '@weft-js/core'
|
|
2
|
+
import type { Context, FnOptions, Skip } from '../data'
|
|
3
|
+
import type { ServerExtension } from '../extension'
|
|
4
|
+
|
|
5
|
+
/** Here and not in verify.ts: that file needs `postgres`, and an app without it still reads this type. */
|
|
6
|
+
export interface VerifyOptions {
|
|
7
|
+
/** `true` or a target's name, as `verify: true` and `verify: 'staging'` mean, for when the rest of this is needed too. */
|
|
8
|
+
target?: true | string
|
|
9
|
+
/** A running staging app. Fine in code. */
|
|
10
|
+
url?: string
|
|
11
|
+
/** A database, from the environment: `process.env.STAGING_DATABASE_URL`. */
|
|
12
|
+
db?: string
|
|
13
|
+
/** The query's input. */
|
|
14
|
+
input?: unknown
|
|
15
|
+
/** Columns of the test row: whose it is, so the signed-in test user can see it. The rest are made up. */
|
|
16
|
+
row?: Record<string, unknown>
|
|
17
|
+
/** Name of the env var with a cookie for a user who can see the row. Default `WEFT_STAGING_COOKIE`. */
|
|
18
|
+
cookie?: string
|
|
19
|
+
/** Name of the env var with a cookie for a user who can't. Default `WEFT_STAGING_OTHER_COOKIE`; without it, a signed-out client. */
|
|
20
|
+
otherCookie?: string
|
|
21
|
+
/** Ms each link may take. Default 5000. */
|
|
22
|
+
timeout?: number
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** The Standard Schema interface (standardschema.dev): Zod, Valibot, ArkType and Drizzle's derived schemas. */
|
|
26
|
+
export interface StandardSchemaV1<I = unknown, O = I> {
|
|
27
|
+
readonly '~standard': {
|
|
28
|
+
readonly version: 1
|
|
29
|
+
readonly vendor: string
|
|
30
|
+
readonly validate: (value: unknown) => SchemaResult<O> | Promise<SchemaResult<O>>
|
|
31
|
+
readonly types?: { readonly input: I; readonly output: O } | undefined
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/** What a Standard Schema validator answers. */
|
|
35
|
+
export type SchemaResult<O> =
|
|
36
|
+
| { readonly value: O; readonly issues?: undefined }
|
|
37
|
+
| { readonly issues: readonly SchemaIssue[] }
|
|
38
|
+
/** One failure from a schema, with the path to the field when there is one. */
|
|
39
|
+
export interface SchemaIssue {
|
|
40
|
+
readonly message: string
|
|
41
|
+
readonly path?: readonly (PropertyKey | { readonly key: PropertyKey })[] | undefined
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** `'minutes'` etc. or seconds: how long the shared server cache keeps a value (research 27 §4). */
|
|
45
|
+
export type Life = 'seconds' | 'minutes' | 'hours' | 'days' | 'max' | number
|
|
46
|
+
|
|
47
|
+
/** `auth: 'user'` narrows `ctx.user` to non-null in `run` (A002 answers a call with no session). */
|
|
48
|
+
export type AuthScope = 'user'
|
|
49
|
+
type Ctx<A> = A extends 'user'
|
|
50
|
+
? Context extends { user: infer U }
|
|
51
|
+
? Omit<Context, 'user'> & { user: NonNullable<U> }
|
|
52
|
+
: Context
|
|
53
|
+
: Context
|
|
54
|
+
|
|
55
|
+
export type In<S> = S extends StandardSchemaV1<infer I, unknown> ? I : void
|
|
56
|
+
export type Out<S> = S extends StandardSchemaV1<unknown, infer O> ? O : undefined
|
|
57
|
+
type DeepPartial<T> = T extends object ? { [K in keyof T]?: DeepPartial<T[K]> } : T
|
|
58
|
+
type Tags<A extends unknown[]> = readonly string[] | ((...args: A) => readonly string[])
|
|
59
|
+
/** Read before `run`, for one user's tag: `tags: (_, ctx) => [`entitlements:${ctx.user.id}`]`. */
|
|
60
|
+
type QueryTags<I, A> = Tags<[input: I, ctx: Ctx<A>]>
|
|
61
|
+
|
|
62
|
+
/** What an `Impl` is called with. */
|
|
63
|
+
export interface ImplCall {
|
|
64
|
+
fn: Fn
|
|
65
|
+
input: unknown
|
|
66
|
+
ctx: Context
|
|
67
|
+
request: Request
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** A server function's body in Rust: `remote('rust://search')` or `native('media')`, from `@weft-js/start/rust` (research 36, 37). */
|
|
71
|
+
export interface Impl {
|
|
72
|
+
readonly kind: string
|
|
73
|
+
readonly target: string
|
|
74
|
+
readonly service: string
|
|
75
|
+
call(c: ImplCall): Promise<{ value: unknown; tags: string[]; touched: string[] }>
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Errors a function raises by name, with the data each carries: a Rust function's become an enum. */
|
|
79
|
+
export type Errors = Record<string, StandardSchemaV1>
|
|
80
|
+
|
|
81
|
+
/** `query()` with a `run` on this server. */
|
|
82
|
+
export interface QueryDef<S, O, A = undefined> extends FnOptions {
|
|
83
|
+
input?: S
|
|
84
|
+
/** The result's shape: checked in dev and tests (C002), and the contract's `output`. */
|
|
85
|
+
output?: StandardSchemaV1
|
|
86
|
+
/** `'user'`: refuse a call with no session (A002). The contract's auth scope. */
|
|
87
|
+
auth?: A
|
|
88
|
+
/** An example result, for what `output` can't say: sizes a pending view (research 31 §2). */
|
|
89
|
+
shape?: O extends (infer R)[] ? DeepPartial<R>[] : DeepPartial<O>
|
|
90
|
+
/** What invalidates it: the client cache, the request cache and the shared cache all go by these. */
|
|
91
|
+
tags?: QueryTags<Out<S>, A>
|
|
92
|
+
/** `required: true`: `weft check` and `weft build` fail on a target with no real change feed, and the server won't start without one (R013, R015). */
|
|
93
|
+
live?: {
|
|
94
|
+
required?: boolean
|
|
95
|
+
/** A real round trip against a target that isn't production, in `weft check` and `weft build`. `true` finds `WEFT_STAGING_URL` or `STAGING_DATABASE_URL`; a string names a target in `live.targets`. */
|
|
96
|
+
verify?: true | string | VerifyOptions
|
|
97
|
+
}
|
|
98
|
+
/** Turn on the shared server cache (across requests). Omit it and the query runs once per request. */
|
|
99
|
+
cache?: {
|
|
100
|
+
life?: Life
|
|
101
|
+
/** One cached value per signed-in user: `ctx.user` is readable. */ scope?: 'user'
|
|
102
|
+
}
|
|
103
|
+
/** Named failures, for the client as `FnError.code` and `.data`. */
|
|
104
|
+
errors?: Errors
|
|
105
|
+
run: (args: { input: Out<S>; ctx: Ctx<A> }) => O | Promise<O>
|
|
106
|
+
impl?: undefined
|
|
107
|
+
/** Set by the `weft:start` plugin: `<export>.<file hash>`. */
|
|
108
|
+
$id?: string
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** The same declaration with its body elsewhere: `output` is required, because it's the whole contract for what comes back. */
|
|
112
|
+
export type ImplQueryDef<S, OS extends StandardSchemaV1, A = undefined> = Omit<
|
|
113
|
+
QueryDef<S, Out<OS>, A>,
|
|
114
|
+
'run' | 'impl' | 'output'
|
|
115
|
+
> & { output: OS; impl: Impl; run?: undefined }
|
|
116
|
+
/** `mutation()` with `impl` and an `output` schema: no `run` here. */
|
|
117
|
+
export type ImplMutationDef<S, OS extends StandardSchemaV1, A = undefined> = Omit<
|
|
118
|
+
MutationDef<S, Out<OS>, A>,
|
|
119
|
+
'run' | 'impl' | 'output'
|
|
120
|
+
> & { output: OS; impl: Impl; run?: undefined }
|
|
121
|
+
|
|
122
|
+
/** `mutation()` with a `run` on this server. */
|
|
123
|
+
export interface MutationDef<S, O, A = undefined> extends FnOptions {
|
|
124
|
+
input?: S
|
|
125
|
+
output?: StandardSchemaV1
|
|
126
|
+
auth?: A
|
|
127
|
+
/** Tags whose queries the response refreshes (single-flight) and whose cached values it drops. */
|
|
128
|
+
invalidates?: Tags<[input: Out<S>, result: Awaited<O>]>
|
|
129
|
+
/** Client only: predict the result. Each call's layers are re-applied on new values until it settles. */
|
|
130
|
+
optimistic?: (args: { input: In<S> }, cache: OptimisticCache) => void
|
|
131
|
+
errors?: Errors
|
|
132
|
+
run: (args: { input: Out<S>; ctx: Ctx<A> }) => O | Promise<O>
|
|
133
|
+
impl?: undefined
|
|
134
|
+
$id?: string
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
type Arg<I> = I | (() => I | Skip)
|
|
138
|
+
|
|
139
|
+
/** What `query()` returns: call it for a resource, or `.live()` for one that stays current. */
|
|
140
|
+
export interface Query<I, O> {
|
|
141
|
+
/** The live value, read in a component or a defineState setup (Q001 elsewhere: use `fetch`). */
|
|
142
|
+
(
|
|
143
|
+
...args: [I] extends [void] ? [input?: undefined | (() => undefined | Skip)] : [input: Arg<I>]
|
|
144
|
+
): Resource<O>
|
|
145
|
+
/** The value as a promise: in `load`, handlers, server code and tests. Through the request or client cache. */
|
|
146
|
+
fetch(...args: [I] extends [void] ? [] : [input: I]): Promise<O>
|
|
147
|
+
/** A placeholder result to size a pending view by (last seen, else declared), or undefined when nothing is known. */
|
|
148
|
+
shape(...args: [I] extends [void] ? [] : [input: I]): O | undefined
|
|
149
|
+
/**
|
|
150
|
+
* The same value, refetched whenever a mutation anywhere invalidates one of its tags. `.live` is the connection's
|
|
151
|
+
* status. `keep`: how long the subscription outlives its last reader (`'30s'`, `'session'`).
|
|
152
|
+
*/
|
|
153
|
+
live(
|
|
154
|
+
...args: [I] extends [void]
|
|
155
|
+
? [input?: undefined | (() => undefined | Skip), options?: LiveOptions]
|
|
156
|
+
: [input: Arg<I>, options?: LiveOptions]
|
|
157
|
+
): LiveQuery<O>
|
|
158
|
+
readonly kind: 'query'
|
|
159
|
+
readonly $id: string
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** What `mutation()` returns: call it, or read its `pending` and `error`. */
|
|
163
|
+
export interface Mutation<I, O> {
|
|
164
|
+
(...args: [I] extends [void] ? [] : [input: I]): Promise<O>
|
|
165
|
+
/** A call is in flight. */
|
|
166
|
+
readonly pending: Derived<boolean>
|
|
167
|
+
/** The last call's failure, until the next call succeeds. */
|
|
168
|
+
readonly error: Derived<unknown>
|
|
169
|
+
readonly kind: 'mutation'
|
|
170
|
+
readonly $id: string
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** `degraded`: connected, and writes from outside the app arrive late or not at all. `.liveMode` says which. */
|
|
174
|
+
export type LiveStatus = 'connecting' | 'live' | 'degraded' | 'reconnecting' | 'offline'
|
|
175
|
+
/**
|
|
176
|
+
* How writes reach this page. `app`: mutations only, no `changes` configured. `feed`: the database reports every change.
|
|
177
|
+
* `external`: a separate listener does, through the bus. `poll`: an estimate, every few seconds. `off`: mutations only, though `changes` asks for more.
|
|
178
|
+
*/
|
|
179
|
+
export type LiveMode = 'app' | 'feed' | 'external' | 'poll' | 'off'
|
|
180
|
+
/** `.live()`: how long it's kept, and whether it refuses to run without a real feed. */
|
|
181
|
+
export interface LiveOptions {
|
|
182
|
+
keep?: string | number
|
|
183
|
+
/** Fail `weft check`, `weft build` and server startup rather than run without a real change feed (R013, R015). */
|
|
184
|
+
required?: boolean
|
|
185
|
+
}
|
|
186
|
+
/** A resource that also says how live it is. */
|
|
187
|
+
export type LiveQuery<O> = Resource<O> & {
|
|
188
|
+
readonly live: Derived<LiveStatus>
|
|
189
|
+
readonly liveMode: Derived<LiveMode>
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** Any definition, as the executor and transports see it. */
|
|
193
|
+
export interface Fn {
|
|
194
|
+
kind: 'query' | 'mutation' | 'stream' | 'channel' | 'shared'
|
|
195
|
+
$id: string
|
|
196
|
+
def: {
|
|
197
|
+
input?: StandardSchemaV1
|
|
198
|
+
output?: StandardSchemaV1
|
|
199
|
+
message?: StandardSchemaV1
|
|
200
|
+
shape?: unknown
|
|
201
|
+
auth?: AuthScope
|
|
202
|
+
run?: (args: {
|
|
203
|
+
input: unknown
|
|
204
|
+
ctx: Context
|
|
205
|
+
signal?: AbortSignal
|
|
206
|
+
lastEventId?: unknown
|
|
207
|
+
}) => unknown
|
|
208
|
+
impl?: Impl
|
|
209
|
+
errors?: Errors
|
|
210
|
+
tags?: Tags<[input: unknown, ctx?: Context]>
|
|
211
|
+
cache?: { life?: Life; scope?: 'user' }
|
|
212
|
+
invalidates?: Tags<[unknown, unknown]>
|
|
213
|
+
optimistic?: (args: { input: unknown }, cache: OptimisticCache) => void
|
|
214
|
+
/** `channel` and `sharedState` (research 32). */
|
|
215
|
+
params?: StandardSchemaV1
|
|
216
|
+
authorize?: unknown
|
|
217
|
+
left?: (args: { id: string; ctx: Context; params: unknown; room: unknown }) => unknown
|
|
218
|
+
recheck?: (args: { id: string; ctx: Context; reply: unknown }) => unknown
|
|
219
|
+
client?: Record<string, StandardSchemaV1>
|
|
220
|
+
server?: Record<string, StandardSchemaV1>
|
|
221
|
+
on?: Record<string, (args: never) => unknown>
|
|
222
|
+
limits?: { perSecond?: number }
|
|
223
|
+
key?: StandardSchemaV1
|
|
224
|
+
value?: StandardSchemaV1
|
|
225
|
+
initial?: unknown
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** One cached entry as the server sends it: `[id, input, value, tags]` (page payload, navigation, refreshes). */
|
|
230
|
+
export type Wire = [id: string, input: unknown, value: unknown, tags: string[]]
|
|
231
|
+
|
|
232
|
+
/** What the transports answer (research 27 §3). */
|
|
233
|
+
export interface Answer {
|
|
234
|
+
v?: unknown
|
|
235
|
+
t?: string[]
|
|
236
|
+
c?: Wire[]
|
|
237
|
+
r?: string
|
|
238
|
+
/** `r` is a full load: the session changed, so nothing cached may survive. */ x?: 1
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/** How a client call reaches the server: `fetch` by default, a fake under test. */
|
|
242
|
+
export interface Transport {
|
|
243
|
+
query(id: string, input: unknown): Promise<Answer>
|
|
244
|
+
/** `form`: the input came from a form, so strings may be coerced; a FormData is sent as is (it holds files). */
|
|
245
|
+
mutate(
|
|
246
|
+
id: string,
|
|
247
|
+
input: unknown,
|
|
248
|
+
refresh: [string, unknown][],
|
|
249
|
+
form?: true | FormData,
|
|
250
|
+
): Promise<Answer>
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/** @weft-js/core/test's fake-transport controls (research 27 §7). */
|
|
254
|
+
export interface NetControl {
|
|
255
|
+
latency: number
|
|
256
|
+
ctx?: object
|
|
257
|
+
/** Server extensions for this test (`net.extension`). */
|
|
258
|
+
extensions: ServerExtension[]
|
|
259
|
+
online(): boolean
|
|
260
|
+
fails: Map<string, unknown[]>
|
|
261
|
+
handlers: Map<string, (input: unknown, ctx: Context) => unknown>
|
|
262
|
+
log: { id: string; kind: string; input: unknown }[]
|
|
263
|
+
inflight: Set<Promise<unknown>>
|
|
264
|
+
als: { run<T>(store: unknown, fn: () => T): T; getStore(): unknown }
|
|
265
|
+
resets: (() => unknown)[]
|
|
266
|
+
/** Subscriptions an owner holds: open at the end of a test, they're T012. */
|
|
267
|
+
open: Map<object, [what: string, close: () => void]>
|
|
268
|
+
/** The realtime fake, while one is connected: `net.push`, `net.poke`, `net.drop`. */
|
|
269
|
+
rt?:
|
|
270
|
+
| {
|
|
271
|
+
push(id: string, msg: unknown): number
|
|
272
|
+
poke(tags: string[]): Promise<void>
|
|
273
|
+
drop(): void
|
|
274
|
+
subscriptions(): { kind: string; id: string; input: unknown }[]
|
|
275
|
+
}
|
|
276
|
+
| undefined
|
|
277
|
+
/** The channel fake, while a socket is open. */
|
|
278
|
+
ws?:
|
|
279
|
+
| {
|
|
280
|
+
publish(id: string, params: unknown, name: string, msg: unknown): Promise<void>
|
|
281
|
+
drop(): void
|
|
282
|
+
recheck(): Promise<void>
|
|
283
|
+
}
|
|
284
|
+
| undefined
|
|
285
|
+
/** What this client's sockets sent, across reconnects. */
|
|
286
|
+
sent: { id: string; name: string; msg: unknown }[]
|
|
287
|
+
reconnects: number
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/** A query handle, whatever its input: what the cache needs of one. */
|
|
291
|
+
export type QueryRef<O> = Pick<Query<void, O>, 'kind' | '$id'> & {
|
|
292
|
+
fetch(...args: never[]): Promise<O>
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/** The cache as an `optimistic` sees it. */
|
|
296
|
+
export interface OptimisticCache {
|
|
297
|
+
/** Change a query's cached value with a draft (copy-on-write: untouched rows keep their identity). */
|
|
298
|
+
update<O>(q: QueryRef<O>, input: unknown, recipe: (draft: O) => O | void): void
|
|
299
|
+
set<O>(q: QueryRef<O>, input: unknown, value: O): void
|
|
300
|
+
get<O>(q: QueryRef<O>, input?: unknown): O | undefined
|
|
301
|
+
/** The value (a row) was made by a pending optimistic layer. */
|
|
302
|
+
isOptimistic(value: unknown): boolean
|
|
303
|
+
}
|