@spfn/core 0.3.0-beta.3 → 0.3.0-beta.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/README.md +304 -12
  2. package/dist/app-config/index.d.ts +39 -1
  3. package/dist/app-config/index.js.map +1 -1
  4. package/dist/authz/index.js +47 -391
  5. package/dist/authz/index.js.map +1 -1
  6. package/dist/{boss-D16fO2oG.d.ts → boss-BiDCbbAQ.d.ts} +60 -7
  7. package/dist/codegen/index.d.ts +157 -16
  8. package/dist/codegen/index.js +772 -166
  9. package/dist/codegen/index.js.map +1 -1
  10. package/dist/config/index.js +4 -4
  11. package/dist/config/index.js.map +1 -1
  12. package/dist/contract/index.d.ts +39 -16
  13. package/dist/contract/index.js +147 -23
  14. package/dist/contract/index.js.map +1 -1
  15. package/dist/db/index.d.ts +230 -35
  16. package/dist/db/index.js +316 -160
  17. package/dist/db/index.js.map +1 -1
  18. package/dist/env/index.d.ts +6 -442
  19. package/dist/env/index.js +13 -16
  20. package/dist/env/index.js.map +1 -1
  21. package/dist/env/loader.js +24 -1
  22. package/dist/env/loader.js.map +1 -1
  23. package/dist/error-CIZlYJlL.d.ts +157 -0
  24. package/dist/errors/index.d.ts +18 -1
  25. package/dist/errors/index.js +47 -391
  26. package/dist/errors/index.js.map +1 -1
  27. package/dist/event/index.d.ts +5 -5
  28. package/dist/event/index.js +202 -3
  29. package/dist/event/index.js.map +1 -1
  30. package/dist/event/sse/client.d.ts +18 -4
  31. package/dist/event/sse/client.js +94 -18
  32. package/dist/event/sse/client.js.map +1 -1
  33. package/dist/event/sse/index.d.ts +18 -5
  34. package/dist/event/sse/index.js +452 -114
  35. package/dist/event/sse/index.js.map +1 -1
  36. package/dist/event/ws/client.d.ts +3 -2
  37. package/dist/event/ws/index.d.ts +4 -3
  38. package/dist/job/index.d.ts +48 -3
  39. package/dist/job/index.js +131 -52
  40. package/dist/job/index.js.map +1 -1
  41. package/dist/logger/index.js +0 -12
  42. package/dist/logger/index.js.map +1 -1
  43. package/dist/middleware/index.js +52 -397
  44. package/dist/middleware/index.js.map +1 -1
  45. package/dist/nextjs/index.d.ts +75 -14
  46. package/dist/nextjs/index.js +200 -84
  47. package/dist/nextjs/index.js.map +1 -1
  48. package/dist/nextjs/server.d.ts +46 -8
  49. package/dist/nextjs/server.js +15 -1
  50. package/dist/nextjs/server.js.map +1 -1
  51. package/dist/ops/index.d.ts +61 -42
  52. package/dist/ops/index.js +648 -97
  53. package/dist/ops/index.js.map +1 -1
  54. package/dist/registry-C2KQCh0m.d.ts +473 -0
  55. package/dist/route/index.d.ts +5 -2
  56. package/dist/route/index.js +107 -28
  57. package/dist/route/index.js.map +1 -1
  58. package/dist/route/types.d.ts +7 -1
  59. package/dist/{router-Cy7rAfmj.d.ts → route-builder-DRW-MSCJ.d.ts} +51 -126
  60. package/dist/router-C57y-vz1.d.ts +184 -0
  61. package/dist/router-CX5uVjmU.d.ts +406 -0
  62. package/dist/server/index.d.ts +130 -7
  63. package/dist/server/index.js +1174 -164
  64. package/dist/server/index.js.map +1 -1
  65. package/dist/state/client.d.ts +206 -0
  66. package/dist/state/client.js +678 -0
  67. package/dist/state/client.js.map +1 -0
  68. package/dist/state/index.d.ts +443 -0
  69. package/dist/state/index.js +644 -0
  70. package/dist/state/index.js.map +1 -0
  71. package/dist/state/react.d.ts +49 -0
  72. package/dist/state/react.js +50 -0
  73. package/dist/state/react.js.map +1 -0
  74. package/dist/surface/index.d.ts +115 -0
  75. package/dist/surface/index.js +1013 -0
  76. package/dist/surface/index.js.map +1 -0
  77. package/dist/token-manager-Ckjt2-qq.d.ts +122 -0
  78. package/dist/{types-CfYVhIQ9.d.ts → types-5Rl11_Nb.d.ts} +43 -4
  79. package/dist/types-CKH0D3Dy.d.ts +441 -0
  80. package/dist/{types-ZQODsBft.d.ts → types-CurMab2Q.d.ts} +52 -2
  81. package/dist/{types-2AbaW4Ie.d.ts → types-DncqXfWN.d.ts} +4 -3
  82. package/docs/file-upload.md +195 -333
  83. package/package.json +41 -11
  84. package/src/cache/README.md +330 -0
  85. package/src/codegen/README.md +721 -0
  86. package/src/config/README.md +333 -0
  87. package/src/contract/README.md +442 -0
  88. package/src/db/README.md +589 -0
  89. package/src/db/manager/README.md +500 -0
  90. package/src/db/schema/README.md +354 -0
  91. package/src/db/transaction/README.md +845 -0
  92. package/src/env/README.md +787 -0
  93. package/src/errors/README.md +445 -0
  94. package/src/event/README.md +994 -0
  95. package/src/job/README.md +613 -0
  96. package/src/logger/README.md +321 -0
  97. package/src/middleware/README.md +634 -0
  98. package/src/nextjs/README.md +756 -0
  99. package/src/route/README.md +763 -0
  100. package/src/security/README.md +100 -0
  101. package/src/server/README.md +704 -0
  102. package/src/state/README.md +806 -0
  103. package/dist/token-manager-BT5EnUAR.d.ts +0 -278
  104. package/dist/types-9oszaJqp.d.ts +0 -250
