@weft-js/start 0.1.1 → 0.1.3
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 +1278 -12
- package/crate/Cargo.lock +1347 -0
- package/crate/Cargo.toml +72 -0
- package/crate/README.md +322 -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,176 @@
|
|
|
1
|
+
import { fns, StartError, type Fn, type SchemaIssue } from '../data'
|
|
2
|
+
import type { Scope } from './scope'
|
|
3
|
+
|
|
4
|
+
type Obj = Record<PropertyKey, unknown>
|
|
5
|
+
|
|
6
|
+
// ---- validation and checks ------------------------------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
export async function validate(fn: Fn, input: unknown): Promise<unknown> {
|
|
9
|
+
const schema = fn.def.input
|
|
10
|
+
if (!schema) return input
|
|
11
|
+
const r = await schema['~standard'].validate(input)
|
|
12
|
+
if (!r.issues) return r.value
|
|
13
|
+
throw rejected(fn, r.issues)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const plainPath = (i: SchemaIssue) => (i.path ?? []).map((p) => (typeof p === 'object' ? p.key : p))
|
|
17
|
+
|
|
18
|
+
function rejected(fn: Fn, list: readonly SchemaIssue[]) {
|
|
19
|
+
const issues = list.map((i) => ({
|
|
20
|
+
message: i.message,
|
|
21
|
+
path: i.path && plainPath(i),
|
|
22
|
+
})) as (SchemaIssue & { path?: PropertyKey[] })[]
|
|
23
|
+
const where = issues
|
|
24
|
+
.map((i) => `${i.path?.length ? i.path.map(String).join('.') + ': ' : ''}${i.message}`)
|
|
25
|
+
.join('; ')
|
|
26
|
+
return new StartError(
|
|
27
|
+
'Q002',
|
|
28
|
+
`\`${fn.$id}\` rejected its input: ${where}`,
|
|
29
|
+
'pass what the schema accepts',
|
|
30
|
+
{ status: 400, issues },
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
/** A form's string as the type it may stand for (research 29 §5): the schema says whether it was wrong. */
|
|
34
|
+
function reading(s: string): unknown {
|
|
35
|
+
if (s === '') return undefined
|
|
36
|
+
if (s === 'on' || s === 'true') return true
|
|
37
|
+
if (s === 'false') return false
|
|
38
|
+
return /^-?\d+(\.\d+)?$/.test(s) ? Number(s) : s
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* A form sends strings and a schema may want numbers or booleans, and Standard Schema can't say which: each issue
|
|
42
|
+
* on a string tries its obvious reading, then the input is validated again. Fields a reading fixed report nothing; the
|
|
43
|
+
* rest report what the schema says of what was typed (an empty field: its "required", not "expected a string").
|
|
44
|
+
*/
|
|
45
|
+
export async function formInput(fn: Fn, input: unknown): Promise<unknown> {
|
|
46
|
+
const schema = fn.def.input
|
|
47
|
+
if (!schema || !input || typeof input !== 'object') return validate(fn, input)
|
|
48
|
+
const value: Obj = { ...input }
|
|
49
|
+
let left: readonly SchemaIssue[] = []
|
|
50
|
+
for (let i = 0; i < 3; i++) {
|
|
51
|
+
const r = await schema['~standard'].validate(value)
|
|
52
|
+
if (!r.issues) return r.value
|
|
53
|
+
left = r.issues
|
|
54
|
+
let changed = false
|
|
55
|
+
for (const issue of r.issues) {
|
|
56
|
+
const path = plainPath(issue)
|
|
57
|
+
if (!path.length) continue
|
|
58
|
+
let at: Obj | undefined = value
|
|
59
|
+
for (const p of path.slice(0, -1)) {
|
|
60
|
+
const inner: unknown = at?.[p]
|
|
61
|
+
if (!inner || typeof inner !== 'object') {
|
|
62
|
+
at = undefined
|
|
63
|
+
break
|
|
64
|
+
}
|
|
65
|
+
at = at[p] = (Array.isArray(inner) ? [...inner] : { ...inner }) as Obj
|
|
66
|
+
}
|
|
67
|
+
const k = path.at(-1)!
|
|
68
|
+
const typed = at?.[k]
|
|
69
|
+
if (typeof typed !== 'string') continue
|
|
70
|
+
const next = reading(typed)
|
|
71
|
+
if (next === typed) continue
|
|
72
|
+
if (next === undefined) delete at![k]
|
|
73
|
+
else at![k] = next
|
|
74
|
+
changed = true
|
|
75
|
+
}
|
|
76
|
+
if (!changed) break
|
|
77
|
+
}
|
|
78
|
+
const first = await schema['~standard'].validate(input)
|
|
79
|
+
const where = (i: SchemaIssue) => JSON.stringify(plainPath(i))
|
|
80
|
+
throw rejected(
|
|
81
|
+
fn,
|
|
82
|
+
left.map((i) => first.issues?.find((o) => where(o) === where(i)) ?? i),
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
/** Dev and tests: the value against `output` (C002), and what JSON would change (Q005). */
|
|
86
|
+
export async function checked(v: unknown, fn: Fn, scope: Scope) {
|
|
87
|
+
serializable(v, fn, scope)
|
|
88
|
+
const schema = fn.def.output
|
|
89
|
+
if (!schema || fn.def.impl) return // `impl`: rust.ts checked what came back, on every call
|
|
90
|
+
const r = await schema['~standard'].validate(v)
|
|
91
|
+
if (r.issues)
|
|
92
|
+
throw new StartError(
|
|
93
|
+
'C002',
|
|
94
|
+
`\`${fn.$id}\` returned a value its \`output\` schema rejects: ${r.issues.map((i) => `${i.path?.map((p) => (typeof p === 'object' ? p.key : p)).join('.') ?? ''}: ${i.message}`).join('; ')}`,
|
|
95
|
+
'fix `run`, or the schema',
|
|
96
|
+
{ status: 500 },
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
/** Q005 (dev): what JSON would change on the way to the client. */
|
|
100
|
+
function serializable(v: unknown, fn: Fn, scope: Scope) {
|
|
101
|
+
const bad = (x: unknown, path: string): string | undefined => {
|
|
102
|
+
if (x === null || typeof x === 'string' || typeof x === 'boolean') return undefined
|
|
103
|
+
if (typeof x === 'number') return Number.isFinite(x) ? undefined : `${path} is ${x}`
|
|
104
|
+
if (x === undefined) return undefined
|
|
105
|
+
if (typeof x !== 'object') return `${path} is a ${typeof x}`
|
|
106
|
+
if (Array.isArray(x)) {
|
|
107
|
+
for (let i = 0; i < x.length; i++) {
|
|
108
|
+
if (x[i] === undefined) return `${path}[${i}] is undefined (JSON sends null)`
|
|
109
|
+
const b = bad(x[i], `${path}[${i}]`)
|
|
110
|
+
if (b) return b
|
|
111
|
+
}
|
|
112
|
+
return undefined
|
|
113
|
+
}
|
|
114
|
+
const proto: unknown = Object.getPrototypeOf(x)
|
|
115
|
+
if (proto !== Object.prototype && proto !== null)
|
|
116
|
+
return `${path} is a ${(x as { constructor?: { name?: string } }).constructor?.name ?? 'class instance'}`
|
|
117
|
+
for (const k of Object.keys(x)) {
|
|
118
|
+
const b = bad((x as Obj)[k], `${path}.${k}`)
|
|
119
|
+
if (b) return b
|
|
120
|
+
}
|
|
121
|
+
return undefined
|
|
122
|
+
}
|
|
123
|
+
const b = bad(v, 'result')
|
|
124
|
+
if (b)
|
|
125
|
+
scope.options.onDiagnostic?.({
|
|
126
|
+
code: 'Q005',
|
|
127
|
+
severity: 'warning',
|
|
128
|
+
message: `\`${fn.$id}\`'s ${b}: the client gets a different value than the server rendered with. Send plain data (ISO strings, arrays, objects)`,
|
|
129
|
+
})
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Q008 (dev, once): each mutation's static `invalidates` against the tags queries declare. A tags function is called
|
|
133
|
+
* with an input whose every field reads `*`, so `todo:${id}` declares `todo:*`.
|
|
134
|
+
*/
|
|
135
|
+
export function checkTags(
|
|
136
|
+
report: (d: { code: string; severity: string; message: string }) => void,
|
|
137
|
+
) {
|
|
138
|
+
const star: unknown = new Proxy(
|
|
139
|
+
{},
|
|
140
|
+
{ get: (_, k) => (k === Symbol.toPrimitive ? () => '*' : '*') },
|
|
141
|
+
)
|
|
142
|
+
const declared: string[] = []
|
|
143
|
+
for (const f of fns.values()) {
|
|
144
|
+
if (f.kind !== 'query') continue
|
|
145
|
+
try {
|
|
146
|
+
declared.push(...(typeof f.def.tags === 'function' ? f.def.tags(star) : (f.def.tags ?? [])))
|
|
147
|
+
} catch {}
|
|
148
|
+
}
|
|
149
|
+
const matches = (tag: string) =>
|
|
150
|
+
declared.some(
|
|
151
|
+
(d) =>
|
|
152
|
+
d === tag ||
|
|
153
|
+
(d.includes('*') &&
|
|
154
|
+
new RegExp(`^${d.replace(/[.+?^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '[^]*')}$`).test(
|
|
155
|
+
tag,
|
|
156
|
+
)) ||
|
|
157
|
+
(tag.includes('*') && d.startsWith(tag.slice(0, tag.indexOf('*')))),
|
|
158
|
+
)
|
|
159
|
+
for (const f of fns.values()) {
|
|
160
|
+
if (f.kind !== 'mutation' || !f.def.invalidates) continue
|
|
161
|
+
let tags: readonly string[]
|
|
162
|
+
try {
|
|
163
|
+
tags =
|
|
164
|
+
typeof f.def.invalidates === 'function' ? f.def.invalidates(star, star) : f.def.invalidates
|
|
165
|
+
} catch {
|
|
166
|
+
continue
|
|
167
|
+
}
|
|
168
|
+
for (const t of tags)
|
|
169
|
+
if (!matches(t))
|
|
170
|
+
report({
|
|
171
|
+
code: 'Q008',
|
|
172
|
+
severity: 'warning',
|
|
173
|
+
message: `\`${f.$id}\` invalidates \`${t}\`, a tag no query declares: nothing refreshes. Fix the tag, or add it to the query's \`tags\``,
|
|
174
|
+
})
|
|
175
|
+
}
|
|
176
|
+
}
|