@rivetjs/typescript-plugin 0.1.0-alpha.0 → 0.1.0-alpha.1

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/dist/index.cjs CHANGED
@@ -24,15 +24,15 @@ __export(index_exports, {
24
24
  });
25
25
  module.exports = __toCommonJS(index_exports);
26
26
  var import_node_fs5 = require("node:fs");
27
- var import_node_path6 = require("node:path");
27
+ var import_node_path7 = require("node:path");
28
28
 
29
29
  // libs/language-core/index.ts
30
- var import_node_path5 = require("node:path");
30
+ var import_node_path6 = require("node:path");
31
31
 
32
32
  // libs/compiler/codes.json
33
33
  var codes_default = {
34
34
  $comment: "Rivet diagnostic code registry. Compiler codes are assigned in rust/src/diagnostics.rs (tests/codes.test.ts keeps both in sync); language-service codes (RV2101, RV3101, RV7101, RV7102) in libs/language-core. Codes are never reused; retired codes stay listed. `docs` per code = the docs base + code (placeholder origin until the docs site is published).",
35
- docs: "https://rivetjs.dev/errors/",
35
+ docs: "https://rivetjs.severyn.pro/errors/",
36
36
  ranges: {
37
37
  RV1: "syntax / JSX / attributes",
38
38
  RV2: "resume",
@@ -63,418 +63,460 @@ var codes_default = {
63
63
  code: "RV1000",
64
64
  severity: "error",
65
65
  title: "Parse error",
66
- docs: "https://rivetjs.dev/errors/RV1000"
66
+ docs: "https://rivetjs.severyn.pro/errors/RV1000"
67
67
  },
68
68
  {
69
69
  code: "RV1001",
70
70
  severity: "error",
71
71
  title: "Spread attributes (retired: spreads compile)",
72
72
  retired: true,
73
- docs: "https://rivetjs.dev/errors/RV1001"
73
+ docs: "https://rivetjs.severyn.pro/errors/RV1001"
74
74
  },
75
75
  {
76
76
  code: "RV1002",
77
77
  severity: "error",
78
78
  title: "Spread children are not supported",
79
- docs: "https://rivetjs.dev/errors/RV1002"
79
+ docs: "https://rivetjs.severyn.pro/errors/RV1002"
80
80
  },
81
81
  {
82
82
  code: "RV1003",
83
83
  severity: "error",
84
84
  title: "Unsupported namespaced tag",
85
- docs: "https://rivetjs.dev/errors/RV1003"
85
+ docs: "https://rivetjs.severyn.pro/errors/RV1003"
86
86
  },
87
87
  {
88
88
  code: "RV1004",
89
89
  severity: "error",
90
90
  title: "Unsupported native tag",
91
- docs: "https://rivetjs.dev/errors/RV1004"
91
+ docs: "https://rivetjs.severyn.pro/errors/RV1004"
92
92
  },
93
93
  {
94
94
  code: "RV1005",
95
95
  severity: "error",
96
96
  title: "Parser-repaired native nesting",
97
- docs: "https://rivetjs.dev/errors/RV1005"
97
+ docs: "https://rivetjs.severyn.pro/errors/RV1005"
98
98
  },
99
99
  {
100
100
  code: "RV1006",
101
101
  severity: "error",
102
102
  title: "Text directly in table structure",
103
- docs: "https://rivetjs.dev/errors/RV1006"
103
+ docs: "https://rivetjs.severyn.pro/errors/RV1006"
104
104
  },
105
105
  {
106
106
  code: "RV1007",
107
107
  severity: "error",
108
108
  title: "<script> / <style> JSX content",
109
- docs: "https://rivetjs.dev/errors/RV1007"
109
+ docs: "https://rivetjs.severyn.pro/errors/RV1007"
110
110
  },
111
111
  {
112
112
  code: "RV1008",
113
113
  severity: "error",
114
114
  title: "Void element with children",
115
- docs: "https://rivetjs.dev/errors/RV1008"
115
+ docs: "https://rivetjs.severyn.pro/errors/RV1008"
116
116
  },
117
117
  {
118
118
  code: "RV1009",
119
119
  severity: "error",
120
120
  title: "Textarea model with children",
121
- docs: "https://rivetjs.dev/errors/RV1009"
121
+ docs: "https://rivetjs.severyn.pro/errors/RV1009"
122
122
  },
123
123
  {
124
124
  code: "RV1010",
125
125
  severity: "error",
126
126
  title: "Textarea / title children must be scalar text",
127
- docs: "https://rivetjs.dev/errors/RV1010"
127
+ docs: "https://rivetjs.severyn.pro/errors/RV1010"
128
128
  },
129
129
  {
130
130
  code: "RV1011",
131
131
  severity: "error",
132
132
  title: "Unsupported model binding",
133
- docs: "https://rivetjs.dev/errors/RV1011"
133
+ docs: "https://rivetjs.severyn.pro/errors/RV1011"
134
134
  },
135
135
  {
136
136
  code: "RV1012",
137
137
  severity: "error",
138
138
  title: "Unknown native event",
139
- docs: "https://rivetjs.dev/errors/RV1012"
139
+ docs: "https://rivetjs.severyn.pro/errors/RV1012"
140
140
  },
141
141
  {
142
142
  code: "RV1013",
143
143
  severity: "error",
144
144
  title: "Unsupported global event attribute",
145
- docs: "https://rivetjs.dev/errors/RV1013"
145
+ docs: "https://rivetjs.severyn.pro/errors/RV1013"
146
146
  },
147
147
  {
148
148
  code: "RV1014",
149
149
  severity: "error",
150
150
  title: "Structural prop passes a value instead of an accessor (retired: component props are live, each={xs()} / when={on()} update)",
151
151
  retired: true,
152
- docs: "https://rivetjs.dev/errors/RV1014"
152
+ docs: "https://rivetjs.severyn.pro/errors/RV1014"
153
153
  },
154
154
  {
155
155
  code: "RV1015",
156
156
  severity: "error",
157
157
  title: "Event handler is a literal (string / number / boolean), not a function",
158
- docs: "https://rivetjs.dev/errors/RV1015"
158
+ docs: "https://rivetjs.severyn.pro/errors/RV1015"
159
159
  },
160
160
  {
161
161
  code: "RV1016",
162
162
  severity: "error",
163
163
  title: "Event handler reads state when it binds (handlers bind once; wrap it in an arrow)",
164
- docs: "https://rivetjs.dev/errors/RV1016"
164
+ docs: "https://rivetjs.severyn.pro/errors/RV1016"
165
165
  },
166
166
  {
167
167
  code: "RV1101",
168
168
  severity: "error",
169
169
  title: "Unsafe or unsupported native attribute / URL",
170
- docs: "https://rivetjs.dev/errors/RV1101"
170
+ docs: "https://rivetjs.severyn.pro/errors/RV1101"
171
171
  },
172
172
  {
173
173
  code: "RV1102",
174
174
  severity: "error",
175
175
  title: "Invalid preload directive value",
176
- docs: "https://rivetjs.dev/errors/RV1102"
176
+ docs: "https://rivetjs.severyn.pro/errors/RV1102"
177
177
  },
178
178
  {
179
179
  code: "RV1103",
180
180
  severity: "error",
181
181
  title: "Invalid clientEvent target",
182
- docs: "https://rivetjs.dev/errors/RV1103"
182
+ docs: "https://rivetjs.severyn.pro/errors/RV1103"
183
183
  },
184
184
  {
185
185
  code: "RV2001",
186
186
  severity: "error",
187
187
  title: "Browser global read during server rendering (resume)",
188
- docs: "https://rivetjs.dev/errors/RV2001"
188
+ docs: "https://rivetjs.severyn.pro/errors/RV2001"
189
189
  },
190
190
  {
191
191
  code: "RV2002",
192
192
  severity: "error",
193
193
  title: "Resumable ref callback must assign a local",
194
- docs: "https://rivetjs.dev/errors/RV2002"
194
+ docs: "https://rivetjs.severyn.pro/errors/RV2002"
195
195
  },
196
196
  {
197
197
  code: "RV2003",
198
198
  severity: "error",
199
199
  title: "Captured class instance is not serializable",
200
- docs: "https://rivetjs.dev/errors/RV2003"
200
+ docs: "https://rivetjs.severyn.pro/errors/RV2003"
201
201
  },
202
202
  {
203
203
  code: "RV2004",
204
204
  severity: "error",
205
205
  title: "Capture-phase handlers are not resumable (retired: onXCapture resumes)",
206
206
  retired: true,
207
- docs: "https://rivetjs.dev/errors/RV2004"
207
+ docs: "https://rivetjs.severyn.pro/errors/RV2004"
208
208
  },
209
209
  {
210
210
  code: "RV2005",
211
211
  severity: "error",
212
212
  title: "Ref-bound local assigned outside ref callbacks",
213
- docs: "https://rivetjs.dev/errors/RV2005"
213
+ docs: "https://rivetjs.severyn.pro/errors/RV2005"
214
214
  },
215
215
  {
216
216
  code: "RV2006",
217
217
  severity: "error",
218
218
  title: "JSX inside handlers, effects and computeds",
219
- docs: "https://rivetjs.dev/errors/RV2006"
219
+ docs: "https://rivetjs.severyn.pro/errors/RV2006"
220
220
  },
221
221
  {
222
222
  code: "RV2007",
223
223
  severity: "error",
224
224
  title: "Resumable code captures `this`",
225
- docs: "https://rivetjs.dev/errors/RV2007"
225
+ docs: "https://rivetjs.severyn.pro/errors/RV2007"
226
226
  },
227
227
  {
228
228
  code: "RV2008",
229
229
  severity: "error",
230
230
  title: "Resumed module exports a different binding under a captured name",
231
- docs: "https://rivetjs.dev/errors/RV2008"
231
+ docs: "https://rivetjs.severyn.pro/errors/RV2008"
232
232
  },
233
233
  {
234
234
  code: "RV2009",
235
235
  severity: "error",
236
236
  title: "Two module references share a browser name",
237
- docs: "https://rivetjs.dev/errors/RV2009"
237
+ docs: "https://rivetjs.severyn.pro/errors/RV2009"
238
238
  },
239
239
  {
240
240
  code: "RV2010",
241
241
  severity: "error",
242
242
  title: "One module reference id resolves to two installed copies (Vite build)",
243
- docs: "https://rivetjs.dev/errors/RV2010"
243
+ docs: "https://rivetjs.severyn.pro/errors/RV2010"
244
244
  },
245
245
  {
246
246
  code: "RV2011",
247
247
  severity: "error",
248
248
  title: "Mutually recursive functions separated by an early exit (resume)",
249
- docs: "https://rivetjs.dev/errors/RV2011"
249
+ docs: "https://rivetjs.severyn.pro/errors/RV2011"
250
250
  },
251
251
  {
252
252
  code: "RV2012",
253
253
  severity: "error",
254
254
  title: "`.bind` with a spread first argument (resume; a warning when the receiver may be an object with its own `bind`)",
255
- docs: "https://rivetjs.dev/errors/RV2012"
255
+ docs: "https://rivetjs.severyn.pro/errors/RV2012"
256
256
  },
257
257
  {
258
258
  code: "RV2013",
259
259
  severity: "warning",
260
260
  title: "`.bind` of a value Rivet cannot resume (resume; an error when it is provably a function)",
261
- docs: "https://rivetjs.dev/errors/RV2013"
261
+ docs: "https://rivetjs.severyn.pro/errors/RV2013"
262
262
  },
263
263
  {
264
264
  code: "RV2014",
265
265
  severity: "warning",
266
266
  title: "`.bind(\u2026)` inside an object literal a resumable closure reaches (resume)",
267
- docs: "https://rivetjs.dev/errors/RV2014"
267
+ docs: "https://rivetjs.severyn.pro/errors/RV2014"
268
268
  },
269
269
  {
270
270
  code: "RV2015",
271
271
  severity: "warning",
272
272
  title: "Component returns a function that cannot resume: anonymous default export or wrapper call (resume)",
273
- docs: "https://rivetjs.dev/errors/RV2015"
273
+ docs: "https://rivetjs.severyn.pro/errors/RV2015"
274
+ },
275
+ {
276
+ code: "RV2016",
277
+ severity: "warning",
278
+ title: "`resource()` in a form that cannot resume: destructured, a `let`, not a `const` initializer, or inside a hook (resume)",
279
+ docs: "https://rivetjs.severyn.pro/errors/RV2016"
274
280
  },
275
281
  {
276
282
  code: "RV2101",
277
283
  severity: "warning",
278
284
  title: "Captured value not serializable (type-aware, language service)",
279
- docs: "https://rivetjs.dev/errors/RV2101"
285
+ docs: "https://rivetjs.severyn.pro/errors/RV2101"
280
286
  },
281
287
  {
282
288
  code: "RV2102",
283
289
  severity: "error",
284
290
  title: "Captured class instance cannot resume as data (private field, decorator, non-data base or resource); at render: [serialize] without static [revive]",
285
- docs: "https://rivetjs.dev/errors/RV2102"
291
+ docs: "https://rivetjs.severyn.pro/errors/RV2102"
292
+ },
293
+ {
294
+ code: "RV2103",
295
+ severity: "error",
296
+ title: "Render is not deterministic under the derived resume model (Date.now / Math.random / performance.now / crypto.randomUUID / new Date() while rendering a boundary)",
297
+ docs: "https://rivetjs.severyn.pro/errors/RV2103"
298
+ },
299
+ {
300
+ code: "RV2104",
301
+ severity: "warning",
302
+ title: "Keyed state (signal / createStore) created in a loop under the derived resume model",
303
+ docs: "https://rivetjs.severyn.pro/errors/RV2104"
304
+ },
305
+ {
306
+ code: "RV2105",
307
+ severity: "warning",
308
+ title: "Keyed `For` row renders non-deterministically (Date.now / Math.random / performance.now / crypto.randomUUID / getRandomValues / Date() / new Date()): its rows stay recorded instead of lazy (resume)",
309
+ docs: "https://rivetjs.severyn.pro/errors/RV2105"
310
+ },
311
+ {
312
+ code: "RV2106",
313
+ severity: "warning",
314
+ title: "Keyed `For` row holds a site a lazy row cannot replay (clientOnly / Async / Portal / a ref into a local outside the row): its rows stay recorded (resume)",
315
+ docs: "https://rivetjs.severyn.pro/errors/RV2106"
286
316
  },
287
317
  {
288
318
  code: "RV2145",
289
319
  severity: "error",
290
320
  title: "A module is claimed by both the Rivet compiler and a framework bridge, or by two bridges (Vite build)",
291
- docs: "https://rivetjs.dev/errors/RV2145"
321
+ docs: "https://rivetjs.severyn.pro/errors/RV2145"
292
322
  },
293
323
  {
294
324
  code: "RV2146",
295
325
  severity: "warning",
296
326
  title: "Non-component export of a framework module used in browser code of a Rivet module (Vite build)",
297
- docs: "https://rivetjs.dev/errors/RV2146"
327
+ docs: "https://rivetjs.severyn.pro/errors/RV2146"
298
328
  },
299
329
  {
300
330
  code: "RV2148",
301
331
  severity: "error",
302
332
  title: "A module owned by a framework bridge uses the Rivet runtime (Vite build)",
303
- docs: "https://rivetjs.dev/errors/RV2148"
333
+ docs: "https://rivetjs.severyn.pro/errors/RV2148"
304
334
  },
305
335
  {
306
336
  code: "RV2149",
307
337
  severity: "warning",
308
338
  title: "A framework island recovered from a hydration error by rendering on the client (browser)",
309
- docs: "https://rivetjs.dev/errors/RV2149"
339
+ docs: "https://rivetjs.severyn.pro/errors/RV2149"
310
340
  },
311
341
  {
312
342
  code: "RV2150",
313
343
  severity: "error",
314
344
  title: "The installed Preact no longer reads the island useId mask: Preact island ids would not be unique (server)",
315
- docs: "https://rivetjs.dev/errors/RV2150"
345
+ docs: "https://rivetjs.severyn.pro/errors/RV2150"
316
346
  },
317
347
  {
318
348
  code: "RV3001",
319
349
  severity: "error",
320
350
  title: "Target helper re-exports",
321
- docs: "https://rivetjs.dev/errors/RV3001"
351
+ docs: "https://rivetjs.severyn.pro/errors/RV3001"
322
352
  },
323
353
  {
324
354
  code: "RV3002",
325
355
  severity: "error",
326
356
  title: "Target helper / runtime namespace imports",
327
- docs: "https://rivetjs.dev/errors/RV3002"
357
+ docs: "https://rivetjs.severyn.pro/errors/RV3002"
328
358
  },
329
359
  {
330
360
  code: "RV3003",
331
361
  severity: "error",
332
362
  title: "Structural helper namespace imports",
333
- docs: "https://rivetjs.dev/errors/RV3003"
363
+ docs: "https://rivetjs.severyn.pro/errors/RV3003"
334
364
  },
335
365
  {
336
366
  code: "RV3004",
337
367
  severity: "error",
338
368
  title: "Structural helper aliases",
339
- docs: "https://rivetjs.dev/errors/RV3004"
369
+ docs: "https://rivetjs.severyn.pro/errors/RV3004"
340
370
  },
341
371
  {
342
372
  code: "RV3005",
343
373
  severity: "error",
344
374
  title: "Server clientOnly must be called directly",
345
- docs: "https://rivetjs.dev/errors/RV3005"
375
+ docs: "https://rivetjs.severyn.pro/errors/RV3005"
346
376
  },
347
377
  {
348
378
  code: "RV3006",
349
379
  severity: "error",
350
380
  title: "Server clientOnly erasure shape",
351
- docs: "https://rivetjs.dev/errors/RV3006"
381
+ docs: "https://rivetjs.severyn.pro/errors/RV3006"
352
382
  },
353
383
  {
354
384
  code: "RV3007",
355
385
  severity: "error",
356
- title: "Route module server export shape",
357
- docs: "https://rivetjs.dev/errors/RV3007"
386
+ title: "Route module server export shape (own statement, wrapper position, duplicate kind, export *)",
387
+ docs: "https://rivetjs.severyn.pro/errors/RV3007"
358
388
  },
359
389
  {
360
390
  code: "RV3008",
361
391
  severity: "error",
362
392
  title: "Server-only code referenced by browser code",
363
- docs: "https://rivetjs.dev/errors/RV3008"
393
+ docs: "https://rivetjs.severyn.pro/errors/RV3008"
364
394
  },
365
395
  {
366
396
  code: "RV3009",
367
397
  severity: "error",
368
398
  title: "Server-only code renders JSX in a resumed route module",
369
- docs: "https://rivetjs.dev/errors/RV3009"
399
+ docs: "https://rivetjs.severyn.pro/errors/RV3009"
400
+ },
401
+ {
402
+ code: "RV3010",
403
+ severity: "error",
404
+ title: "Untagged server export: a former magic export name without its wrapper call",
405
+ docs: "https://rivetjs.severyn.pro/errors/RV3010"
406
+ },
407
+ {
408
+ code: "RV3011",
409
+ severity: "error",
410
+ title: "Server-only module reached from client-rendered code (router build)",
411
+ docs: "https://rivetjs.severyn.pro/errors/RV3011"
370
412
  },
371
413
  {
372
414
  code: "RV3101",
373
415
  severity: "warning",
374
416
  title: "Link matches no route (language service)",
375
- docs: "https://rivetjs.dev/errors/RV3101"
417
+ docs: "https://rivetjs.severyn.pro/errors/RV3101"
376
418
  },
377
419
  {
378
420
  code: "RV4001",
379
421
  severity: "error",
380
422
  title: "Unknown message key / prefix",
381
- docs: "https://rivetjs.dev/errors/RV4001"
423
+ docs: "https://rivetjs.severyn.pro/errors/RV4001"
382
424
  },
383
425
  {
384
426
  code: "RV4002",
385
427
  severity: "error",
386
428
  title: "Unknown or missing message param",
387
- docs: "https://rivetjs.dev/errors/RV4002"
429
+ docs: "https://rivetjs.severyn.pro/errors/RV4002"
388
430
  },
389
431
  {
390
432
  code: "RV4003",
391
433
  severity: "error",
392
434
  title: "Static call arguments",
393
- docs: "https://rivetjs.dev/errors/RV4003"
435
+ docs: "https://rivetjs.severyn.pro/errors/RV4003"
394
436
  },
395
437
  {
396
438
  code: "RV4004",
397
439
  severity: "error",
398
440
  title: "Message tags",
399
- docs: "https://rivetjs.dev/errors/RV4004"
441
+ docs: "https://rivetjs.severyn.pro/errors/RV4004"
400
442
  },
401
443
  {
402
444
  code: "RV4005",
403
445
  severity: "error",
404
446
  title: "Static call configuration",
405
- docs: "https://rivetjs.dev/errors/RV4005"
447
+ docs: "https://rivetjs.severyn.pro/errors/RV4005"
406
448
  },
407
449
  {
408
450
  code: "RV5001",
409
451
  severity: "error",
410
452
  title: "css`` syntax",
411
- docs: "https://rivetjs.dev/errors/RV5001"
453
+ docs: "https://rivetjs.severyn.pro/errors/RV5001"
412
454
  },
413
455
  {
414
456
  code: "RV5002",
415
457
  severity: "error",
416
458
  title: "css`` usage (declaration, interpolation, unknown or written class)",
417
- docs: "https://rivetjs.dev/errors/RV5002"
459
+ docs: "https://rivetjs.severyn.pro/errors/RV5002"
418
460
  },
419
461
  {
420
462
  code: "RV5003",
421
463
  severity: "error",
422
464
  title: "Invalid or unsafe class / style attribute value",
423
- docs: "https://rivetjs.dev/errors/RV5003"
465
+ docs: "https://rivetjs.severyn.pro/errors/RV5003"
424
466
  },
425
467
  {
426
468
  code: "RV6001",
427
469
  severity: "error",
428
470
  title: "<slot> outside a component or without its props parameter",
429
- docs: "https://rivetjs.dev/errors/RV6001"
471
+ docs: "https://rivetjs.severyn.pro/errors/RV6001"
430
472
  },
431
473
  {
432
474
  code: "RV6002",
433
475
  severity: "error",
434
476
  title: "Invalid r:name / r:args",
435
- docs: "https://rivetjs.dev/errors/RV6002"
477
+ docs: "https://rivetjs.severyn.pro/errors/RV6002"
436
478
  },
437
479
  {
438
480
  code: "RV6003",
439
481
  severity: "error",
440
482
  title: "Invalid r:slot usage",
441
- docs: "https://rivetjs.dev/errors/RV6003"
483
+ docs: "https://rivetjs.severyn.pro/errors/RV6003"
442
484
  },
443
485
  {
444
486
  code: "RV6004",
445
487
  severity: "error",
446
488
  title: "Slot content passed twice",
447
- docs: "https://rivetjs.dev/errors/RV6004"
489
+ docs: "https://rivetjs.severyn.pro/errors/RV6004"
448
490
  },
449
491
  {
450
492
  code: "RV7001",
451
493
  severity: "error",
452
494
  title: "Invalid compiler option",
453
- docs: "https://rivetjs.dev/errors/RV7001"
495
+ docs: "https://rivetjs.severyn.pro/errors/RV7001"
454
496
  },
455
497
  {
456
498
  code: "RV7101",
457
499
  severity: "error",
458
500
  title: "Editor manifest (.rivet/editor.json) missing (rivet check)",
459
- docs: "https://rivetjs.dev/errors/RV7101"
501
+ docs: "https://rivetjs.severyn.pro/errors/RV7101"
460
502
  },
461
503
  {
462
504
  code: "RV7102",
463
505
  severity: "warning",
464
506
  title: "Editor manifest (.rivet/editor.json) stale (rivet check)",
465
- docs: "https://rivetjs.dev/errors/RV7102"
507
+ docs: "https://rivetjs.severyn.pro/errors/RV7102"
466
508
  },
467
509
  {
468
510
  code: "RV9000",
469
511
  severity: "error",
470
512
  title: "Unclassified compiler error",
471
- docs: "https://rivetjs.dev/errors/RV9000"
513
+ docs: "https://rivetjs.severyn.pro/errors/RV9000"
472
514
  },
473
515
  {
474
516
  code: "RV9001",
475
517
  severity: "error",
476
518
  title: "Internal compiler error (incl. compiler panic)",
477
- docs: "https://rivetjs.dev/errors/RV9001"
519
+ docs: "https://rivetjs.severyn.pro/errors/RV9001"
478
520
  }
479
521
  ]
480
522
  };
