@spfn/core 0.2.0-beta.8 → 0.3.0-beta.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.
Files changed (95) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +444 -305
  3. package/dist/authz/index.d.ts +34 -0
  4. package/dist/authz/index.js +810 -0
  5. package/dist/authz/index.js.map +1 -0
  6. package/dist/{boss-DI1r4kTS.d.ts → boss-D16fO2oG.d.ts} +41 -1
  7. package/dist/cache/index.js +42 -30
  8. package/dist/cache/index.js.map +1 -1
  9. package/dist/codegen/index.d.ts +121 -13
  10. package/dist/codegen/index.js +212 -15
  11. package/dist/codegen/index.js.map +1 -1
  12. package/dist/config/index.d.ts +615 -6
  13. package/dist/config/index.js +124 -5
  14. package/dist/config/index.js.map +1 -1
  15. package/dist/contract/index.d.ts +220 -0
  16. package/dist/contract/index.js +558 -0
  17. package/dist/contract/index.js.map +1 -0
  18. package/dist/db/index.d.ts +528 -85
  19. package/dist/db/index.js +831 -122
  20. package/dist/db/index.js.map +1 -1
  21. package/dist/define-middleware-DfDP39Nq.d.ts +167 -0
  22. package/dist/env/index.d.ts +26 -2
  23. package/dist/env/index.js +15 -5
  24. package/dist/env/index.js.map +1 -1
  25. package/dist/env/loader.d.ts +26 -19
  26. package/dist/env/loader.js +32 -25
  27. package/dist/env/loader.js.map +1 -1
  28. package/dist/errors/index.d.ts +10 -0
  29. package/dist/errors/index.js +418 -5
  30. package/dist/errors/index.js.map +1 -1
  31. package/dist/event/index.d.ts +33 -3
  32. package/dist/event/index.js +24 -3
  33. package/dist/event/index.js.map +1 -1
  34. package/dist/event/sse/client.d.ts +42 -3
  35. package/dist/event/sse/client.js +128 -45
  36. package/dist/event/sse/client.js.map +1 -1
  37. package/dist/event/sse/index.d.ts +12 -5
  38. package/dist/event/sse/index.js +280 -32
  39. package/dist/event/sse/index.js.map +1 -1
  40. package/dist/event/ws/client.d.ts +59 -0
  41. package/dist/event/ws/client.js +273 -0
  42. package/dist/event/ws/client.js.map +1 -0
  43. package/dist/event/ws/index.d.ts +94 -0
  44. package/dist/event/ws/index.js +272 -0
  45. package/dist/event/ws/index.js.map +1 -0
  46. package/dist/job/index.d.ts +2 -2
  47. package/dist/job/index.js +155 -42
  48. package/dist/job/index.js.map +1 -1
  49. package/dist/logger/index.d.ts +5 -0
  50. package/dist/logger/index.js +14 -0
  51. package/dist/logger/index.js.map +1 -1
  52. package/dist/middleware/index.d.ts +347 -9
  53. package/dist/middleware/index.js +1462 -15
  54. package/dist/middleware/index.js.map +1 -1
  55. package/dist/nextjs/index.d.ts +2 -2
  56. package/dist/nextjs/index.js +42 -28
  57. package/dist/nextjs/index.js.map +1 -1
  58. package/dist/nextjs/server.d.ts +35 -51
  59. package/dist/nextjs/server.js +126 -60
  60. package/dist/nextjs/server.js.map +1 -1
  61. package/dist/ops/index.d.ts +107 -0
  62. package/dist/ops/index.js +476 -0
  63. package/dist/ops/index.js.map +1 -0
  64. package/dist/route/index.d.ts +8 -694
  65. package/dist/route/index.js +111 -22
  66. package/dist/route/index.js.map +1 -1
  67. package/dist/router-ukNdAZcN.d.ts +676 -0
  68. package/dist/security/index.d.ts +83 -0
  69. package/dist/security/index.js +173 -0
  70. package/dist/security/index.js.map +1 -0
  71. package/dist/server/index.d.ts +491 -22
  72. package/dist/server/index.js +1887 -308
  73. package/dist/server/index.js.map +1 -1
  74. package/dist/token-manager-BT5EnUAR.d.ts +278 -0
  75. package/dist/types-2AbaW4Ie.d.ts +205 -0
  76. package/dist/{types-BOPTApC2.d.ts → types-9oszaJqp.d.ts} +7 -2
  77. package/dist/types-Bvvig_tT.d.ts +115 -0
  78. package/dist/types-ZQODsBft.d.ts +282 -0
  79. package/package.json +244 -208
  80. package/dist/router-Di7ENoah.d.ts +0 -151
  81. package/dist/types-B-e_f2dQ.d.ts +0 -121
  82. package/docs/cache.md +0 -133
  83. package/docs/codegen.md +0 -74
  84. package/docs/database.md +0 -346
  85. package/docs/entity.md +0 -539
  86. package/docs/env.md +0 -477
  87. package/docs/errors.md +0 -319
  88. package/docs/event.md +0 -116
  89. package/docs/job.md +0 -131
  90. package/docs/logger.md +0 -108
  91. package/docs/middleware.md +0 -337
  92. package/docs/nextjs.md +0 -241
  93. package/docs/repository.md +0 -496
  94. package/docs/route.md +0 -497
  95. package/docs/server.md +0 -307