@@ -293,6 +293,24 @@ interface GenerationStats {
293
293
  * Generates a route map file containing routeName → {method, path} mappings.
294
294
  * This allows RPC proxy to resolve routes without importing the full router.
295
295
  *
296
+ * The router is loaded and walked, not parsed from source. A source parser sees
297
+ * the spelling rather than the router: `defineRouter({ createUser })` written on
298
+ * one line, an aliased key `create: createUser`, and a nested router assembled by
299
+ * a second `defineRouter(` in the same file are all ordinary ways to write a
300
+ * router, and all of them used to leave routes out of the map. The RPC client
301
+ * addresses a route by name, so a dropped name is a route that typechecks and
302
+ * 404s. Walking the loaded router registers exactly what `registerRoutes`
303
+ * registers, whatever the source looks like.
304
+ *
305
+ * The map carries the routes of every package router the app mounts with
306
+ * `.packages()`, not only the app's own. A package ships a client that addresses
307
+ * its routes by name — `authApi.login`, `monitorApi.getStats` — and the app's
308
+ * `createRpcProxy` resolves a name in the single map it was constructed with, so
309
+ * a package route in nobody's map is a call that resolves nowhere. The app
310
+ * therefore merges nothing by hand: `{ ...routeMap, ...authRouteMap }` is a
311
+ * no-op today and `{ ...routeMap }` is the whole of it. (`eventRouteMap` is the
312
+ * exception that stays — it is a hand-written constant, not a mounted router.)
313
+ *
296
314
  * @example
297
315
  * ```typescript
298
316
  * // .spfnrc.ts
@@ -326,10 +344,19 @@ interface RouteMapGeneratorConfig {
326
344
  */
327
345
  outputPath?: string;
328
346
  /**
329
- * Additional route directories to scan (for package routers)
347
+ * Extra file patterns to watch, for routes outside src/server/routes
348
+ *
349
+ * @deprecated Ignored. The map is read from the loaded router, which reaches
350
+ * every route through its own imports wherever they live, so there is no
351
+ * longer a set of directories to scan. Still accepted so an existing
352
+ * `.spfnrc.ts` keeps working; the watch patterns it produced are unchanged.
330
353
  */
331
354
  additionalRouteDirs?: string[];
332
355
  }
356
+ /** Thrown when the router cannot produce a route map at all. */
357
+ declare class RouteMapGeneratorError extends Error {
358
+ constructor(message: string);
359
+ }
333
360
 
334
361
  /**
335
362
  * Contract Generator
@@ -352,6 +379,7 @@ interface RouteMapGeneratorConfig {
352
379
  * defineGenerator({
353
380
  * name: '@spfn/core:contract',
354
381
  * routerPath: './src/server/router.ts',
382
+ * eventRouterPath: './src/server/events.ts', // optional: contracted SSE events
355
383
  * outputDir: './contracts',
356
384
  * })
357
385
  * ]
@@ -374,6 +402,18 @@ interface ContractGeneratorConfig {
374
402
  * @default 'appRouter', falling back to the default export
375
403
  */
376
404
  routerExport?: string;
405
+ /**
406
+ * Path to the file exporting the `defineEventRouter()` result (relative to
407
+ * project root). When set, its contracted events are written into the
408
+ * document's `events` section; unset, the document has none.
409
+ * @example './src/server/events.ts'
410
+ */
411
+ eventRouterPath?: string;
412
+ /**
413
+ * Named export holding the event router.
414
+ * @default 'eventRouter', falling back to the default export
415
+ */
416
+ eventRouterExport?: string;
377
417
  /**
378
418
  * Directory holding current.json, released/ and usage/ (relative to project root)
379
419
  * @default './contracts'
@@ -389,30 +429,126 @@ declare class ContractGeneratorError extends Error {
389
429
  constructor(message: string);
390
430
  }
391
431
 
432
+ /**
433
+ * States Generator
434
+ *
435
+ * Generates a file naming the states the app declared — their ids and their
436
+ * manifest — as values a browser root or a server component can execute.
437
+ *
438
+ * `defineStateRouter` declares the states, and the browser reads them through
439
+ * `createStateClient<typeof states>`: a type import, erased at build. So nothing
440
+ * that runs in the browser says which states the app declared, and importing
441
+ * the router to find out is not an option — each state's `read()` closes over
442
+ * repositories and the database. A root that compares the declared ids with the
443
+ * `manifest` of `GET /state` can report a deploy skew between the Next.js server
444
+ * and the API before rendering, instead of a screen meeting `state.<id>` as
445
+ * `undefined`.
446
+ *
447
+ * The router is loaded and walked, as the route map's is. A state router is told
448
+ * apart by the `_stateManifest` marker `defineStateRouter` sets on the router it
449
+ * derives — the object the snapshot route answers as `manifest` — so the ids
450
+ * written here and the manifest the server serves come from one value.
451
+ *
452
+ * @example
453
+ * ```typescript
454
+ * // .spfnrc.ts
455
+ * import { defineConfig, defineGenerator } from '@spfn/core/codegen';
456
+ *
457
+ * export default defineConfig({
458
+ * generators: [
459
+ * defineGenerator({
460
+ * name: '@spfn/core:states',
461
+ * routerPath: './src/server/router.ts',
462
+ * outputPath: './src/generated/states.ts',
463
+ * })
464
+ * ]
465
+ * });
466
+ * ```
467
+ */
468
+
469
+ interface StatesGeneratorConfig {
470
+ /**
471
+ * Generator name (required for package-based loading)
472
+ */
473
+ name: '@spfn/core:states';
474
+ /**
475
+ * Path to the router file (relative to project root) — the one the route map reads
476
+ * @example './src/server/router.ts'
477
+ */
478
+ routerPath: string;
479
+ /**
480
+ * Output path for the generated states file (relative to project root)
481
+ * @default './src/generated/states.ts'
482
+ */
483
+ outputPath?: string;
484
+ }
485
+ /** Thrown when the router cannot produce the app's declared states. */
486
+ declare class StatesGeneratorError extends Error {
487
+ constructor(message: string);
488
+ }
489
+
392
490
  /**
393
491
  * Router registration guard
394
492
  *
395
- * A contract has to describe what production serves. When a route is registered
396
- * only under a feature flag or an environment check, the generated contract
397
- * describes whichever way the generator happened to run — and the gate then
398
- * compares a promise nobody made.
493
+ * A route registered only behind a feature flag or an environment check makes
494
+ * generated output depend on how the generator happened to run. The contract
495
+ * then describes a promise nobody made, and the route map leaves out a name
496
+ * `registerRoutes` registers in production — so `api.admin.call()` typechecks
497
+ * and 404s wherever the flag differs.
399
498
  *
400
- * Object spread is the one way a key can be conditionally present in the object
401
- * `defineRouter()` receives, so that is what this reads. A spread of a plain
402
- * identifier (`...baseRoutes`) is unconditional and passes; anything computed
403
- * inside the spread does not.
499
+ * What it reads is the router the generator was pointed at: the export the
500
+ * loader found the router under, and the routers that export mounts by name
501
+ * from the same file, since a nested router's routes land in the same flat map.
502
+ * A `defineRouter(` the file writes but the app router never reaches — a second
503
+ * router a test harness mounts, say — registers nothing in production and is
504
+ * not scanned.
505
+ *
506
+ * Reading is done with TypeScript's parser rather than a regular expression
507
+ * over the text. A spread is a syntax node there, so a JSDoc that spells
508
+ * `defineRouter({ ... })` in prose, a brace inside a route path, and a comment
509
+ * holding an old router are not router source. Only a spread whose expression
510
+ * reads a condition is refused — a ternary, `&&`, `||`, `??`. A call is
511
+ * allowed: `...metadataRoutes(config, resource)` is how `@spfn/mcp` composes
512
+ * its router, and loading yields exactly the routes it returns.
513
+ *
514
+ * Past it, by construction: a conditional spread inside an imported route
515
+ * module, a router built by a factory, a condition hoisted to a variable
516
+ * (`const extra = flag ? { admin } : {}` spread afterwards), and a
517
+ * `.packages()` list assembled conditionally. `NODE_ENV` is pinned before the
518
+ * router loads, which covers those only when the shell left it unset: the
519
+ * generator then reads the router production gets. `spfn build` pins
520
+ * `production` itself, so the deployed map is right. `spfn dev` sets
521
+ * `development` before it spawns the watcher, which inherits it — a
522
+ * `NODE_ENV === 'development'` condition hoisted to a variable therefore writes
523
+ * a dev-only route into the map the watcher regenerates, and `spfn build` takes
524
+ * it back out. The generated map's header names the `NODE_ENV` it was written
525
+ * under, so that difference shows up in the diff.
404
526
  */
405
- /** Thrown when the router registers contracted routes conditionally. */
527
+ /** Thrown when the router registers routes conditionally. */
406
528
  declare class ConditionalRegistrationError extends Error {
407
529
  constructor(message: string);
408
530
  }
531
+ interface RegistrationScan {
532
+ /** Path named in the error message. */
533
+ routerPath: string;
534
+ /** Contents of the router file. */
535
+ source: string;
536
+ /** The export the loader found the router under: `appRouter`, `default`, … */
537
+ exportName: string;
538
+ /** What is being generated, named in the message: `route map`, `contract`. */
539
+ subject: string;
540
+ /**
541
+ * The call the router is built with. `defineEventRouter` scans an event
542
+ * router file, whose event set the contract publishes the same way.
543
+ * @default 'defineRouter'
544
+ */
545
+ factory?: RouterFactory;
546
+ }
547
+ type RouterFactory = 'defineRouter' | 'defineEventRouter';
409
548
  /**
410
549
  * Refuse a router whose route set depends on a condition.
411
- *
412
- * @param routerPath - path named in the error message
413
- * @param source - contents of the router file
414
550
  */
415
- declare function assertUnconditionalRegistration(routerPath: string, source: string): void;
551
+ declare function assertUnconditionalRegistration(scan: RegistrationScan): void;
416
552
 
417
553
  /**
418
554
  * Built-in Generators Export
@@ -421,7 +557,7 @@ declare function assertUnconditionalRegistration(routerPath: string, source: str
421
557
  * ```typescript
422
558
  * // .spfnrc.ts
423
559
  * import { defineConfig, defineGenerator } from '@spfn/core/codegen';
424
- * import type { RouteMapGeneratorConfig, ContractGeneratorConfig } from '@spfn/core/codegen';
560
+ * import type { RouteMapGeneratorConfig, ContractGeneratorConfig, StatesGeneratorConfig } from '@spfn/core/codegen';
425
561
  *
426
562
  * export default defineConfig({
427
563
  * generators: [
@@ -435,6 +571,11 @@ declare function assertUnconditionalRegistration(routerPath: string, source: str
435
571
  * routerPath: './src/server/router.ts',
436
572
  * outputDir: './contracts',
437
573
  * }),
574
+ * defineGenerator<StatesGeneratorConfig>({
575
+ * name: '@spfn/core:states',
576
+ * routerPath: './src/server/router.ts',
577
+ * outputPath: './src/generated/states.ts',
578
+ * }),
438
579
  * ]
439
580
  * });
440
581
  * ```
@@ -447,4 +588,4 @@ declare function assertUnconditionalRegistration(routerPath: string, source: str
447
588
  */
448
589
  declare const generators: Record<string, unknown>;
449
590
 
450
- export { type ClientGenerationOptions, type CodegenConfig, CodegenOrchestrator, ConditionalRegistrationError, type ContractGeneratorConfig, ContractGeneratorError, type GenerationStats, type Generator, type GeneratorConfig, type GeneratorOptions, type GeneratorTrigger, type OrchestratorOptions, type ResourceRoutes, type RouteContractMapping, type RouteMapGeneratorConfig, assertUnconditionalRegistration, createGeneratorsFromConfig, defineConfig, defineGenerator, generators, loadCodegenConfig };
591
+ export { type ClientGenerationOptions, type CodegenConfig, CodegenOrchestrator, ConditionalRegistrationError, type ContractGeneratorConfig, ContractGeneratorError, type GenerationStats, type Generator, type GeneratorConfig, type GeneratorOptions, type GeneratorTrigger, type OrchestratorOptions, type RegistrationScan, type ResourceRoutes, type RouteContractMapping, type RouteMapGeneratorConfig, RouteMapGeneratorError, type StatesGeneratorConfig, StatesGeneratorError, assertUnconditionalRegistration, createGeneratorsFromConfig, defineConfig, defineGenerator, generators, loadCodegenConfig };