@@ -530,7 +572,7 @@ function routeCompletion(route) {
530
572
  }
531
573
 
532
574
  // libs/language-core/links.ts
533
- var LINK_MODULES = ["@rivetjs/router", "@rivetjs/router/client"];
575
+ var LINK_MODULES = ["@rivetjs/router", "@rivetjs/router/client", "@rivetjs/router/link"];
534
576
  function narrowed(ts, type) {
535
577
  if (!type) return false;
536
578
  const members = type.isUnion() ? type.types : [type];
@@ -659,8 +701,88 @@ function manifestStatus(root) {
659
701
  return { status: "ok" };
660
702
  }
661
703
 
704
+ // libs/language-core/server-exports.ts
705
+ var import_node_path2 = require("node:path");
706
+ var ROUTER = "@rivetjs/router";
707
+ var MAGIC_SERVER_EXPORTS = {
708
+ serverLoad: { wrapper: "load", name: "loader", argument: "(event) => \u2026" },
709
+ serverGuard: { wrapper: "guard", name: "access", argument: "(event) => \u2026" },
710
+ serverActions: { wrapper: "actions", name: "forms", argument: "{ name: (event) => \u2026 }" },
711
+ serverEntries: { wrapper: "entries", name: "paths", argument: "() => [\u2026]" },
712
+ serverHeaders: { wrapper: "headers", name: "responseHeaders", argument: "(event) => ({ \u2026 })" },
713
+ onRequest: { wrapper: "onRequest", name: "middleware", argument: "(event) => \u2026" },
714
+ onGet: { wrapper: "onGet", name: "GET", argument: "(event) => \u2026" },
715
+ onPost: { wrapper: "onPost", name: "POST", argument: "(event) => \u2026" },
716
+ onPut: { wrapper: "onPut", name: "PUT", argument: "(event) => \u2026" },
717
+ onPatch: { wrapper: "onPatch", name: "PATCH", argument: "(event) => \u2026" },
718
+ onDelete: { wrapper: "onDelete", name: "DELETE", argument: "(event) => \u2026" },
719
+ onOptions: { wrapper: "onOptions", name: "OPTIONS", argument: "(event) => \u2026" }
720
+ };
721
+ function untaggedMessage(name) {
722
+ const magic = MAGIC_SERVER_EXPORTS[name];
723
+ if (!magic) return void 0;
724
+ return `Untagged server export \`${name}\`: route server code is a wrapper call \u2014 \`export const ${magic.name} = ${magic.wrapper}(${magic.argument})\` with \`import { ${magic.wrapper} } from "${ROUTER}"\``;
725
+ }
726
+ function isRouteModule(root, routesDir, fileName) {
727
+ if (!routesDir || !/\.[cm]?[jt]sx?$/.test(fileName) || /\.d\.[cm]?ts$/.test(fileName)) return false;
728
+ const path = (0, import_node_path2.relative)((0, import_node_path2.join)(root, routesDir), fileName);
729
+ return path !== "" && !path.startsWith("..") && !(0, import_node_path2.isAbsolute)(path);
730
+ }
731
+ function wrapperBindings(ts, file) {
732
+ const named = /* @__PURE__ */ new Set();
733
+ const namespaces = /* @__PURE__ */ new Set();
734
+ const wrappers = new Set(Object.values(MAGIC_SERVER_EXPORTS).map((m) => m.wrapper));
735
+ for (const statement of file.statements) {
736
+ if (!ts.isImportDeclaration(statement) || !ts.isStringLiteral(statement.moduleSpecifier) || statement.moduleSpecifier.text !== ROUTER) continue;
737
+ const clause = statement.importClause;
738
+ if (!clause || clause.isTypeOnly || !clause.namedBindings) continue;
739
+ if (ts.isNamespaceImport(clause.namedBindings)) namespaces.add(clause.namedBindings.name.text);
740
+ else
741
+ for (const element of clause.namedBindings.elements)
742
+ if (!element.isTypeOnly && wrappers.has((element.propertyName ?? element.name).text)) named.add(element.name.text);
743
+ }
744
+ return { named, namespaces };
745
+ }
746
+ function isWrapperCall(ts, init2, bindings) {
747
+ while (init2 && ts.isParenthesizedExpression(init2)) init2 = init2.expression;
748
+ if (!init2 || !ts.isCallExpression(init2)) return false;
749
+ const callee = init2.expression;
750
+ if (ts.isIdentifier(callee)) return bindings.named.has(callee.text);
751
+ return ts.isPropertyAccessExpression(callee) && ts.isIdentifier(callee.expression) && bindings.namespaces.has(callee.expression.text);
752
+ }
753
+ var exported = (ts, node) => ts.canHaveModifiers(node) && (ts.getModifiers(node) ?? []).some((m) => m.kind === ts.SyntaxKind.ExportKeyword);
754
+ function untaggedServerExports(ts, file) {
755
+ if (!/\b(server(Load|Guard|Actions|Entries|Headers)|on(Request|Get|Post|Put|Patch|Delete|Options))\b/.test(file.text)) return [];
756
+ const bindings = wrapperBindings(ts, file);
757
+ const out = [];
758
+ const report = (name) => {
759
+ const message = untaggedMessage(name.text);
760
+ if (!message) return;
761
+ const start = name.getStart(file);
762
+ const { line, character } = file.getLineAndCharacterOfPosition(start);
763
+ out.push({ code: "RV3010", severity: "error", message, filename: file.fileName, start, end: name.end, line: line + 1, column: character + 1, targets: [] });
764
+ };
765
+ for (const statement of file.statements) {
766
+ if (ts.isVariableStatement(statement) && exported(ts, statement)) {
767
+ const isConst = Boolean(statement.declarationList.flags & ts.NodeFlags.Const);
768
+ for (const d of statement.declarationList.declarations)
769
+ if (ts.isIdentifier(d.name) && !(isConst && isWrapperCall(ts, d.initializer, bindings))) report(d.name);
770
+ } else if ((ts.isFunctionDeclaration(statement) || ts.isClassDeclaration(statement)) && statement.name && exported(ts, statement)) report(statement.name);
771
+ else if (ts.isExportDeclaration(statement) && !statement.isTypeOnly && !statement.moduleSpecifier && statement.exportClause && ts.isNamedExports(statement.exportClause))
772
+ for (const element of statement.exportClause.elements) {
773
+ if (element.isTypeOnly || !MAGIC_SERVER_EXPORTS[element.name.text]) continue;
774
+ const local = (element.propertyName ?? element.name).text;
775
+ const declared = file.statements.some(
776
+ (s) => ts.isVariableStatement(s) && s.declarationList.flags & ts.NodeFlags.Const && s.declarationList.declarations.some((d) => ts.isIdentifier(d.name) && d.name.text === local && isWrapperCall(ts, d.initializer, bindings))
777
+ );
778
+ if (!declared) report(element.name);
779
+ }
780
+ }
781
+ return out;
782
+ }
783
+
662
784
  // libs/language-core/fixes.ts
663
- var FIXABLE_CODES = ["RV1008", "RV1009", "RV1016", "RV1102", "RV2001", "RV2002", "RV2003", "RV2102", "RV3002"];
785
+ var FIXABLE_CODES = ["RV1008", "RV1009", "RV1016", "RV1102", "RV2001", "RV2002", "RV2003", "RV2016", "RV2102", "RV3002", "RV3010"];
664
786
  var PRELOAD = ["eager", "idle", "visible", "intent", "interaction"];
665
787
  function distance(a, b) {
666
788
  const row = Array.from({ length: b.length + 1 }, (_, i) => i);
@@ -819,10 +941,212 @@ function importedAs(ts, file, modules, name) {
819
941
  }
820
942
  return void 0;
821
943
  }
944
+ function usesFunctionScope(ts, body) {
945
+ let found = false;
946
+ const visit = (n) => {
947
+ if (found) return;
948
+ if (n.kind === ts.SyntaxKind.ThisKeyword || n.kind === ts.SyntaxKind.SuperKeyword || ts.isIdentifier(n) && n.text === "arguments") found = true;
949
+ else if (!(ts.isFunctionLike(n) && !ts.isArrowFunction(n)) && !ts.isClassLike(n)) ts.forEachChild(n, visit);
950
+ };
951
+ ts.forEachChild(body, visit);
952
+ return found;
953
+ }
954
+ function moduleNames(ts, file) {
955
+ const names = /* @__PURE__ */ new Set();
956
+ const bind = (name) => {
957
+ if (ts.isIdentifier(name)) names.add(name.text);
958
+ else for (const element of name.elements) if (!ts.isOmittedExpression(element)) bind(element.name);
959
+ };
960
+ for (const statement of file.statements) {
961
+ if (ts.isVariableStatement(statement)) for (const d of statement.declarationList.declarations) bind(d.name);
962
+ else if ((ts.isFunctionDeclaration(statement) || ts.isClassDeclaration(statement) || ts.isEnumDeclaration(statement)) && statement.name) names.add(statement.name.text);
963
+ else if (ts.isImportDeclaration(statement) && statement.importClause) {
964
+ const clause = statement.importClause;
965
+ if (clause.name) names.add(clause.name.text);
966
+ if (clause.namedBindings && ts.isNamespaceImport(clause.namedBindings)) names.add(clause.namedBindings.name.text);
967
+ else if (clause.namedBindings) for (const element of clause.namedBindings.elements) names.add(element.name.text);
968
+ }
969
+ }
970
+ return names;
971
+ }
972
+ function wrapServerExport(ts, file, node) {
973
+ if (!ts.isIdentifier(node)) return [];
974
+ const old = node.text;
975
+ const magic = MAGIC_SERVER_EXPORTS[old];
976
+ if (!magic) return [];
977
+ const statement = ts.findAncestor(node, (n) => n.parent === file);
978
+ if (!statement) return [];
979
+ const local = importedAs(ts, file, [ROUTER], magic.wrapper);
980
+ const names = moduleNames(ts, file);
981
+ const declared = (name) => name !== old && names.has(name);
982
+ if (!local && declared(magic.wrapper)) return [];
983
+ const call = local ?? magic.wrapper;
984
+ const next = !declared(magic.name) ? magic.name : !declared(magic.name + "Export") ? magic.name + "Export" : void 0;
985
+ if (!next) return [];
986
+ const edits = [];
987
+ const text = file.text;
988
+ if (ts.isVariableStatement(statement)) {
989
+ const [d, ...rest] = statement.declarationList.declarations;
990
+ if (!d || rest.length || d.name !== node || !d.initializer) return [];
991
+ const init2 = d.initializer;
992
+ const type = d.type ? text.slice(d.type.getStart(file), d.type.end) : void 0;
993
+ edits.push({ start: statement.getStart(file), end: init2.getStart(file), newText: `export const ${next} = ${call}(${type ? "(" : ""}` });
994
+ edits.push({ start: init2.end, end: init2.end, newText: type ? `) satisfies ${type})` : ")" });
995
+ } else if (ts.isFunctionDeclaration(statement) && statement.name === node && statement.body) {
996
+ const async = (ts.getModifiers(statement) ?? []).some((m) => m.kind === ts.SyntaxKind.AsyncKeyword);
997
+ const open = statement.parameters.pos - 1;
998
+ if (statement.asteriskToken || statement.typeParameters || usesFunctionScope(ts, statement.body)) {
999
+ const from = statement.typeParameters ? statement.typeParameters.pos - 1 : open;
1000
+ edits.push({ start: statement.getStart(file), end: from, newText: `export const ${next} = ${call}(${async ? "async " : ""}function${statement.asteriskToken ? "*" : ""} ${next}` });
1001
+ } else {
1002
+ edits.push({ start: statement.getStart(file), end: open, newText: `export const ${next} = ${call}(${async ? "async " : ""}` });
1003
+ const head = statement.type ? statement.type.end : statement.parameters.end + 1;
1004
+ edits.push({ start: head, end: statement.body.getStart(file), newText: " => " });
1005
+ }
1006
+ edits.push({ start: statement.body.end, end: statement.body.end, newText: ");" });
1007
+ } else return [];
1008
+ const { copy, checker } = fileChecker(ts, file);
1009
+ const own = nodeAt(ts, copy, node.getStart(file), node.end);
1010
+ const target = own && checker.getSymbolAtLocation(own);
1011
+ if (target) {
1012
+ const visit = (n) => {
1013
+ if (ts.isIdentifier(n) && n.text === old && n !== own) {
1014
+ if (ts.isShorthandPropertyAssignment(n.parent) && n.parent.name === n) {
1015
+ if (checker.getShorthandAssignmentValueSymbol(n.parent) === target) edits.push({ start: n.getStart(copy), end: n.end, newText: `${old}: ${next}` });
1016
+ } else if (checker.getSymbolAtLocation(n) === target) edits.push({ start: n.getStart(copy), end: n.end, newText: next });
1017
+ return;
1018
+ }
1019
+ ts.forEachChild(n, visit);
1020
+ };
1021
+ visit(copy);
1022
+ }
1023
+ if (!local) edits.push(...addNamedImport(ts, file, ROUTER, magic.wrapper));
1024
+ return [{ fixName: "rivetWrapServerExport", description: `Wrap in \`${magic.wrapper}(\u2026)\`: export const ${next} = ${magic.wrapper}(\u2026)`, edits }];
1025
+ }
1026
+ function handleName(file, ts, call) {
1027
+ for (const arg of call.arguments) {
1028
+ const m = ts.isIdentifier(arg) ? /^(?:load|fetch|get)([A-Z][\w$]*)$/.exec(arg.text) : null;
1029
+ if (m) return uniqueName(file, m[1][0].toLowerCase() + m[1].slice(1));
1030
+ }
1031
+ return uniqueName(file, "res");
1032
+ }
1033
+ function replaceReads(ts, file, bindings) {
1034
+ const { copy, checker } = fileChecker(ts, file);
1035
+ const targets = /* @__PURE__ */ new Map();
1036
+ for (const [identifier, replacement] of bindings) {
1037
+ const own = nodeAt(ts, copy, identifier.getStart(file), identifier.end);
1038
+ const symbol = own && checker.getSymbolAtLocation(own);
1039
+ if (!symbol) return void 0;
1040
+ targets.set(symbol, [replacement, own]);
1041
+ }
1042
+ const edits = [];
1043
+ let refused = false;
1044
+ const visit = (n) => {
1045
+ if (ts.isIdentifier(n)) {
1046
+ const parent = n.parent;
1047
+ if (ts.isShorthandPropertyAssignment(parent) && parent.name === n) {
1048
+ const hit2 = targets.get(checker.getShorthandAssignmentValueSymbol(parent));
1049
+ if (hit2) edits.push({ start: n.end, end: n.end, newText: `: ${hit2[0]}` });
1050
+ return;
1051
+ }
1052
+ const symbol = checker.getSymbolAtLocation(n);
1053
+ const hit = symbol && targets.get(symbol);
1054
+ if (!hit || hit[1] === n) return;
1055
+ if (ts.isExportSpecifier(parent) || (ts.isPrefixUnaryExpression(parent) || ts.isPostfixUnaryExpression(parent)) && (parent.operator === ts.SyntaxKind.PlusPlusToken || parent.operator === ts.SyntaxKind.MinusMinusToken) || ts.isBinaryExpression(parent) && parent.left === n && parent.operatorToken.kind >= ts.SyntaxKind.FirstAssignment && parent.operatorToken.kind <= ts.SyntaxKind.LastAssignment)
1056
+ refused = true;
1057
+ else edits.push({ start: n.getStart(copy), end: n.end, newText: hit[0] });
1058
+ return;
1059
+ }
1060
+ ts.forEachChild(n, visit);
1061
+ };
1062
+ visit(copy);
1063
+ return refused ? void 0 : edits;
1064
+ }
1065
+ function resourceFixes(ts, file, node) {
1066
+ const call = ts.findAncestor(node, ts.isCallExpression);
1067
+ if (!call) return [];
1068
+ let at = call;
1069
+ while (ts.isParenthesizedExpression(at.parent) || ts.isAsExpression(at.parent) || ts.isSatisfiesExpression(at.parent) || ts.isNonNullExpression(at.parent))
1070
+ at = at.parent;
1071
+ const callText = file.text.slice(call.getStart(file), call.end);
1072
+ const declaration = at.parent;
1073
+ if (ts.isVariableDeclaration(declaration) && declaration.initializer === at) {
1074
+ const list = declaration.parent;
1075
+ if (!ts.isVariableDeclarationList(list) || list.declarations.length !== 1) return [];
1076
+ const pattern = declaration.name;
1077
+ if (ts.isIdentifier(pattern)) {
1078
+ if (list.flags & ts.NodeFlags.Const) return [];
1079
+ if (!replaceReads(ts, file, [[pattern, pattern.text]])) return [];
1080
+ const keyword = list.getStart(file);
1081
+ return [
1082
+ {
1083
+ fixName: "rivetResourceConst",
1084
+ description: `Declare it with const: const ${pattern.text} = resource(\u2026)`,
1085
+ edits: [{ start: keyword, end: keyword + 3, newText: "const" }]
1086
+ }
1087
+ ];
1088
+ }
1089
+ const name2 = handleName(file, ts, call);
1090
+ const bindings = [];
1091
+ const members = (object, base) => object.elements.every((element) => {
1092
+ if (element.dotDotDotToken || element.initializer || !ts.isIdentifier(element.name)) return false;
1093
+ const key = element.propertyName ?? element.name;
1094
+ if (!ts.isIdentifier(key)) return false;
1095
+ bindings.push([element.name, `${base}.${key.text}`]);
1096
+ return true;
1097
+ });
1098
+ if (ts.isObjectBindingPattern(pattern)) {
1099
+ if (!members(pattern, name2)) return [];
1100
+ } else {
1101
+ if (pattern.elements.length > 2) return [];
1102
+ for (const element of pattern.elements) {
1103
+ if (ts.isOmittedExpression(element)) continue;
1104
+ if (element.dotDotDotToken || element.initializer) return [];
1105
+ if (ts.isIdentifier(element.name)) bindings.push([element.name, name2]);
1106
+ else if (ts.isObjectBindingPattern(element.name) && element === pattern.elements[1]) {
1107
+ if (!members(element.name, name2)) return [];
1108
+ } else return [];
1109
+ }
1110
+ }
1111
+ const reads = replaceReads(ts, file, bindings);
1112
+ if (!reads) return [];
1113
+ const end = declaration.type ? declaration.type.end : pattern.end;
1114
+ return [
1115
+ {
1116
+ fixName: "rivetResourceConst",
1117
+ description: `Keep the handle: const ${name2} = resource(\u2026); use ${name2}.member`,
1118
+ edits: [{ start: pattern.getStart(file), end, newText: name2 }, ...reads]
1119
+ }
1120
+ ];
1121
+ }
1122
+ if (ts.isVariableDeclaration(declaration)) return [];
1123
+ const statement = bodyStatement(ts, call);
1124
+ if (!statement || ts.findAncestor(call.parent, (n) => n === statement || ts.isFunctionLike(n) || ts.isClassLike(n)) !== statement) return [];
1125
+ const fn = statement.parent.parent;
1126
+ const fnName = fn.name ?? (ts.isVariableDeclaration(fn.parent) && ts.isIdentifier(fn.parent.name) ? fn.parent.name : void 0);
1127
+ if (fnName && /^[a-z_$]/.test(fnName.text) && !(ts.getCombinedModifierFlags(fn) & ts.ModifierFlags.Default)) return [];
1128
+ const name = handleName(file, ts, call);
1129
+ const nl = newLineOf(file);
1130
+ const start = statement.getStart(file);
1131
+ return [
1132
+ {
1133
+ fixName: "rivetResourceConst",
1134
+ description: `Create it in the component: const ${name} = resource(\u2026)`,
1135
+ edits: [
1136
+ { start, end: start, newText: `const ${name} = ${callText};${nl}${indentOf(file, start)}` },
1137
+ { start: at.getStart(file), end: at.end, newText: name }
1138
+ ]
1139
+ }
1140
+ ];
1141
+ }
822
1142
  function codeFixes(ts, file, diagnostic) {
823
1143
  const node = nodeAt(ts, file, diagnostic.start, diagnostic.end);
824
1144
  if (!node) return [];
825
1145
  switch (diagnostic.code) {
1146
+ case "RV3010":
1147
+ return wrapServerExport(ts, file, node);
1148
+ case "RV2016":
1149
+ return resourceFixes(ts, file, node);
826
1150
  case "RV1102": {
827
1151
  const attribute = ts.findAncestor(node, ts.isJsxAttribute);
828
1152
  const value = attribute?.initializer;
@@ -1028,18 +1352,18 @@ function serializabilityDiagnostics(ts, checker, file, sites, verdict) {
1028
1352
 
1029
1353
  // libs/language-core/sources.ts
1030
1354
  var import_node_fs2 = require("node:fs");
1031
- var import_node_path2 = require("node:path");
1355
+ var import_node_path3 = require("node:path");
1032
1356
  var cache = /* @__PURE__ */ new Map();
1033
1357
  function sourcePackagesOf(fileName, text) {
1034
1358
  const found = /* @__PURE__ */ new Set();
1035
1359
  for (const [, specifier = ""] of text.matchAll(/(?:\bfrom|\bimport)\s*\(?\s*["']([^"'./][^"']*)["']/g)) {
1036
1360
  const name = specifier.split("/").slice(0, specifier.startsWith("@") ? 2 : 1).join("/");
1037
- const key = (0, import_node_path2.dirname)(fileName) + "\0" + name;
1361
+ const key = (0, import_node_path3.dirname)(fileName) + "\0" + name;
1038
1362
  let hit = cache.get(key);
1039
1363
  if (hit === void 0) {
1040
1364
  hit = null;
1041
- for (let dir = (0, import_node_path2.dirname)(fileName); ; dir = (0, import_node_path2.dirname)(dir)) {
1042
- const file = (0, import_node_path2.join)(dir, "node_modules", name, "package.json");
1365
+ for (let dir = (0, import_node_path3.dirname)(fileName); ; dir = (0, import_node_path3.dirname)(dir)) {
1366
+ const file = (0, import_node_path3.join)(dir, "node_modules", name, "package.json");
1043
1367
  if ((0, import_node_fs2.existsSync)(file)) {
1044
1368
  try {
1045
1369
  const pkg = JSON.parse((0, import_node_fs2.readFileSync)(file, "utf8"));
@@ -1048,7 +1372,7 @@ function sourcePackagesOf(fileName, text) {
1048
1372
  }
1049
1373
  break;
1050
1374
  }
1051
- if ((0, import_node_path2.dirname)(dir) === dir) break;
1375
+ if ((0, import_node_path3.dirname)(dir) === dir) break;
1052
1376
  }
1053
1377
  cache.set(key, hit);
1054
1378
  }
@@ -1060,7 +1384,7 @@ function sourcePackagesOf(fileName, text) {
1060
1384
  // libs/language-core/compiler.ts
1061
1385
  var import_node_fs3 = require("node:fs");
1062
1386
  var import_node_module = require("node:module");
1063
- var import_node_path3 = require("node:path");
1387
+ var import_node_path4 = require("node:path");
1064
1388
  function loadCompiler(wasm) {
1065
1389
  const module2 = new WebAssembly.Module((0, import_node_fs3.readFileSync)(wasm));
1066
1390
  let exports2 = new WebAssembly.Instance(module2, {}).exports;
@@ -1097,8 +1421,8 @@ function loadCompiler(wasm) {
1097
1421
  }
1098
1422
  function workspaceCompilerWasm(directory) {
1099
1423
  try {
1100
- const require2 = (0, import_node_module.createRequire)((0, import_node_path3.join)(directory, "noop.js"));
1101
- return (0, import_node_path3.join)((0, import_node_path3.dirname)(require2.resolve("@rivetjs/compiler")), "rivet_compiler.wasm");
1424
+ const require2 = (0, import_node_module.createRequire)((0, import_node_path4.join)(directory, "noop.js"));
1425
+ return (0, import_node_path4.join)((0, import_node_path4.dirname)(require2.resolve("@rivetjs/compiler")), "rivet_compiler.wasm");
1102
1426
  } catch {
1103
1427
  return null;
1104
1428
  }
@@ -1118,10 +1442,10 @@ function findCompiler(candidates, log) {
1118
1442
  // libs/language-core/doctor.ts
1119
1443
  var import_node_fs4 = require("node:fs");
1120
1444
  var import_node_module2 = require("node:module");
1121
- var import_node_path4 = require("node:path");
1445
+ var import_node_path5 = require("node:path");
1122
1446
  function workspaceTypeScriptVersion(directory) {
1123
1447
  try {
1124
- const require2 = (0, import_node_module2.createRequire)((0, import_node_path4.join)(directory, "noop.js"));
1448
+ const require2 = (0, import_node_module2.createRequire)((0, import_node_path5.join)(directory, "noop.js"));
1125
1449
  return JSON.parse((0, import_node_fs4.readFileSync)(require2.resolve("typescript/package.json"), "utf8")).version;
1126
1450
  } catch {
1127
1451
  return void 0;
@@ -1143,7 +1467,7 @@ function doctor(input) {
1143
1467
  message: `${input.configFile} compiles JSX with jsx=${input.jsx ?? "unset"}, jsxImportSource=${input.jsxImportSource ?? "unset"}; Rivet needs "preserve" and "@rivetjs/runtime".`,
1144
1468
  fix: `Extend tsconfig.base.json in ${input.configFile}.`
1145
1469
  });
1146
- const workspace = workspaceTypeScriptVersion((0, import_node_path4.dirname)(input.fileName));
1470
+ const workspace = workspaceTypeScriptVersion((0, import_node_path5.dirname)(input.fileName));
1147
1471
  if (workspace && workspace !== input.tsVersion)
1148
1472
  findings.push({
1149
1473
  id: "typescript-version",
@@ -1175,14 +1499,14 @@ function doctor(input) {
1175
1499
  fix: "Run `rivet sync` (or restart `vite`) in the app."
1176
1500
  });
1177
1501
  const root = input.project?.root;
1178
- if (root && input.project.manifest.routesDir && !(0, import_node_fs4.existsSync)((0, import_node_path4.join)(root, ".rivet", "routes.d.ts")))
1502
+ if (root && input.project.manifest.routesDir && !(0, import_node_fs4.existsSync)((0, import_node_path5.join)(root, ".rivet", "routes.d.ts")))
1179
1503
  findings.push({ id: "route-types", severity: "info", message: "Route types (.rivet/routes.d.ts) are missing.", fix: "Run `rivet sync` (or `vite` / `vite build`) in the app." });
1180
1504
  return findings;
1181
1505
  }
1182
1506
 
1183
1507
  // libs/language-core/explain.json
1184
1508
  var explain_default = {
1185
- $comment: "Fix texts per RV code (`explain.ts` `explainCode`): what the code means in practice and the mechanical fix, for AI assistants (MCP explain_diagnostic / check_code), rivet check and editors. Severity, title and docs URL stay in libs/compiler/codes.json; every code there has an entry here (tests/explain.test.ts). `messages` = the emitted texts ({x} = placeholder); `avoid` triggers the code, `prefer` compiles clean (verified 2026-10-08 against both targets); `quickFix` = the language-core code fix that applies, if any.",
1509
+ $comment: "Fix texts per RV code (`explain.ts` `explainCode`): what the code means in practice and the mechanical fix, for AI assistants (MCP explain_diagnostic / check_code), rivet check and editors. Severity, title and docs URL stay in libs/compiler/codes.json; every code there has an entry here (tests/explain.test.ts). Also the prose of the docs pages /errors/RVxxxx (docs-kit tools/errors.ts \u2192 apps/docs/content/errors/, regenerate after edits). `messages` = the emitted texts ({x} = placeholder); `avoid` triggers the code, `prefer` compiles clean (verified 2026-10-08 against both targets); `quickFix` = the language-core code fix that applies, if any.",
1186
1510
  codes: [
1187
1511
  {
1188
1512
  code: "RV1000",
@@ -1411,9 +1735,7 @@ var explain_default = {
1411
1735
  },
1412
1736
  {
1413
1737
  code: "RV2009",
1414
- messages: [
1415
- "Cannot resume {module}: module references `{first}` and `{second}` share the browser name `{name}`; rename one import"
1416
- ],
1738
+ messages: ["Cannot resume {module}: module references `{first}` and `{second}` share the browser name `{name}`; rename one import"],
1417
1739
  fix: "Import one of the two values under another exported name (re-export it from a small module) so their module reference names differ.",
1418
1740
  avoid: 'import { fmt } from "./a";\nimport { fmt as fmt2 } from "./b";\nexport function A() { return <List items={[fmt, fmt2]} />; }',
1419
1741
  prefer: 'import { fmt } from "./a";\nimport { fmtB } from "./b-reexport";\nexport function A() { return <List items={[fmt, fmtB]} />; }',
@@ -1481,6 +1803,17 @@ var explain_default = {
1481
1803
  prefer: "export function Count(props: { n: () => number }) {\n return () => <b>{props.n()}</b>;\n}",
1482
1804
  note: "Resume mode (RESUME-ACCESSOR-ROW fixes 1\u20132). A warning: the module compiles, but the server traversal calls the accessor outside every resume site, so a resumed page never updates it. Resumable without a warning, in a PascalCase module-level function: `return () => \u2026`, `return c ? () => a : () => b` (also `&&` / `||` / `??`), `const view = () => \u2026; return view`, a hoisted parameterless `function view() {\u2026}` returned by name, concise `(props) => () => \u2026`. Reported for an anonymous default export, a function passed to a call whose result is a PascalCase `const` or the default export (`const Card = memo((props) => () => \u2026)`), and a returned `let` bound to an accessor (declare it with `const`)."
1483
1805
  },
1806
+ {
1807
+ code: "RV2016",
1808
+ messages: [
1809
+ "`resource()` cannot resume {form}: create it in the component as `const name = resource(\u2026)` and call its methods on the handle (`name.loading()`)"
1810
+ ],
1811
+ fix: "Create the resource in the component body as the whole initializer of one `const` (`const user = resource(() => id(), loadUser)`) and call its methods on that handle (`user.refetch()`, `user.loading()`); pass the handle, not a destructured method, to children and handlers. For a reusable helper, export the fetcher and call `resource(\u2026)` in each component.",
1812
+ avoid: 'import { resource } from "@rivetjs/runtime/dom";\nasync function loadUser(_: true) {\n return "Ada";\n}\nexport function User() {\n const { refetch } = resource(loadUser);\n return <button onClick={() => refetch()}>reload</button>;\n}',
1813
+ prefer: 'import { resource } from "@rivetjs/runtime/dom";\nasync function loadUser(_: true) {\n return "Ada";\n}\nexport function User() {\n const user = resource(loadUser);\n return <button onClick={() => user.refetch()}>{user()}</button>;\n}',
1814
+ note: "Resume mode (RESOURCE-COMPILER). A warning: the module compiles, but the server encodes a resource only through the recipe of the `const` it is created in, so rendering one of these forms resumably throws `resource is not resumable here` once a resumable site captures it. Resumable without a warning: any `const r = resource(\u2026)` in a component (also in `For` rows and `Show` branches), whatever its arguments \u2014 inline arrows, module functions or imports by name (`resource(fetchUser)`, resumed by reference), local `const` fetchers, `initialValue` from props, `ssr: false`; aliased imports (`import { resource as res }`) too. Reported forms: destructuring (`const { loading } = resource(\u2026)` drops the handle its methods need; `const [data] = resource(\u2026)` \u2014 the handle is not iterable), a `let` / `var`, a call that is not a `const` initializer (JSX attribute, object value, argument, returned value, concise arrow body), and a `const` inside a named non-component function (`useUser`: its caller's `const` holds the result, not a recipe). Not reported: module scope, a discarded call, code that runs in the browser only (handlers, effects).",
1815
+ quickFix: 'rivetResourceConst: "Keep the handle: const user = resource(\u2026); use user.member" \u2192 const user = resource(\u2026); destructured reads become user.loading, user.refetch or user (Solid-style [data, { refetch }] too). The same action is "Declare it with const" for a let / var never reassigned and "Create it in the component: const user = resource(\u2026)" for a call outside a const initializer (hoisted before its statement). The handle is named after a loadUser / fetchUser / getUser fetcher, else res; no fix in a hook, for rest / default / nested patterns or for a call inside a nested function'
1816
+ },
1484
1817
  {
1485
1818
  code: "RV2101",
1486
1819
  messages: [
@@ -1508,6 +1841,48 @@ var explain_default = {
1508
1841
  note: "Resume mode (Function Bridge T24). A data class declared at module scope of a resume module registers (`_rClass`) and its captured instances resume without running the constructor. Not eligible: an instance `#private` element or `accessor` field (no private brand without the constructor), a class or member decorator (runs code while constructing), a field arrow reading `super` / `new.target` (cannot run outside the class), an `extends` other than a data class of the same module (built-ins, packages, Node `EventEmitter`), a resource read while constructing (`window`, `document`, `navigator`, `globalThis`, `localStorage`, `sessionStorage`, `WebSocket`, `EventSource`, `Worker`, `BroadcastChannel` in a field initializer or the constructor). Classes with an own or inherited `static [revive]` register as resource holders (T25): an instance with `[serialize]()` sends only that data and the browser rebuilds it with `static [revive](data)` on first use; package classes register the same hooks with `registerClass(cls, { id, serialize, revive })` from `@rivetjs/runtime/resume-format`. At render, `[serialize]` without `static [revive]` is `RV2102: {Class} has [serialize] but no static [revive]` (dev and production).",
1509
1842
  quickFix: 'rivetReconstruct: "Build it in the browser: reconstruct(() => \u2026)" \u2192 const x = reconstruct(() => new X(\u2026)); reads become x() (+ import from @rivetjs/runtime/dom)'
1510
1843
  },
1844
+ {
1845
+ code: "RV2103",
1846
+ messages: [
1847
+ "Render is not deterministic: `{call}` runs while rendering `{Component}`; move it into a keyed signal (its value is shipped) or a clientEffect"
1848
+ ],
1849
+ fix: "Compute the value once into state the page ships (`const seed = signal(Math.random())` \u2014 the browser adopts the server value) or read it in a `clientEffect` / handler; `Date.now()`, `Math.random()`, `performance.now()`, `crypto.randomUUID()` / `getRandomValues()` and `new Date()` without arguments are reported in a boundary's render path.",
1850
+ avoid: 'import { signal } from "@rivetjs/reactivity";\nexport function Stamp() {\n const n = signal(0);\n const t = Date.now();\n return <button onClick={() => n.set(t)}>{n()}</button>;\n}',
1851
+ prefer: 'import { signal } from "@rivetjs/reactivity";\nexport function Stamp() {\n const n = signal(0);\n const t = signal(Date.now());\n return <button onClick={() => n.set(t())}>{n()}</button>;\n}',
1852
+ note: 'Derived resume model only (`resume: { model: "derived" }`, plan M2 C2). The browser derives an interactive component\'s structure by running its compiled DOM function over the server nodes (adopt run): the render must be a function of its props, keyed state and route data. A non-deterministic read outside keyed state would make the adopt run disagree with the markup. Handlers, effects, client events and other browser-only closures are not checked (they never run on the server). Error: the module does not compile under the derived model.'
1853
+ },
1854
+ {
1855
+ code: "RV2104",
1856
+ messages: [
1857
+ "Keyed state `signal()` is created inside a loop: the browser pairs a site's repeated creations with the server's by order, so a loop that runs a different number of times in the adopt run adopts the wrong values; give each iteration its own component (a `<For>` row) or create the state once outside the loop",
1858
+ "Keyed state `createStore()` is created inside a loop: the browser pairs a site's repeated creations with the server's by order, so a loop that runs a different number of times in the adopt run adopts the wrong values; give each iteration its own component (a `<For>` row) or create the state once outside the loop"
1859
+ ],
1860
+ fix: "Give each iteration its own component (render a `<For>` row / child component that creates its state) or create one `signal()` / `createStore()` outside the loop holding the list (`const picked = signal(props.items.map(() => false))`).",
1861
+ avoid: 'import { signal } from "@rivetjs/reactivity";\nexport function Checks(props) {\n const picked = [];\n for (const item of props.items) picked.push(signal(false));\n return <ul>{props.items.map((item, i) => <li onClick={() => picked[i].set(true)}>{item}</li>)}</ul>;\n}',
1862
+ prefer: 'import { signal } from "@rivetjs/reactivity";\nexport function Checks(props) {\n const picked = signal(props.items.map(() => false));\n return <ul>{props.items.map((item, i) => <li onClick={() => picked.set(picked().with(i, true))}>{item}</li>)}</ul>;\n}',
1863
+ note: 'Derived resume model only (`resume: { model: "derived" }`, plan M2 C2). Every `signal(x)` / `createStore(x)` call of a resume module compiles to a keyed registration (`_rSig(key, x)`: key = module prefix + source-order site ordinal); the server ships the values per boundary and the adopt run reads them by key. Keys are per site, so state created under an `if` / `?:` / `&&` / `switch` / `try` is fine (a render that skips it skips its key; M2-R5 2026-10-20 narrowed the warning to loops). A site that registers more than once in a boundary gets an occurrence suffix (`#2`, `#3`, \u2026): those pair by order, which a loop makes depend on the iteration count. Warning: the module compiles.'
1864
+ },
1865
+ {
1866
+ code: "RV2105",
1867
+ messages: [
1868
+ "Keyed `For` row renders non-deterministically: `{call}` runs while rendering the row; a lazy row renders again in the browser, so read it outside the row (loader data, a prop, a signal) or in a handler / clientEffect (the For's rows stay recorded)"
1869
+ ],
1870
+ fix: "Compute the value outside the row (in the component body, loader data or a prop) and read that inside the row, or move the read into a handler / `clientEffect`; `Date.now()`, `Date()`, `new Date()` without arguments, `Math.random()`, `performance.now()`, `crypto.randomUUID()` and `crypto.getRandomValues()` are reported on a keyed `For` row's render path.",
1871
+ avoid: 'import { For } from "@rivetjs/runtime/dom";\nexport function Feed(props: { items: { id: string; title: string }[] }) {\n return (\n <ul>\n <For each={props.items} by={(item) => item.id}>\n {(item) => <li data-seen={Date.now()}>{item().title}</li>}\n </For>\n </ul>\n );\n}',
1872
+ prefer: 'import { For } from "@rivetjs/runtime/dom";\nexport function Feed(props: { items: { id: string; title: string }[]; seen: number }) {\n return (\n <ul>\n <For each={props.items} by={(item) => item.id}>\n {(item) => <li data-seen={props.seen}>{item().title}</li>}\n </For>\n </ul>\n );\n}',
1873
+ note: "Resume mode (lazy collection rows, LAZY-ROWS T2). A warning: the module compiles and the `For` keeps today's per-row resume records. A lazy row (the `For`'s row function as a resumable symbol, materialised in the browser per row) must render the same markup in the browser as on the server; a time / random read in the row's render path would not. Handlers, effects, client events, refs and timer callbacks inside the row are not its render and are not checked. Reported once per `For`, on the server compile."
1874
+ },
1875
+ {
1876
+ code: "RV2106",
1877
+ messages: [
1878
+ "Keyed `For` row holds {site}, which a lazy row cannot replay: move it out of the row or into a component the row renders lazily (the For's rows stay recorded)",
1879
+ "Keyed `For` row holds a `ref` into `{name}`, declared outside the row, which a lazy row cannot replay: keep the ref local to the row or read the element through the event (the For's rows stay recorded)"
1880
+ ],
1881
+ fix: "Render the client-only / streaming / portal content outside the row (one instance next to the list), or keep it in a component the row renders; for a ref, declare it inside the row (`{(row) => { const el = ref(); \u2026 }}`) or read the element from the event (`event.currentTarget`).",
1882
+ avoid: 'import { For, ref } from "@rivetjs/runtime/dom";\nexport function Menu(props: { items: { id: string; label: string }[] }) {\n const last = ref<HTMLElement>();\n return (\n <ul onKeyDown={() => last.current?.focus()}>\n <For each={props.items} by={(item) => item.id}>\n {(item) => <li ref={last}>{item().label}</li>}\n </For>\n </ul>\n );\n}',
1883
+ prefer: 'import { For } from "@rivetjs/runtime/dom";\nexport function Menu(props: { items: { id: string; label: string }[] }) {\n return (\n <ul onKeyDown={(event) => (event.currentTarget.lastElementChild as HTMLElement | null)?.focus()}>\n <For each={props.items} by={(item) => item.id}>\n {(item) => <li tabIndex={-1}>{item().label}</li>}\n </For>\n </ul>\n );\n}',
1884
+ note: "Resume mode (lazy collection rows, LAZY-ROWS T2). A warning: the module compiles and the `For` keeps today's per-row resume records. A lazy row is re-run in the browser over its server markup (adopt mode); `clientOnly(\u2026)` / an element of a `clientOnly` component, `<Async>`, a `<Portal>` element and a `ref` into a local declared outside the row cannot be replayed that way, so the whole `For` stays recorded (one rule per `For`). Reported once per `For`, on the server compile."
1885
+ },
1511
1886
  {
1512
1887
  code: "RV2145",
1513
1888
  messages: [
@@ -1531,7 +1906,7 @@ var explain_default = {
1531
1906
  code: "RV2148",
1532
1907
  messages: ["{file} is owned by {framework} but uses the Rivet runtime ({use}): Rivet code in a framework module is never compiled by Rivet"],
1533
1908
  fix: "Move the Rivet code (signals bound to Rivet JSX, `@rivetjs/runtime` helpers) into a Rivet module and import the island from it, or take the file out of the bridge's ownership: drop the framework suffix (`Chart.react.tsx` \u2192 `Chart.tsx`) or narrow the bridge's `include`.",
1534
- avoid: '// Chart.react.tsx (owned by react())\nimport { Show } from "@rivetjs/runtime";\nexport function Chart() { return <Show when={true}>x</Show>; }',
1909
+ avoid: '// Chart.react.tsx (owned by react())\nimport { Show } from "@rivetjs/runtime/dom";\nexport function Chart() { return <Show when={true}>x</Show>; }',
1535
1910
  prefer: '// Chart.react.tsx: React only\nexport function Chart() { return <div>x</div>; }\n// page.tsx (Rivet): <Show when={ready()}><Chart client="visible" /></Show>',
1536
1911
  note: "Vite plugin (`rivet({ bridges })`). A module a bridge owns is compiled by that framework's plugins only: an `@rivetjs/runtime` import (or `@jsxImportSource @rivetjs/runtime`) there would run Rivet ABI code the Rivet compiler never produced. `@rivetjs/reactivity` signals are fine inside framework components (read them through the framework's own subscription)."
1537
1912
  },
@@ -1545,7 +1920,9 @@ var explain_default = {
1545
1920
  },
1546
1921
  {
1547
1922
  code: "RV2150",
1548
- messages: ["Preact useId mask not applied (probe rendered {html}): Preact island ids would not be unique. This Preact version is not supported by @rivetjs/bridge-preact; update it or pin preact to a supported version."],
1923
+ messages: [
1924
+ "Preact useId mask not applied (probe rendered {html}): Preact island ids would not be unique. This Preact version is not supported by @rivetjs/bridge-preact; update it or pin preact to a supported version."
1925
+ ],
1549
1926
  fix: "Install a Preact version inside `@rivetjs/bridge-preact`'s peer range that the adapter supports (pin `preact` to the last working version), or update `@rivetjs/bridge-preact`.",
1550
1927
  avoid: `// package.json
1551
1928
  "preact": "^11" // a minor that moved Preact's internal useId mask`,
@@ -1605,13 +1982,15 @@ var explain_default = {
1605
1982
  {
1606
1983
  code: "RV3007",
1607
1984
  messages: [
1608
- "Declare server exports (serverLoad, onGet, \u2026) in their own export statement",
1609
- "`export *` is not allowed in a route module: server exports must be named"
1985
+ "Declare server exports (`load(\u2026)`, `onGet(\u2026)`, \u2026) in their own export statement",
1986
+ "`export *` is not allowed in a route module: server exports must be named",
1987
+ "Server export wrapper `{name}` must initialize a module-scope `const` (`export const {export} = {name}(\u2026)`)",
1988
+ "Duplicate server export: `{first}` and `{name}` are both `{kind}(\u2026)`; keep one"
1610
1989
  ],
1611
- fix: "Give each server export (serverLoad, onGet, \u2026) its own export statement and replace `export *` with named exports.",
1612
- avoid: "export const serverLoad = () => 1, other = 2;\nexport default () => <p />;",
1613
- prefer: "export const serverLoad = () => 1;\nexport const other = 2;\nexport default () => <p />;",
1614
- note: "serverExports option (route modules)."
1990
+ fix: "Give each server export its own `export const x = wrapper(\u2026)` statement at module scope (one per kind; request handlers one per method) and replace `export *` with named exports.",
1991
+ avoid: 'import { load } from "@rivetjs/router";\nexport const loader = load(() => 1), other = 2;\nexport default () => <p />;',
1992
+ prefer: 'import { load } from "@rivetjs/router";\nexport const loader = load(() => 1);\nexport const other = 2;\nexport default () => <p />;',
1993
+ note: "serverExports option (route modules). Wrappers: load, guard, actions, entries, headers, onRequest, onGet \u2026 onOptions from @rivetjs/router (aliases and namespace imports tracked)."
1615
1994
  },
1616
1995
  {
1617
1996
  code: "RV3008",
@@ -1619,25 +1998,47 @@ var explain_default = {
1619
1998
  "`{name}` is server-only (removed from browser builds) but browser code references it at line {line}",
1620
1999
  '`{name}` (from "{source}") is used by browser code at line {line}; server-only modules cannot reach the browser \u2014 use it only inside server exports'
1621
2000
  ],
1622
- fix: "Use server exports and server-only imports only inside server exports; pass their results to the page as serverLoad data instead.",
1623
- avoid: 'import { readFileSync } from "node:fs";\nexport const serverLoad = () => 1;\nexport default () => <p>{String(readFileSync)}</p>;',
1624
- prefer: 'import { readFileSync } from "node:fs";\nexport const serverLoad = () => readFileSync("a.txt", "utf8");\nexport default () => <p />;',
2001
+ fix: "Use server exports and server-only imports only inside server exports; pass their results to the page as loader data instead.",
2002
+ avoid: 'import { readFileSync } from "node:fs";\nimport { load } from "@rivetjs/router";\nexport const loader = load(() => 1);\nexport default () => <p>{String(readFileSync)}</p>;',
2003
+ prefer: 'import { readFileSync } from "node:fs";\nimport { load } from "@rivetjs/router";\nexport const loader = load(() => readFileSync("a.txt", "utf8"));\nexport default () => <p />;',
1625
2004
  note: "First variant lacks a fix hint."
1626
2005
  },
1627
2006
  {
1628
2007
  code: "RV3009",
1629
2008
  messages: ["Server-only code cannot render JSX in a resumed route module: return data and render it in the page"],
1630
- fix: "Return plain data from serverLoad/onGet and render the JSX in the page component.",
1631
- avoid: "export const serverLoad = () => ({ x: <p /> });\nexport default () => <p />;",
1632
- prefer: 'export const serverLoad = () => ({ x: "text" });\nexport default () => <p />;',
2009
+ fix: "Return plain data from load(\u2026) / onGet(\u2026) exports and render the JSX in the page component.",
2010
+ avoid: 'import { load } from "@rivetjs/router";\nexport const loader = load(() => ({ x: <p /> }));\nexport default () => <p />;',
2011
+ prefer: 'import { load } from "@rivetjs/router";\nexport const loader = load(() => ({ x: "text" }));\nexport default () => <p />;',
1633
2012
  note: "serverExports + resume."
1634
2013
  },
2014
+ {
2015
+ code: "RV3010",
2016
+ messages: [
2017
+ 'Untagged server export `{name}`: route server code is a wrapper call \u2014 `export const {export} = {wrapper}({argument})` with `import { {wrapper} } from "@rivetjs/router"`'
2018
+ ],
2019
+ fix: "Wrap the export in its @rivetjs/router wrapper call: serverLoad \u2192 export const loader = load(\u2026), serverGuard \u2192 access = guard(\u2026), serverActions \u2192 forms = actions({ \u2026 }), serverEntries \u2192 paths = entries(\u2026), serverHeaders \u2192 responseHeaders = headers(\u2026), onRequest \u2192 middleware = onRequest(\u2026), onGet \u2192 GET = onGet(\u2026) (same for onPost \u2026 onOptions). The export name is free; `rivet migrate server-exports` rewrites a whole app.",
2020
+ avoid: "export const serverLoad = () => ({ n: 1 });\nexport default () => <p />;",
2021
+ prefer: 'import { load } from "@rivetjs/router";\nexport const loader = load(() => ({ n: 1 }));\nexport default () => <p />;',
2022
+ quickFix: "rivetWrapServerExport: Wrap in `load(\u2026)` (renames the export and its in-file references, adds the import).",
2023
+ note: "serverExports option (route modules) and the language service (files under the manifest routesDir). Sibling *.server.ts names (load, GET \u2026) without wrappers fail at the first request instead (server/module.ts)."
2024
+ },
2025
+ {
2026
+ code: "RV3011",
2027
+ messages: [
2028
+ "{chain} (server-only), rendered by {routes} (client-navigated page route[s])",
2029
+ "{chain} (server-only), rendered by the layout of every page under {path} (client-rendered on navigation and resume)"
2030
+ ],
2031
+ fix: 'Pass the data to the component as props (inlined at build time, or read in the page\'s `load(\u2026)`), keep server-only modules out of components, or make the page a document route with `import "@rivetjs/router/server-only"` in the page module. Layouts and error pages cannot be document routes: read the data in their `load(\u2026)`. `rivetRouter({ serverOnly: { reach: "warn" } })` turns the page case into a warning (those pages render as document routes).',
2032
+ avoid: '// src/components/Card.tsx\nimport { rows } from "../lib/rows.server.ts";\nexport const Card = () => <p>{rows.length}</p>;',
2033
+ prefer: "// src/components/Card.tsx\nexport const Card = (props: { count: number }) => <p>{props.count}</p>;\n// src/routes/index.tsx: read rows.server.ts in load(\u2026), render <Card count={\u2026} />",
2034
+ note: "Router build (`@rivetjs/router/vite`, configResolved graph scan); TS-side text (not Rust). Server-only = `@rivetjs/router/server-only` marker, `*.server.*` files, `rivetRouter({ serverOnly })` / `rivet({ serverOnly })` patterns, plugin-declared ids (`api.rivetServerOnly`). Dev: overlay + terminal on page load. Pages that reach one only through their own module / non-component helpers become document routes instead (no diagnostic)."
2035
+ },
1635
2036
  {
1636
2037
  code: "RV3101",
1637
2038
  messages: ['No route matches "{path}" (routes from {routesDir ?? "the router"}).'],
1638
2039
  fix: "Point the Link at an existing route path (check src/routes), or add the missing route file.",
1639
- avoid: 'import { Link } from "@rivetjs/router";\nexport const A = () => <Link to="/abuot">About</Link>;',
1640
- prefer: 'import { Link } from "@rivetjs/router";\nexport const A = () => <Link to="/about">About</Link>;',
2040
+ avoid: 'import { Link } from "@rivetjs/router/client";\nexport const A = () => <Link to="/abuot">About</Link>;',
2041
+ prefer: 'import { Link } from "@rivetjs/router/client";\nexport const A = () => <Link to="/about">About</Link>;',
1641
2042
  note: "Language service only; warning; TS-side text (not Rust). Could suggest nearest route."
1642
2043
  },
1643
2044
  {
@@ -1792,9 +2193,11 @@ var explain_default = {
1792
2193
  "Invalid compiler option `styles.layer`: expected a string, null or false",
1793
2194
  "Invalid compiler option `resume.module`: expected a string",
1794
2195
  "Invalid compiler option `resume`: expected `{ module?: string }`, true or false",
2196
+ 'Invalid compiler option `resume.model`: expected "serialized" or "derived"',
2197
+ "Invalid compiler option `resume.{key}`: expected an array of component names",
1795
2198
  'Invalid compiler option `targets`: unknown target "{bad}"'
1796
2199
  ],
1797
- fix: 'Pass the option in the documented shape (e.g. resume: { module: "src/a.tsx" } or true; styles: { scope: string }).',
2200
+ fix: 'Pass the option in the documented shape (e.g. resume: { module: "src/a.tsx" } or true, resume.model: "serialized" or "derived", resume.boundaries / resume.independent: arrays of component names; styles: { scope: string }).',
1798
2201
  avoid: "check(src, { resume: 5 })",
1799
2202
  prefer: 'check(src, { resume: { module: "src/a.tsx" } })',
1800
2203
  note: "Integration error; no source range."
@@ -1873,7 +2276,7 @@ function createLanguageCore({ compiler, manifests = new ManifestCache() }) {
1873
2276
  },
1874
2277
  project: (fileName) => manifests.find(fileName),
1875
2278
  routes: (fileName) => manifests.find(fileName)?.manifest.routes ?? [],
1876
- routeFile: (project, route) => (0, import_node_path5.resolve)(project.root, route.file)
2279
+ routeFile: (project, route) => (0, import_node_path6.resolve)(project.root, route.file)
1877
2280
  };
1878
2281
  }
1879
2282
  function analyzeFile(ts, core, program, file, options = {}) {
@@ -1893,6 +2296,7 @@ function analyzeFile(ts, core, program, file, options = {}) {
1893
2296
  diagnostics = [...diagnostics, ...serializabilityDiagnostics(ts, checker, file, result.sites, verdict)];
1894
2297
  }
1895
2298
  const manifest = result.project?.manifest;
2299
+ if (manifest && isRouteModule(result.project.root, manifest.routesDir, file.fileName)) diagnostics = [...diagnostics, ...untaggedServerExports(ts, file)];
1896
2300
  if (manifest?.routes)
1897
2301
  diagnostics = [
1898
2302
  ...diagnostics,
@@ -1912,7 +2316,7 @@ function analyzeFile(ts, core, program, file, options = {}) {
1912
2316
  // libs/typescript-plugin/index.ts
1913
2317
  var CODE = (code) => 7e4 + Number(code.slice(2));
1914
2318
  var FIX_NUMBERS = new Set(FIXABLE_CODES.map(CODE));
1915
- var bundledWasm = () => typeof __dirname === "string" ? (0, import_node_path6.join)(__dirname, "rivet_compiler.wasm") : null;
2319
+ var bundledWasm = () => typeof __dirname === "string" ? (0, import_node_path7.join)(__dirname, "rivet_compiler.wasm") : null;
1916
2320
  var cores = /* @__PURE__ */ new Map();
1917
2321
  function loadCore(wasm, log) {
1918
2322
  let mtime;
@@ -2180,20 +2584,20 @@ function init({ typescript: ts }) {
2180
2584
  * the `.rivet/` of each app (Vite root) the project's Rivet files belong to, and the tsconfig's own. */
2181
2585
  getExternalFiles(project) {
2182
2586
  if (project.getCompilerOptions().jsxImportSource !== "@rivetjs/runtime" || project.projectKind !== ts.server.ProjectKind.Configured) return [];
2183
- const roots = /* @__PURE__ */ new Set([(0, import_node_path6.dirname)(project.getProjectName())]);
2587
+ const roots = /* @__PURE__ */ new Set([(0, import_node_path7.dirname)(project.getProjectName())]);
2184
2588
  const byDir = /* @__PURE__ */ new Map();
2185
2589
  for (const name of project.getRootFiles() ?? []) {
2186
2590
  if (!/\.[jt]sx$/.test(name) || /[/\\]node_modules[/\\]/.test(name)) continue;
2187
- const dir = (0, import_node_path6.dirname)(name);
2591
+ const dir = (0, import_node_path7.dirname)(name);
2188
2592
  let root = byDir.get(dir);
2189
2593
  if (root === void 0) byDir.set(dir, root = viteRoot(name));
2190
2594
  if (root) roots.add(root);
2191
2595
  }
2192
2596
  return [...roots].flatMap((root) => {
2193
- const dir = (0, import_node_path6.join)(root, ".rivet");
2597
+ const dir = (0, import_node_path7.join)(root, ".rivet");
2194
2598
  if (!(0, import_node_fs5.existsSync)(dir)) return [];
2195
2599
  try {
2196
- return (0, import_node_fs5.readdirSync)(dir).filter((name) => name.endsWith(".d.ts")).map((name) => (0, import_node_path6.join)(dir, name));
2600
+ return (0, import_node_fs5.readdirSync)(dir).filter((name) => name.endsWith(".d.ts")).map((name) => (0, import_node_path7.join)(dir, name));
2197
2601
  } catch {
2198
2602
  return [];
2199
2603
  }
Binary file
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@rivetjs/typescript-plugin",
3
- "version": "0.1.0-alpha.0",
3
+ "version": "0.1.0-alpha.1",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "TypeScript language service plugin: Rivet compiler diagnostics, quick fixes, resume hints and route links in TS editors.",
7
7
  "main": "./dist/index.cjs",
8
8
  "dependencies": {
9
- "@rivetjs/language-core": "0.1.0-alpha.0"
9
+ "@rivetjs/language-core": "0.1.0-alpha.1"
10
10
  },
11
11
  "peerDependencies": {
12
12
  "typescript": ">=5.6"