@@ -11,6 +11,7 @@ var RouteBuilder = class _RouteBuilder {
11
11
  _interceptor;
12
12
  _middlewares;
13
13
  _skipMiddlewares;
14
+ _contract;
14
15
  /**
15
16
  * Create a new RouteBuilder with copied properties and optional overrides
16
17
  */
@@ -22,6 +23,7 @@ var RouteBuilder = class _RouteBuilder {
22
23
  builder._interceptor = overrides?.interceptor ?? this._interceptor;
23
24
  builder._middlewares = overrides?.middlewares ?? this._middlewares;
24
25
  builder._skipMiddlewares = overrides?.skipMiddlewares ?? this._skipMiddlewares;
26
+ builder._contract = overrides?.contract ?? this._contract;
25
27
  return builder;
26
28
  }
27
29
  /**
@@ -159,6 +161,39 @@ var RouteBuilder = class _RouteBuilder {
159
161
  skip(middlewareNames) {
160
162
  return this.clone({ skipMiddlewares: middlewareNames });
161
163
  }
164
+ /**
165
+ * Publish this route as a versioned contract operation
166
+ *
167
+ * Marks the route as a promise to clients that are compiled and deployed
168
+ * separately from the server — a mobile app, an external API consumer.
169
+ * The `@spfn/core:contract` generator writes every contracted route into
170
+ * `contracts/current.json`, and the build refuses a change that would break
171
+ * an already-released client.
172
+ *
173
+ * Routes without `.contract()` are unaffected: they simply do not appear in
174
+ * the contract. A web client needs nothing here — it derives its types from
175
+ * the router in the same build.
176
+ *
177
+ * @example
178
+ * ```ts
179
+ * export const getUser = route.get('/users/:id')
180
+ * .input({ params: Type.Object({ id: Type.String() }) })
181
+ * .contract({
182
+ * since: '1.2.0',
183
+ * auth: 'clientProofV1',
184
+ * requiresSession: true,
185
+ * response: Type.Object({
186
+ * id: Type.String(),
187
+ * name: Type.String(),
188
+ * email: Type.Optional(Type.String()),
189
+ * }),
190
+ * })
191
+ * .handler(async (c) => { ... });
192
+ * ```
193
+ */
194
+ contract(contract) {
195
+ return this.clone({ contract });
196
+ }
162
197
  /**
163
198
  * Define handler function
164
199
  *
@@ -213,6 +248,7 @@ var RouteBuilder = class _RouteBuilder {
213
248
  interceptor: this._interceptor,
214
249
  middlewares: this._middlewares,
215
250
  skipMiddlewares: this._skipMiddlewares,
251
+ contract: this._contract,
216
252
  handler: fn,
217
253
  _input: {},
218
254
  _interceptor: {},
@@ -237,12 +273,13 @@ var route = {
237
273
  };
238
274
 
239
275
  // src/route/router.ts
240
- function createRouterInstance(routes, packageRouters = [], globalMiddlewares = []) {
276
+ function createRouterInstance(routes, packageRouters = [], globalMiddlewares = [], contractVersion = null) {
241
277
  return {
242
278
  routes,
243
279
  _routes: routes,
244
280
  _packageRouters: packageRouters,
245
281
  _globalMiddlewares: globalMiddlewares,
282
+ _contractVersion: contractVersion,
246
283
  packages(routers) {
247
284
  const newPackageRouters = [...this._packageRouters, ...routers];
248
285
  for (const pkgRouter of routers) {
@@ -250,13 +287,39 @@ function createRouterInstance(routes, packageRouters = [], globalMiddlewares = [
250
287
  newPackageRouters.push(...pkgRouter._packageRouters);
251
288
  }
252
289
  }
253
- return createRouterInstance(this.routes, newPackageRouters, this._globalMiddlewares);
290
+ return createRouterInstance(
291
+ this.routes,
292
+ newPackageRouters,
293
+ this._globalMiddlewares,
294
+ this._contractVersion
295
+ );
254
296
  },
255
297
  use(middlewares) {
256
- return createRouterInstance(this.routes, this._packageRouters, [...this._globalMiddlewares, ...middlewares]);
298
+ return createRouterInstance(
299
+ this.routes,
300
+ this._packageRouters,
301
+ [...this._globalMiddlewares, ...middlewares],
302
+ this._contractVersion
303
+ );
304
+ },
305
+ contractVersion(version) {
306
+ assertContractVersion(version);
307
+ return createRouterInstance(
308
+ this.routes,
309
+ this._packageRouters,
310
+ this._globalMiddlewares,
311
+ version
312
+ );
257
313
  }
258
314
  };
259
315
  }
316
+ function assertContractVersion(version) {
317
+ if (!/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)*$/.test(version)) {
318
+ throw new Error(
319
+ `contractVersion("${version}") is not a version of the form major.minor.patch. The released snapshot is named from this value and releases are compared by it.`
320
+ );
321
+ }
322
+ }
260
323
  function defineRouter(routes) {
261
324
  return createRouterInstance(routes);
262
325
  }
@@ -575,7 +638,7 @@ function registerRoutes(app, router, namedMiddlewares, collectedRoutes) {
575
638
  return routes;
576
639
  }
577
640
  function registerRoute(app, name, routeDef, namedMiddlewares) {
578
- const { method, path, input, middlewares = [], skipMiddlewares, handler } = routeDef;
641
+ const { method, path, input, interceptor, middlewares = [], skipMiddlewares, handler } = routeDef;
579
642
  if (!method || !path) {
580
643
  logger.warn(`Route "${name}" is missing method or path - skipping`, {
581
644
  method,
@@ -584,7 +647,7 @@ function registerRoute(app, name, routeDef, namedMiddlewares) {
584
647
  return null;
585
648
  }
586
649
  const wrappedHandler = async (c) => {
587
- const { context, responseMeta } = await createRouteBuilderContext(c, input || {});
650
+ const { context, responseMeta } = await createRouteBuilderContext(c, input || {}, interceptor);
588
651
  const result = await handler(context);
589
652
  if (result instanceof Response) {
590
653
  return result;
@@ -602,18 +665,30 @@ function registerRoute(app, name, routeDef, namedMiddlewares) {
602
665
  const registeredNames = /* @__PURE__ */ new Set();
603
666
  const registeredHandlers = /* @__PURE__ */ new Set();
604
667
  const skipAll = skipMiddlewares === "*";
668
+ const autoSkips = /* @__PURE__ */ new Set();
669
+ for (const mw of middlewares) {
670
+ if (isNamedMiddleware(mw) && mw.skips) {
671
+ for (const skipName of mw.skips) {
672
+ autoSkips.add(skipName);
673
+ }
674
+ }
675
+ }
605
676
  if (namedMiddlewares && namedMiddlewares.length > 0) {
606
677
  if (skipAll) {
607
678
  logger.debug(`\u23ED\uFE0F Skipping all middlewares (*) for route: ${method} ${path}`, { name });
608
679
  } else {
609
680
  const skipSet = new Set(Array.isArray(skipMiddlewares) ? skipMiddlewares : []);
610
681
  for (const middleware of namedMiddlewares) {
611
- if (!skipSet.has(middleware.name)) {
682
+ if (skipSet.has(middleware.name)) {
683
+ logger.debug(`\u23ED\uFE0F Skipping middleware '${middleware.name}' for route: ${method} ${path}`, { name });
684
+ } else if (autoSkips.has(middleware.name)) {
685
+ logger.debug(`\u23ED\uFE0F Auto-skipping middleware '${middleware.name}' for route: ${method} ${path}`, { name });
686
+ } else if (middleware.name && registeredNames.has(middleware.name)) {
687
+ logger.debug(`\u{1F504} Skipping duplicate middleware '${middleware.name}' for route: ${method} ${path}`, { name });
688
+ } else {
612
689
  allMiddlewares.push(middleware.handler);
613
690
  registeredNames.add(middleware.name);
614
691
  registeredHandlers.add(middleware.handler);
615
- } else {
616
- logger.debug(`\u23ED\uFE0F Skipping middleware '${middleware.name}' for route: ${method} ${path}`, { name });
617
692
  }
618
693
  }
619
694
  }
@@ -636,29 +711,32 @@ function registerRoute(app, name, routeDef, namedMiddlewares) {
636
711
  }
637
712
  }
638
713
  const methodLower = method.toLowerCase();
639
- if (allMiddlewares.length > 0) {
640
- app[methodLower](path, ...allMiddlewares, wrappedHandler);
641
- } else {
642
- app[methodLower](path, wrappedHandler);
643
- }
714
+ const handlers = [...allMiddlewares, wrappedHandler];
715
+ app.on([methodLower], [path], ...handlers);
644
716
  logger.debug(`Registered route: ${method} ${path}`, { name });
645
717
  return { method, path, name };
646
718
  }
647
- async function createRouteBuilderContext(c, input) {
719
+ function methodCarriesBody(method) {
720
+ return method !== "GET" && method !== "HEAD";
721
+ }
722
+ async function createRouteBuilderContext(c, input, interceptor) {
648
723
  const params = validateField(input.params, c.req.param(), "path parameters");
649
724
  const query = validateField(input.query, extractQueryParams(c), "query parameters");
650
725
  const headers = validateField(input.headers, extractHeaders(c), "headers");
651
726
  const cookies = validateField(input.cookies, extractCookies(c), "cookies");
652
727
  let body = {};
653
728
  let formData = {};
654
- if (input.body || input.formData) {
729
+ const injected = methodCarriesBody(c.req.method) ? interceptor : void 0;
730
+ const expectsBody = input.body ?? injected?.body;
731
+ const expectsFormData = input.formData ?? injected?.formData;
732
+ if (expectsBody || expectsFormData) {
655
733
  const contentType = c.req.header("content-type") || "";
656
- if (contentType.includes("multipart/form-data") && input.formData) {
734
+ if (contentType.includes("multipart/form-data") && expectsFormData) {
657
735
  const rawFormData = await parseFormData(c);
658
- formData = validateFormData(input.formData, rawFormData, "form data");
659
- } else if (input.body) {
736
+ formData = input.formData ? validateFormData(input.formData, rawFormData, "form data") : rawFormData;
737
+ } else if (expectsBody) {
660
738
  const rawBody = await parseJsonBody(c);
661
- body = validateField(input.body, rawBody, "request body");
739
+ body = input.body ? validateField(input.body, rawBody, "request body") : rawBody;
662
740
  }
663
741
  }
664
742
  let cachedData = null;
@@ -720,14 +798,16 @@ async function createRouteBuilderContext(c, input) {
720
798
  }
721
799
 
722
800
  // src/route/define-middleware.ts
723
- function defineMiddleware(name, handlerOrFactory) {
801
+ function defineMiddleware(name, handlerOrFactory, options) {
802
+ const skips = options?.skips;
724
803
  if (typeof handlerOrFactory === "function") {
725
804
  const paramCount = handlerOrFactory.length;
726
805
  if (paramCount === 2) {
727
806
  return {
728
807
  name,
729
808
  handler: handlerOrFactory,
730
- _name: name
809
+ _name: name,
810
+ ...skips && { skips }
731
811
  };
732
812
  } else {
733
813
  const factory = handlerOrFactory;
@@ -744,13 +824,22 @@ function defineMiddleware(name, handlerOrFactory) {
744
824
  enumerable: false,
745
825
  configurable: true
746
826
  });
827
+ if (skips) {
828
+ Object.defineProperty(wrapper, "skips", {
829
+ value: skips,
830
+ writable: false,
831
+ enumerable: false,
832
+ configurable: true
833
+ });
834
+ }
747
835
  return wrapper;
748
836
  }
749
837
  }
750
838
  return {
751
839
  name,
752
840
  handler: handlerOrFactory,
753
- _name: name
841
+ _name: name,
842
+ ...skips && { skips }
754
843
  };
755
844
  }
756
845
  function defineMiddlewareFactory(name, factory) {