@weft-js/cli 0.1.0 → 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 +38 -14
- package/bin/weft.mjs +8 -1
- package/docs/async.md +16 -6
- package/docs/browser.md +22 -8
- package/docs/config.md +39 -9
- package/docs/core.md +30 -0
- package/docs/deploy.md +108 -0
- package/docs/design.md +12 -3
- package/docs/entrypoints.md +79 -0
- package/docs/fonts.md +12 -2
- package/docs/icons.md +13 -7
- package/docs/lists.md +3 -1
- package/docs/meta.md +13 -3
- package/docs/plugins.md +32 -9
- package/docs/props.md +13 -2
- package/docs/reactivity.md +26 -0
- package/docs/rules.md +19 -12
- package/docs/rust.md +47 -17
- package/docs/start-short.md +3 -2
- package/docs/start.md +226 -52
- package/docs/state.md +33 -7
- package/docs/style.md +31 -8
- package/docs/surface.md +61 -15
- package/docs/testing.md +15 -5
- package/docs/theme.md +182 -0
- package/docs/ui-state.md +19 -6
- package/docs/verify.md +13 -0
- package/package.json +13 -4
- package/src/add/config.mjs +93 -0
- package/src/add/errors.mjs +2 -0
- package/src/add/setup.mjs +88 -0
- package/src/add-rust.mjs +82 -27
- package/src/add.mjs +209 -181
- package/src/agents-md.mjs +144 -43
- package/src/alias.mjs +16 -5
- package/src/audit/page.mjs +313 -0
- package/src/audit/report.mjs +69 -0
- package/src/audit/utils.mjs +5 -0
- package/src/audit.mjs +174 -188
- package/src/browser/cli.mjs +24 -5
- package/src/browser/client.mjs +189 -25
- package/src/browser/commands.mjs +166 -51
- package/src/catalog/audit.mjs +129 -0
- package/src/catalog/auth.mjs +40 -0
- package/src/catalog/compiler.mjs +337 -0
- package/src/catalog/contracts.mjs +18 -0
- package/src/catalog/database.mjs +52 -0
- package/src/catalog/deploy.mjs +117 -0
- package/src/catalog/design.mjs +159 -0
- package/src/catalog/forms.mjs +25 -0
- package/src/catalog/hydration.mjs +29 -0
- package/src/catalog/icons.mjs +24 -0
- package/src/catalog/media.mjs +163 -0
- package/src/catalog/metadata.mjs +116 -0
- package/src/catalog/plugins.mjs +228 -0
- package/src/catalog/realtime.mjs +195 -0
- package/src/catalog/rust.mjs +75 -0
- package/src/catalog/server-functions.mjs +92 -0
- package/src/catalog/state.mjs +39 -0
- package/src/catalog/style-groups.mjs +54 -0
- package/src/catalog/tests.mjs +127 -0
- package/src/catalog/text.mjs +200 -0
- package/src/catalog/ui-state.mjs +42 -0
- package/src/catalog.mjs +68 -444
- package/src/check/runtime.mjs +280 -0
- package/src/check.mjs +199 -207
- package/src/cli/authoring.mjs +213 -0
- package/src/cli/project.mjs +302 -0
- package/src/cli.mjs +228 -224
- package/src/config.mjs +1 -1
- package/src/contributions.mjs +75 -25
- package/src/db-layer.mjs +13 -5
- package/src/deploy/cdn.mjs +142 -0
- package/src/deploy/files.mjs +266 -0
- package/src/deploy/fit.mjs +166 -0
- package/src/deploy/hosts.mjs +126 -0
- package/src/deploy/index/cli.mjs +20 -0
- package/src/deploy/index/environment.mjs +69 -0
- package/src/deploy/index/steps.mjs +87 -0
- package/src/deploy/index.mjs +387 -0
- package/src/deploy/needs.mjs +149 -0
- package/src/design-checks/accessible-names.mjs +62 -0
- package/src/design-checks/contrast.mjs +43 -0
- package/src/design-checks/csp.mjs +60 -0
- package/src/design-checks/files.mjs +59 -0
- package/src/design-checks/heading-links.mjs +61 -0
- package/src/design-checks/interaction.mjs +65 -0
- package/src/design-checks/jsx.mjs +67 -0
- package/src/design-checks/layout-values.mjs +68 -0
- package/src/design-checks/native-controls.mjs +48 -0
- package/src/design-checks/outline.mjs +39 -0
- package/src/design-checks/reporter.mjs +37 -0
- package/src/design-checks/rules.mjs +274 -0
- package/src/design-checks/values.mjs +262 -0
- package/src/design-checks.mjs +10 -400
- package/src/diag.mjs +32 -8
- package/src/docs.mjs +138 -42
- package/src/doctor.mjs +20 -8
- package/src/files.mjs +18 -5
- package/src/fix.mjs +10 -2
- package/src/gate/after.mjs +40 -0
- package/src/gate/before.mjs +54 -0
- package/src/gate/classify.mjs +72 -0
- package/src/gate/mutation.mjs +209 -0
- package/src/gate/resolve-cases.mjs +22 -0
- package/src/gate/result.mjs +63 -0
- package/src/gate/run.mjs +75 -0
- package/src/gate.mjs +104 -202
- package/src/infer/box-props.cjs +140 -0
- package/src/infer/core-boxes.cjs +46 -0
- package/src/infer/derived-writes.cjs +95 -0
- package/src/infer/engine/checks.cjs +193 -0
- package/src/infer/engine/flow.cjs +293 -0
- package/src/infer/engine/report.cjs +158 -0
- package/src/infer/engine/utils.cjs +15 -0
- package/src/infer/engine.cjs +108 -368
- package/src/infer/facts/sites.cjs +114 -0
- package/src/infer/facts.cjs +210 -117
- package/src/infer/hosts.cjs +69 -22
- package/src/infer/lsp.mjs +149 -40
- package/src/infer/session.cjs +73 -17
- package/src/infer/shadow.cjs +13 -5
- package/src/infer/worker.cjs +57 -9
- package/src/jsx-scan.mjs +63 -21
- package/src/load-config/schema.mjs +281 -0
- package/src/load-config.mjs +144 -116
- package/src/map.mjs +93 -26
- package/src/mcp/browser-tools.mjs +155 -0
- package/src/mcp.mjs +147 -69
- package/src/meta-table.mjs +43 -8
- package/src/mutate.mjs +120 -36
- package/src/plugin-checks.mjs +52 -23
- package/src/plugins.mjs +125 -39
- package/src/project.mjs +170 -79
- package/src/props-checks.mjs +24 -6
- package/src/props-listener.mjs +27 -9
- package/src/react-checks.mjs +35 -4
- package/src/server-state-checks.mjs +34 -0
- package/src/share-checks.mjs +121 -37
- package/src/site.mjs +33 -6
- package/src/slots.mjs +104 -19
- package/src/start/build.mjs +116 -0
- package/src/start/routes.mjs +38 -0
- package/src/start.mjs +194 -92
- package/src/state-checks/core-state.mjs +223 -0
- package/src/state-checks/modules.mjs +17 -0
- package/src/state-checks/scan.mjs +232 -0
- package/src/state-checks/server-graph.mjs +65 -0
- package/src/state-checks.mjs +109 -133
- package/src/style/find.mjs +121 -0
- package/src/style.mjs +133 -127
- package/src/surface-checks.mjs +212 -46
- package/src/tailwind.mjs +5 -4
- package/src/tasks.mjs +189 -27
- package/src/test-rules/context.mjs +109 -0
- package/src/test-rules/file-rules.mjs +279 -0
- package/src/test-rules/rule-helpers.mjs +244 -0
- package/src/test-rules/syntax.mjs +223 -0
- package/src/test-rules/test-rules.mjs +127 -0
- package/src/testdiff.mjs +94 -32
- package/src/tests.mjs +31 -483
- package/src/text-checks.mjs +229 -69
- package/src/tokens.mjs +12 -4
- package/src/trust/scan.mjs +95 -0
- package/src/trust.mjs +214 -102
- package/src/weft.mjs +41 -9
- package/src/workspace.mjs +96 -19
- package/ts-plugin/context.js +57 -0
- package/ts-plugin/index.js +4 -124
- package/ts-plugin/mapping.js +65 -0
- package/ts-plugin/proxy.js +144 -0
package/docs/plugins.md
CHANGED
|
@@ -8,8 +8,8 @@ own diagnostics and `weft add` steps. It uses only what any plugin can use, and
|
|
|
8
8
|
|
|
9
9
|
```
|
|
10
10
|
packages/pay/ packages/pay/ (`src/stripe.ts`)
|
|
11
|
-
plugin.mjs the manifest plugin.mjs
|
|
12
|
-
src/index.ts client-safe src/index.ts
|
|
11
|
+
plugin.mjs the manifest plugin.mjs `pay.rail` provider
|
|
12
|
+
src/index.ts client-safe src/index.ts stripeRail(options)
|
|
13
13
|
src/server.ts the ledger
|
|
14
14
|
src/test.ts fakePay, payRailSuite
|
|
15
15
|
src/checks.mjs PAY-codes
|
|
@@ -24,8 +24,12 @@ A rail is how a payment happens. `fakePay` is a second rail, which is how you kn
|
|
|
24
24
|
|
|
25
25
|
```js
|
|
26
26
|
export const pay = definePlugin({
|
|
27
|
-
name: '@weft-js/pay',
|
|
28
|
-
|
|
27
|
+
name: '@weft-js/pay',
|
|
28
|
+
version: '0.1.0', weft: '*', namespace: 'PAY', permissions: {},
|
|
29
|
+
contribute: () => ({
|
|
30
|
+
provides: [...], diagnostics, check, docs, setup,
|
|
31
|
+
agents, catalog, map,
|
|
32
|
+
}),
|
|
29
33
|
})
|
|
30
34
|
```
|
|
31
35
|
|
|
@@ -42,8 +46,18 @@ imports lazily, on that side only. That's why an app that doesn't use a plugin c
|
|
|
42
46
|
Pay provides `server.extension`, the slot for anything that needs a server presence:
|
|
43
47
|
|
|
44
48
|
```js
|
|
45
|
-
provide(
|
|
46
|
-
{
|
|
49
|
+
provide(
|
|
50
|
+
defineSlot({
|
|
51
|
+
id: 'server.extension', version: 1,
|
|
52
|
+
cardinality: 'keyed', sides: ['server'],
|
|
53
|
+
}),
|
|
54
|
+
{
|
|
55
|
+
key: 'pay',
|
|
56
|
+
sides: { server: {
|
|
57
|
+
module: '@weft-js/pay/server', export: 'extension',
|
|
58
|
+
} },
|
|
59
|
+
},
|
|
60
|
+
)
|
|
47
61
|
```
|
|
48
62
|
|
|
49
63
|
The key is the mount: `/_weft/x/pay/...`. A keyed provider also needs `src/<key>.server.ts`, the app's own configuration, and
|
|
@@ -82,7 +96,11 @@ and a good template for a provider.
|
|
|
82
96
|
`diagnostics` declares each code: severity, what it catches, the fix. `check` finds them:
|
|
83
97
|
|
|
84
98
|
```js
|
|
85
|
-
check: {
|
|
99
|
+
check: {
|
|
100
|
+
include: ['src/**/*.{ts,tsx}'],
|
|
101
|
+
run: ctx => import('./src/checks.mjs')
|
|
102
|
+
.then(c => c.run(ctx)),
|
|
103
|
+
}
|
|
86
104
|
```
|
|
87
105
|
|
|
88
106
|
`run(ctx)` gets `files`, `read`, `exists`, `jsx`, `slots`, and returns findings: `{ code, file, start, end, why, help?, fix? }`.
|
|
@@ -108,9 +126,14 @@ sees what a user sees. It's imported from `@weft-js/pay/test`, since core can't
|
|
|
108
126
|
```js
|
|
109
127
|
export const setup = {
|
|
110
128
|
with: ['@weft-js/pay/stripe'], // added too
|
|
111
|
-
|
|
129
|
+
// Refused without changes until these are present.
|
|
130
|
+
needs: ['db', 'auth'],
|
|
112
131
|
run({ engine, layer, read }) {
|
|
113
|
-
return {
|
|
132
|
+
return {
|
|
133
|
+
files: {...}, edits: {...},
|
|
134
|
+
env: { STRIPE_SECRET_KEY: 'sk_test_placeholder' },
|
|
135
|
+
next: ['...'],
|
|
136
|
+
}
|
|
114
137
|
},
|
|
115
138
|
}
|
|
116
139
|
```
|
package/docs/props.md
CHANGED
|
@@ -7,10 +7,21 @@ Leave a component's props unannotated. weft infers them from its callers and its
|
|
|
7
7
|
|
|
8
8
|
```tsx
|
|
9
9
|
function Row(props) {
|
|
10
|
-
|
|
10
|
+
const { label, onPick } = props
|
|
11
|
+
return (
|
|
12
|
+
<li>
|
|
13
|
+
<button type="button" onClick={onPick}>
|
|
14
|
+
{label.toUpperCase()}
|
|
15
|
+
</button>
|
|
16
|
+
</li>
|
|
17
|
+
)
|
|
11
18
|
}
|
|
12
19
|
export default function List() {
|
|
13
|
-
return
|
|
20
|
+
return (
|
|
21
|
+
<ul>
|
|
22
|
+
<Row label="One" onPick={() => console.log('picked one')} />
|
|
23
|
+
</ul>
|
|
24
|
+
)
|
|
14
25
|
}
|
|
15
26
|
```
|
|
16
27
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Reading reactive values
|
|
2
|
+
|
|
3
|
+
`state(initial)` returns a writable `State<T>`. Read or assign its `.value`.
|
|
4
|
+
`derived(fn)` returns a read-only `Derived<T>`. Read `.value` to evaluate it and
|
|
5
|
+
track its dependencies.
|
|
6
|
+
|
|
7
|
+
Use `memo(fn)` when a caller needs an accessor instead of a box:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { derived, memo, state } from '@weft-js/core'
|
|
11
|
+
|
|
12
|
+
const count = state(1)
|
|
13
|
+
const doubled = derived(() => count.value * 2)
|
|
14
|
+
const readDoubled = memo(() => count.value * 2)
|
|
15
|
+
|
|
16
|
+
console.log(doubled.value, readDoubled()) // 2, 2
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Both forms cache their result and refresh it when a dependency changes. `memo`
|
|
20
|
+
returns a function, so call it; it has no `.value` property. The compiler also uses
|
|
21
|
+
this accessor form to cache component children.
|
|
22
|
+
|
|
23
|
+
For side effects, `watch(source, callback)` tracks reads in `source`. Keep the
|
|
24
|
+
source pure and give reactive work an owner with a component or `createRoot`.
|
|
25
|
+
Graph tracking, mount queues and renderer helpers belong to
|
|
26
|
+
`@weft-js/core/internal`; application code uses the public lifecycle APIs.
|
package/docs/rules.md
CHANGED
|
@@ -1,32 +1,39 @@
|
|
|
1
1
|
```tsx
|
|
2
2
|
import { derived } from '@weft-js/core'
|
|
3
3
|
|
|
4
|
-
export function Cart({ items, taxRate }: {
|
|
5
|
-
|
|
4
|
+
export function Cart({ items, taxRate }: {
|
|
5
|
+
items: { id: string; name: string; price: number }[]
|
|
6
|
+
taxRate: number
|
|
7
|
+
}) {
|
|
8
|
+
const subtotal = derived(() =>
|
|
9
|
+
items.reduce((s, i) => s + i.price, 0),
|
|
10
|
+
)
|
|
6
11
|
const total = derived(() => subtotal.value * (1 + taxRate))
|
|
7
12
|
return (
|
|
8
13
|
<section>
|
|
9
|
-
<ul>
|
|
14
|
+
<ul>
|
|
15
|
+
{items.map(item => <li key={item.id}>{item.name}</li>)}
|
|
16
|
+
</ul>
|
|
10
17
|
<p>{total.value.toFixed(2)}</p>
|
|
11
18
|
</section>
|
|
12
19
|
)
|
|
13
20
|
}
|
|
14
21
|
```
|
|
15
22
|
|
|
16
|
-
1. **State is a box. Read and write `.value
|
|
17
|
-
2. **
|
|
18
|
-
3. **JSX is
|
|
19
|
-
4. **Props are
|
|
20
|
-
`const x = props.y` is a live read-only alias
|
|
23
|
+
1. **State is a box. Read and write `.value`**, including in JSX (W015).
|
|
24
|
+
2. **Compute from other values with `derived`**, not `watch` or the component body (W001).
|
|
25
|
+
3. **JSX is live.** Each `{expr}` and attribute re-evaluates when its inputs change.
|
|
26
|
+
4. **Props are current.** Destructure one level (W006). Read in `derived`, `watch`, JSX or handlers.
|
|
27
|
+
`const x = props.y` is a live read-only alias; `let x = props.y` and `props.y =` write locally; only `bind:y={box}`
|
|
21
28
|
writes out.
|
|
22
|
-
5. **The
|
|
29
|
+
5. **The body runs once.** No state-based early returns (W040) or `let` counters (W041). Branch in JSX.
|
|
23
30
|
6. **Pass a box only to give write access** (`value={coupon}`, child prop typed `State<string | null>`), or so list
|
|
24
31
|
rows can read one entry: `cart.get(p.id)` re-runs one row, `cart[p.id]` or `cart.value[p.id]` re-runs them all
|
|
25
32
|
(W017), in the list and in the row component alike.
|
|
26
|
-
7. **Lists and conditionals
|
|
27
|
-
stable: replace only the item
|
|
33
|
+
7. **Lists and conditionals**: `.map` with `key` (W010), `&&`, ternaries. Keep items
|
|
34
|
+
stable: replace only the changed item (`p.id === id ? { ...p, price } : p`), never `({ ...p })` every item
|
|
28
35
|
(W014).
|
|
29
36
|
8. **Copy a prop into state with `state.linked(() => prop)`**, not `state(prop)` (W005). `untrack(() => …)` is a
|
|
30
|
-
snapshot that never updates:
|
|
37
|
+
snapshot that never updates: never render it (W018).
|
|
31
38
|
9. **Side effects go in `watch(() => source, value => cleanup?)`**; DOM work in `onMount`. `flush()` applies pending
|
|
32
39
|
DOM updates now.
|
package/docs/rust.md
CHANGED
|
@@ -37,10 +37,16 @@ import { z } from 'zod'
|
|
|
37
37
|
import { search } from './rust'
|
|
38
38
|
|
|
39
39
|
export const searchDocs = query({
|
|
40
|
-
input: z.object({
|
|
41
|
-
|
|
40
|
+
input: z.object({
|
|
41
|
+
q: z.string().trim().min(1).max(200),
|
|
42
|
+
limit: z.int().min(1).max(50).default(10),
|
|
43
|
+
}),
|
|
44
|
+
output: z.array(z.object({
|
|
45
|
+
id: z.string(), title: z.string(), score: z.number(),
|
|
46
|
+
})),
|
|
42
47
|
tags: ['docs'],
|
|
43
|
-
|
|
48
|
+
// Was: run: ({ input }) => rank(input.q, input.limit)
|
|
49
|
+
impl: search,
|
|
44
50
|
})
|
|
45
51
|
```
|
|
46
52
|
|
|
@@ -54,8 +60,10 @@ serde = { version = "1", features = ["derive"] }
|
|
|
54
60
|
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
|
|
55
61
|
weft = { path = "../../node_modules/@weft-js/start/crate" }
|
|
56
62
|
|
|
57
|
-
[build-dependencies]
|
|
58
|
-
|
|
63
|
+
[build-dependencies.weft]
|
|
64
|
+
path = "../../node_modules/@weft-js/start/crate"
|
|
65
|
+
default-features = false
|
|
66
|
+
features = ["build"]
|
|
59
67
|
```
|
|
60
68
|
|
|
61
69
|
```rust
|
|
@@ -65,13 +73,18 @@ fn main() { weft::build::generate("search"); }
|
|
|
65
73
|
|
|
66
74
|
```rust
|
|
67
75
|
// crates/search/src/main.rs
|
|
68
|
-
|
|
76
|
+
// Types and the `Service` trait are generated from Zod.
|
|
77
|
+
weft::contract!();
|
|
69
78
|
use contract::*;
|
|
70
79
|
|
|
71
80
|
struct Search;
|
|
72
81
|
|
|
73
82
|
impl Service for Search {
|
|
74
|
-
async fn search_docs(
|
|
83
|
+
async fn search_docs(
|
|
84
|
+
&self,
|
|
85
|
+
ctx: weft::Ctx,
|
|
86
|
+
input: SearchDocsInput,
|
|
87
|
+
) -> Result<SearchDocsOutput, weft::Error> {
|
|
75
88
|
ctx.touch("docs");
|
|
76
89
|
Ok(vec![])
|
|
77
90
|
}
|
|
@@ -97,7 +110,11 @@ use contract::*;
|
|
|
97
110
|
|
|
98
111
|
struct Media;
|
|
99
112
|
impl Service for Media {
|
|
100
|
-
async fn thumbnail(
|
|
113
|
+
async fn thumbnail(
|
|
114
|
+
&self,
|
|
115
|
+
ctx: weft::Ctx,
|
|
116
|
+
input: ThumbnailInput,
|
|
117
|
+
) -> Result<ThumbnailOutput, weft::Error> { /* … */ }
|
|
101
118
|
}
|
|
102
119
|
|
|
103
120
|
weft::native!(contract::functions(Media));
|
|
@@ -106,7 +123,8 @@ weft::native!(contract::functions(Media));
|
|
|
106
123
|
```ts
|
|
107
124
|
// src/server/rust.ts
|
|
108
125
|
import { native } from '@weft-js/start/rust'
|
|
109
|
-
|
|
126
|
+
// Options: timeout, claims, validate, as for remote().
|
|
127
|
+
export const media = native('media')
|
|
110
128
|
```
|
|
111
129
|
|
|
112
130
|
- An addon runs each call on the worker pool, so a long one doesn't hold the event loop. `native()` sizes the pool to the
|
|
@@ -147,13 +165,24 @@ All three have `ctx.touch(table)`, `ctx.tag(tag)`, `ctx.remaining()`, `ctx.cance
|
|
|
147
165
|
|
|
148
166
|
<!-- example: skip a fragment of src/server/docs.ts -->
|
|
149
167
|
```ts
|
|
150
|
-
export const getDoc = query({
|
|
151
|
-
|
|
168
|
+
export const getDoc = query({
|
|
169
|
+
input, output,
|
|
170
|
+
errors: { NotFound: z.object({ id: z.string() }) },
|
|
171
|
+
impl: search,
|
|
172
|
+
})
|
|
173
|
+
// A TS body raises the same thing:
|
|
174
|
+
// run: () => raise('NotFound', { id })
|
|
152
175
|
```
|
|
153
176
|
|
|
154
177
|
```rust
|
|
155
|
-
async fn get_doc(
|
|
156
|
-
|
|
178
|
+
async fn get_doc(
|
|
179
|
+
&self,
|
|
180
|
+
ctx: weft::Ctx,
|
|
181
|
+
input: GetDocInput,
|
|
182
|
+
) -> Result<GetDocOutput, GetDocError> {
|
|
183
|
+
Err(GetDocError::NotFound(GetDocNotFound {
|
|
184
|
+
id: input.id.to_string(),
|
|
185
|
+
}))
|
|
157
186
|
}
|
|
158
187
|
```
|
|
159
188
|
|
|
@@ -189,11 +218,12 @@ request id, and the detail stays in the sidecar's log.
|
|
|
189
218
|
```ts
|
|
190
219
|
// weft.config.ts
|
|
191
220
|
rust: {
|
|
192
|
-
unions: 'allow',
|
|
221
|
+
unions: 'allow', // let plain object unions through (R102)
|
|
193
222
|
services: {
|
|
194
|
-
search: { crate: 'native/search' },
|
|
195
|
-
core: { external: true },
|
|
196
|
-
|
|
223
|
+
search: { crate: 'native/search' }, // default: crates/<name>
|
|
224
|
+
core: { external: true }, // built and run elsewhere: no cargo
|
|
225
|
+
// Links C; this host has an addon, so no fallback (R104).
|
|
226
|
+
imaging: { wasm: false },
|
|
197
227
|
},
|
|
198
228
|
}
|
|
199
229
|
```
|
package/docs/start-short.md
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
`@weft-js/start` app: file routes in `src/routes` (`_layout.tsx`, `[id].tsx`, `load`, `meta`), server functions in `src/server/**` or `*.server.ts`.
|
|
1
|
+
`@weft-js/start` app: file routes in `src/routes` (`_layout.tsx`, `[id].tsx`, `load`, `meta`), server functions in `src/server/**` or `*.server.ts`. Import your own code as `#/server/todos` (`src/server/todos`); generated files are `weft:slot/<id>` and `weft:tokens/<name>`.
|
|
2
2
|
|
|
3
3
|
- **Reads:** `query({ tags: ['todos'], auth: 'user', run: ({ ctx }) => ctx.db.select().from(todos) })`. In a component `todos().value ?? []`; in `load` and handlers `await todos.fetch()`.
|
|
4
4
|
- **Writes:** `mutation({ input, auth: 'user', invalidates: ['todos'], optimistic, run })`.
|
|
5
5
|
- **Database:** Drizzle; `await` every statement. Schema change: `weft db generate` (Q015). Inputs: `schemas.todos.insert.pick({ title: true })`.
|
|
6
|
-
- **Auth:** `ctx.user` is the user or `null`; `auth: 'user'` refuses anyone else (A002). A guard is `redirect('/login')` in a layout's `load`. `run` must still check who owns a row.
|
|
6
|
+
- **Auth:** `ctx.user` is the user or `null`; `auth: 'user'` refuses anyone else (A002). A guard is `redirect('/login')` in a layout's `load`. `run` must still check who owns a row. Login is a database session: a signed cookie caches it for pages for 5 minutes, and mutations read the database (`session.cookieCache` in `createAuth`). JWTs are API-only (`api: { functions }`).
|
|
7
7
|
- **Never** copy query results into `state`, import `db` from a component (Q012), `fetch` `/_weft/*`, or poll (`todos.live()`; rooms: `channel`).
|
|
8
|
+
- **Navigation:** chrome shared across sections (sidebar, tabs) lives in the one layout above them, following `currentPath()`; a `_layout.tsx` per section is replaced on every move between sections. Route changes show a skeleton from the page's markup; `export const skeleton = Named` (a local function of plain markup: div/section/p/h1/ul/li…, class and data-* only) overrides it.
|
|
8
9
|
- **Every page sets `meta` as you build it**, from `data` and `params`: `weft docs meta`.
|