@spfn/core 0.3.0-beta.2 → 0.3.0-beta.21

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 (94) hide show
  1. package/README.md +343 -16
  2. package/dist/app-config/index.d.ts +134 -0
  3. package/dist/app-config/index.js +58 -0
  4. package/dist/app-config/index.js.map +1 -0
  5. package/dist/authz/index.js +1 -381
  6. package/dist/authz/index.js.map +1 -1
  7. package/dist/{boss-D16fO2oG.d.ts → boss-BiDCbbAQ.d.ts} +60 -7
  8. package/dist/codegen/index.d.ts +93 -15
  9. package/dist/codegen/index.js +626 -165
  10. package/dist/codegen/index.js.map +1 -1
  11. package/dist/config/index.d.ts +0 -63
  12. package/dist/config/index.js +15 -15
  13. package/dist/config/index.js.map +1 -1
  14. package/dist/contract/index.d.ts +40 -17
  15. package/dist/contract/index.js +147 -23
  16. package/dist/contract/index.js.map +1 -1
  17. package/dist/db/index.d.ts +201 -34
  18. package/dist/db/index.js +294 -158
  19. package/dist/db/index.js.map +1 -1
  20. package/dist/{define-middleware-DfDP39Nq.d.ts → define-middleware-CVKgqo8S.d.ts} +1 -1
  21. package/dist/env/index.d.ts +6 -442
  22. package/dist/env/index.js +13 -16
  23. package/dist/env/index.js.map +1 -1
  24. package/dist/env/loader.js +24 -1
  25. package/dist/env/loader.js.map +1 -1
  26. package/dist/error-TsUIjp_x.d.ts +157 -0
  27. package/dist/errors/index.js +1 -381
  28. package/dist/errors/index.js.map +1 -1
  29. package/dist/event/index.d.ts +5 -5
  30. package/dist/event/index.js +32 -1
  31. package/dist/event/index.js.map +1 -1
  32. package/dist/event/sse/client.d.ts +18 -4
  33. package/dist/event/sse/client.js +38 -2
  34. package/dist/event/sse/client.js.map +1 -1
  35. package/dist/event/sse/index.d.ts +5 -4
  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.d.ts +6 -2
  44. package/dist/middleware/index.js +7 -388
  45. package/dist/middleware/index.js.map +1 -1
  46. package/dist/nextjs/index.d.ts +88 -8
  47. package/dist/nextjs/index.js +138 -62
  48. package/dist/nextjs/index.js.map +1 -1
  49. package/dist/nextjs/server.d.ts +45 -7
  50. package/dist/nextjs/server.js +14 -0
  51. package/dist/nextjs/server.js.map +1 -1
  52. package/dist/ops/index.d.ts +62 -43
  53. package/dist/ops/index.js +638 -97
  54. package/dist/ops/index.js.map +1 -1
  55. package/dist/registry-C2KQCh0m.d.ts +473 -0
  56. package/dist/route/index.d.ts +6 -3
  57. package/dist/route/index.js +97 -28
  58. package/dist/route/index.js.map +1 -1
  59. package/dist/route/types.d.ts +7 -1
  60. package/dist/{router-Qbssr11H.d.ts → route-builder-DRW-MSCJ.d.ts} +52 -127
  61. package/dist/router-4YoIy5fN.d.ts +172 -0
  62. package/dist/{token-manager-BT5EnUAR.d.ts → router-CysBmgv3.d.ts} +81 -127
  63. package/dist/server/index.d.ts +185 -14
  64. package/dist/server/index.js +883 -78
  65. package/dist/server/index.js.map +1 -1
  66. package/dist/surface/index.d.ts +115 -0
  67. package/dist/surface/index.js +1003 -0
  68. package/dist/surface/index.js.map +1 -0
  69. package/dist/token-manager-Ckjt2-qq.d.ts +122 -0
  70. package/dist/{types-D1c57Ko-.d.ts → types-5Rl11_Nb.d.ts} +43 -4
  71. package/dist/{types-ZQODsBft.d.ts → types-B8zAxFsE.d.ts} +2 -1
  72. package/dist/types-CxhPH4s_.d.ts +375 -0
  73. package/dist/{types-2AbaW4Ie.d.ts → types-W4u_S0lF.d.ts} +4 -3
  74. package/docs/file-upload.md +195 -333
  75. package/package.json +20 -9
  76. package/src/cache/README.md +330 -0
  77. package/src/codegen/README.md +634 -0
  78. package/src/config/README.md +333 -0
  79. package/src/contract/README.md +440 -0
  80. package/src/db/README.md +589 -0
  81. package/src/db/manager/README.md +500 -0
  82. package/src/db/schema/README.md +354 -0
  83. package/src/db/transaction/README.md +822 -0
  84. package/src/env/README.md +787 -0
  85. package/src/errors/README.md +429 -0
  86. package/src/event/README.md +789 -0
  87. package/src/job/README.md +613 -0
  88. package/src/logger/README.md +321 -0
  89. package/src/middleware/README.md +634 -0
  90. package/src/nextjs/README.md +688 -0
  91. package/src/route/README.md +763 -0
  92. package/src/security/README.md +100 -0
  93. package/src/server/README.md +704 -0
  94. package/dist/types-9oszaJqp.d.ts +0 -250
@@ -0,0 +1,473 @@
1
+ /**
2
+ * Environment Variable Schema Definition System
3
+ *
4
+ * 환경변수에 메타데이터를 정의하여 중앙 관리, 문서화, 검증을 지원합니다.
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * const schema = defineEnvSchema({
9
+ * DATABASE_URL: envUrl({
10
+ * description: 'Database connection',
11
+ * required: true,
12
+ * validator: parsePostgresUrl,
13
+ * sensitive: true,
14
+ * })
15
+ * });
16
+ * ```
17
+ *
18
+ * @module env/schema
19
+ */
20
+ /**
21
+ * 환경변수 스키마 정의
22
+ */
23
+ interface EnvVarSchema<T = string> {
24
+ /** 환경변수 키 */
25
+ key: string;
26
+ /** 설명 (목적, 사용처) */
27
+ description: string;
28
+ /** 타입 */
29
+ type: 'string' | 'number' | 'boolean' | 'url' | 'enum' | 'json';
30
+ /** 필수 여부 */
31
+ required?: boolean;
32
+ /** 기본값 */
33
+ default?: T;
34
+ /** 검증/변환 함수 */
35
+ validator?: (value: string) => T;
36
+ /** Fallback 환경변수 키들 (backward compatibility) */
37
+ fallbackKeys?: string[];
38
+ /** 최소 길이 (문자열 타입) */
39
+ minLength?: number;
40
+ /** 민감정보 여부 (로깅 시 마스킹) */
41
+ sensitive?: boolean;
42
+ /**
43
+ * 자동 생성 방식 — 값이 미지정이면 프로비저닝(secrets 관리 CLI)이 이 방식으로 생성한다.
44
+ * 외부에서 받아야 하는 시크릿(API 키 등)은 generate를 비워 사용자 입력으로 둔다.
45
+ */
46
+ generate?: 'hex32' | 'hex64' | 'uuid' | 'base64url32';
47
+ /** 예시 값들 (타입과 일치해야 함) */
48
+ examples?: T[];
49
+ /**
50
+ * Next.js 프로세스에서 사용 여부
51
+ *
52
+ * - true: .env.local에 존재해야 함 (Next.js 서버 컴포넌트에서 접근 가능)
53
+ * - false: .env.server에만 존재해야 함 (SPFN 서버에서만 접근)
54
+ *
55
+ * @default NEXT_PUBLIC_* 이면 true, 아니면 false
56
+ */
57
+ nextjs?: boolean;
58
+ /**
59
+ * Which layer of a deployment must supply the value.
60
+ *
61
+ * - `'environment'`: one value shared by every instance of an environment
62
+ * (a third-party API key) — kept in `secrets/<env>.enc.json`.
63
+ * - `'instance'`: a value that differs per instance — kept in
64
+ * `secrets/<env>.<instance>.enc.json` or computed by the deploy script
65
+ * and passed to `spfn secret export --with <file>`.
66
+ *
67
+ * Unset: the value may come from any layer. Set it to pin where a value
68
+ * must live — `spfn secret export` then fails when the value arrives from
69
+ * another layer. `--all` on `spfn secret set`/`generate`/`rotate` gives an
70
+ * unset entry to the environment target only.
71
+ */
72
+ layer?: EnvLayer;
73
+ /**
74
+ * Files that read this variable under a computed key, as paths relative to
75
+ * the project root — e.g. `['src/server/config/providers.ts']`.
76
+ *
77
+ * `spfn env audit` counts a name as read when it appears as a property
78
+ * access or a string literal; a name only ever reached through
79
+ * `env[name]` needs this instead. The name passes when every listed file
80
+ * exists and is scanned. The runtime ignores `readBy`.
81
+ */
82
+ readBy?: string[];
83
+ }
84
+ /**
85
+ * The layer of a deployment an env value comes from — see {@link EnvVarSchema.layer}.
86
+ */
87
+ type EnvLayer = 'environment' | 'instance';
88
+ /**
89
+ * 스키마 컬렉션 타입
90
+ */
91
+ type EnvSchemaCollection = Record<string, EnvVarSchema<any>>;
92
+ /**
93
+ * Helper type: Check if field has default value
94
+ */
95
+ type HasDefault<T> = T extends {
96
+ default: any;
97
+ } ? true : false;
98
+ /**
99
+ * Helper type: Check if field is explicitly required
100
+ */
101
+ type IsRequired<T> = T extends {
102
+ required: true;
103
+ } ? true : false;
104
+ /**
105
+ * Helper type: Check if field should be required (has default OR required: true)
106
+ */
107
+ type ShouldBeRequired<T> = HasDefault<T> extends true ? true : IsRequired<T>;
108
+ /**
109
+ * 스키마로부터 타입 추출
110
+ *
111
+ * required: true 또는 default가 있는 필드 → 필수
112
+ * required: false 또는 미지정 → optional (| undefined)
113
+ */
114
+ type InferEnvType<T extends EnvSchemaCollection> = {
115
+ [K in keyof T as ShouldBeRequired<T[K]> extends true ? K : never]: T[K] extends EnvVarSchema<infer U> ? U : string;
116
+ } & {
117
+ [K in keyof T as ShouldBeRequired<T[K]> extends true ? never : K]?: T[K] extends EnvVarSchema<infer U> ? U | undefined : string | undefined;
118
+ };
119
+ /**
120
+ * 스키마 정의 헬퍼 (타입 추론 지원)
121
+ *
122
+ * Automatically fills in the `key` property from object keys. Every other
123
+ * field passes through as written — an entry without `layer` may come from
124
+ * any deployment layer.
125
+ *
126
+ * @example
127
+ * ```typescript
128
+ * const schema = defineEnvSchema({
129
+ * DATABASE_URL: envString({ description: 'Database URL', required: true })
130
+ * });
131
+ * // Automatically adds key: 'DATABASE_URL'
132
+ * ```
133
+ */
134
+ declare function defineEnvSchema<T extends Record<string, any>>(schema: T): {
135
+ [K in keyof T]: T[K] & {
136
+ key: K;
137
+ };
138
+ };
139
+ /**
140
+ * 문자열 스키마 헬퍼
141
+ *
142
+ * @example
143
+ * ```typescript
144
+ * const schema = {
145
+ * API_KEY: {
146
+ * ...envString({
147
+ * description: 'API authentication key',
148
+ * required: true,
149
+ * sensitive: true,
150
+ * }),
151
+ * key: 'API_KEY',
152
+ * }
153
+ * };
154
+ * ```
155
+ */
156
+ declare function envString<T extends Omit<EnvVarSchema, 'key' | 'type'>>(options: T): T & {
157
+ type: 'string';
158
+ validator: (value: string) => string;
159
+ };
160
+ /**
161
+ * 시크릿 스키마 헬퍼 — sensitive:true 자동.
162
+ *
163
+ * generate 지정 시 프로비저닝(secrets CLI)이 값을 자동 생성, 미지정이면 외부 입력(사용자가 붙여넣음).
164
+ * 이로써 env.config.ts가 "설정(envString) vs 외부 시크릿(envSecret) vs 자동 시크릿(envSecret+generate)"을
165
+ * 선언적으로 가른다 — CLI·프레임워크·프로비저닝이 한 원천을 읽는다.
166
+ *
167
+ * @example
168
+ * ```typescript
169
+ * SPFN_AUTH_SESSION_SECRET: envSecret({ description: '세션 서명 키', generate: 'base64url32' }),
170
+ * OPENAI_API_KEY: envSecret({ description: 'OpenAI API 키' }), // generate 없음 → 외부 입력
171
+ * ```
172
+ */
173
+ declare function envSecret<T extends Omit<EnvVarSchema, 'key' | 'type' | 'sensitive'>>(options: T): T & {
174
+ type: 'string';
175
+ sensitive: true;
176
+ validator: (value: string) => string;
177
+ };
178
+ /**
179
+ * 숫자 스키마 헬퍼
180
+ *
181
+ * @example
182
+ * ```typescript
183
+ * const schema = {
184
+ * PORT: {
185
+ * ...envNumber({
186
+ * description: 'Server port',
187
+ * default: 3000,
188
+ * validator: createNumberParser({ min: 1, max: 65535 }),
189
+ * }),
190
+ * key: 'PORT',
191
+ * }
192
+ * };
193
+ * ```
194
+ */
195
+ declare function envNumber<T extends Omit<EnvVarSchema<number>, 'key' | 'type'>>(options: T): T & {
196
+ type: 'number';
197
+ validator: (value: string) => number;
198
+ };
199
+ /**
200
+ * Boolean 스키마 헬퍼
201
+ *
202
+ * @example
203
+ * ```typescript
204
+ * const schema = {
205
+ * DEBUG: {
206
+ * ...envBoolean({
207
+ * description: 'Enable debug mode',
208
+ * default: false,
209
+ * }),
210
+ * key: 'DEBUG',
211
+ * }
212
+ * };
213
+ * ```
214
+ */
215
+ declare function envBoolean<T extends Omit<EnvVarSchema<boolean>, 'key' | 'type'>>(options: T): T & {
216
+ type: 'boolean';
217
+ validator: (value: string) => boolean;
218
+ };
219
+ /**
220
+ * URL 스키마 헬퍼
221
+ *
222
+ * @example
223
+ * ```typescript
224
+ * const schema = {
225
+ * DATABASE_URL: {
226
+ * ...envUrl({
227
+ * description: 'Database connection URL',
228
+ * required: true,
229
+ * validator: parsePostgresUrl,
230
+ * }),
231
+ * key: 'DATABASE_URL',
232
+ * }
233
+ * };
234
+ * ```
235
+ */
236
+ declare function envUrl<T extends Omit<EnvVarSchema, 'key' | 'type'>>(options: T): T & {
237
+ type: 'url';
238
+ };
239
+ /**
240
+ * Enum 스키마 헬퍼
241
+ *
242
+ * @example
243
+ * ```typescript
244
+ * const schema = {
245
+ * LOG_LEVEL: {
246
+ * ...envEnum(['debug', 'info', 'warn', 'error'] as const, {
247
+ * description: 'Logging level',
248
+ * default: 'info',
249
+ * }),
250
+ * key: 'LOG_LEVEL',
251
+ * }
252
+ * };
253
+ * ```
254
+ */
255
+ declare function envEnum<T extends string, O extends Omit<EnvVarSchema<T>, 'key' | 'type' | 'validator'>>(allowed: readonly T[], options: O): O & {
256
+ type: 'enum';
257
+ validator: (val: string) => T;
258
+ };
259
+ /**
260
+ * JSON 스키마 헬퍼
261
+ *
262
+ * @example
263
+ * ```typescript
264
+ * const schema = {
265
+ * CONFIG_JSON: {
266
+ * ...envJson<{ host: string; port: number }>({
267
+ * description: 'JSON configuration object',
268
+ * required: true,
269
+ * }),
270
+ * key: 'CONFIG_JSON',
271
+ * }
272
+ * };
273
+ * ```
274
+ */
275
+ declare function envJson<T = any, O extends Omit<EnvVarSchema<T>, 'key' | 'type' | 'validator'> = Omit<EnvVarSchema<T>, 'key' | 'type' | 'validator'>>(options: O): O & {
276
+ type: 'json';
277
+ validator: (val: string) => T;
278
+ };
279
+ /**
280
+ * 환경변수가 클라이언트에서 접근 가능한지 확인
281
+ * (NEXT_PUBLIC_ 접두사로 판단)
282
+ *
283
+ * @param key - 환경변수 키
284
+ * @returns 클라이언트에서 접근 가능하면 true
285
+ *
286
+ * @example
287
+ * ```typescript
288
+ * isClientAccessible('NEXT_PUBLIC_API_URL'); // true
289
+ * isClientAccessible('DATABASE_URL'); // false
290
+ * ```
291
+ */
292
+ declare function isClientAccessible(key: string): boolean;
293
+ /**
294
+ * 환경변수가 서버 전용인지 확인
295
+ * (NEXT_PUBLIC_ 접두사가 없으면 서버 전용)
296
+ *
297
+ * @param key - 환경변수 키
298
+ * @returns 서버 전용이면 true
299
+ *
300
+ * @example
301
+ * ```typescript
302
+ * isServerOnly('DATABASE_URL'); // true
303
+ * isServerOnly('NEXT_PUBLIC_API_URL'); // false
304
+ * ```
305
+ */
306
+ declare function isServerOnly(key: string): boolean;
307
+ /**
308
+ * 스키마의 nextjs 옵션 값 결정
309
+ *
310
+ * 명시적으로 지정되지 않은 경우:
311
+ * - NEXT_PUBLIC_* → true
312
+ * - 그 외 → false
313
+ *
314
+ * @param schema - 환경변수 스키마
315
+ * @returns Next.js 프로세스에서 사용 가능 여부
316
+ */
317
+ declare function isNextjsAccessible(schema: EnvVarSchema): boolean;
318
+ /**
319
+ * 스키마가 SPFN 서버 전용인지 확인
320
+ *
321
+ * @param schema - 환경변수 스키마
322
+ * @returns SPFN 서버에서만 사용되면 true
323
+ */
324
+ declare function isSpfnServerOnly(schema: EnvVarSchema): boolean;
325
+
326
+ /**
327
+ * Environment Variable Registry
328
+ *
329
+ * 환경변수 스키마를 등록하고 타입 안전하게 접근할 수 있는 레지스트리
330
+ *
331
+ * @example
332
+ * ```typescript
333
+ * const schema = defineEnvSchema({
334
+ * DATABASE_URL: envString({ description: 'Database URL', required: true })
335
+ * });
336
+ *
337
+ * const registry = createEnvRegistry(schema);
338
+ * const env = registry.validate(); // 검증 + env 반환
339
+ * console.log(env.DATABASE_URL);
340
+ * ```
341
+ *
342
+ * @module env/registry
343
+ */
344
+
345
+ /**
346
+ * 환경변수 레지스트리
347
+ *
348
+ * 스키마 기반 환경변수 관리 및 검증
349
+ */
350
+ declare class EnvRegistry<T extends EnvSchemaCollection = EnvSchemaCollection> {
351
+ private schemas;
352
+ private hasValidated;
353
+ constructor(schemas?: T);
354
+ /**
355
+ * 스키마 등록
356
+ */
357
+ register(schema: EnvVarSchema): void;
358
+ /**
359
+ * 여러 스키마 등록
360
+ */
361
+ registerMultiple(schemas: EnvSchemaCollection): void;
362
+ /**
363
+ * 검증 상태 리셋 (테스트용)
364
+ */
365
+ reset(): void;
366
+ /**
367
+ * 환경변수 원시값 가져오기 (fallback 지원)
368
+ */
369
+ private getRawValue;
370
+ /**
371
+ * 값에 validator 적용
372
+ */
373
+ private applyValidator;
374
+ /**
375
+ * 스키마 검증 수행 (값 읽기 없이)
376
+ *
377
+ * @internal
378
+ */
379
+ private validateSchemas;
380
+ /**
381
+ * SKIP_ENV_VALIDATION 환경변수 확인
382
+ */
383
+ private shouldSkipValidation;
384
+ /**
385
+ * 실제 접근 시점에 환경변수 값 가져오기 및 검증
386
+ *
387
+ * @internal
388
+ */
389
+ private getAndValidate;
390
+ /**
391
+ * 모든 환경변수를 명시적으로 검증 (SKIP_ENV_VALIDATION 무시)
392
+ *
393
+ * CLI에서 사용하기 위한 메서드로, 모든 required 환경변수를 강제 검증합니다.
394
+ *
395
+ * @returns 검증 결과 (errors, warnings)
396
+ */
397
+ validateAll(): {
398
+ errors: Array<{
399
+ key: string;
400
+ message: string;
401
+ }>;
402
+ warnings: Array<{
403
+ key: string;
404
+ message: string;
405
+ }>;
406
+ };
407
+ /**
408
+ * 환경변수 검증 및 타입 안전한 env 객체 반환
409
+ *
410
+ * Proxy 기반으로 구현되어 실제 환경변수 접근 시점에 값을 읽고 검증합니다.
411
+ * 이를 통해 dotenv 로딩 타이밍과 무관하게 최신 환경변수 값을 가져올 수 있습니다.
412
+ *
413
+ * @returns 검증된 환경변수 객체 (Proxy)
414
+ * @throws {Error} 필수 변수 누락 또는 검증 실패 시
415
+ *
416
+ * @example
417
+ * ```typescript
418
+ * const registry = createEnvRegistry(schema);
419
+ * const env = registry.validate(); // 스키마만 검증
420
+ * // ... dotenv 로딩 ...
421
+ * console.log(env.DATABASE_URL); // 이 시점에 실제 값 읽기
422
+ * ```
423
+ */
424
+ validate(): InferEnvType<T>;
425
+ }
426
+ /**
427
+ * 환경변수 검증 결과
428
+ */
429
+ interface EnvValidationResult {
430
+ valid: boolean;
431
+ errors: Array<{
432
+ key: string;
433
+ message: string;
434
+ }>;
435
+ warnings: Array<{
436
+ key: string;
437
+ message: string;
438
+ }>;
439
+ }
440
+ /**
441
+ * 레지스트리 생성 헬퍼
442
+ *
443
+ * @example
444
+ * ```typescript
445
+ * const schema = defineEnvSchema({
446
+ * DATABASE_URL: envString({ description: 'Database URL', required: true })
447
+ * });
448
+ *
449
+ * const registry = createEnvRegistry(schema);
450
+ * const env = registry.validate();
451
+ * ```
452
+ */
453
+ declare function createEnvRegistry<T extends EnvSchemaCollection>(schemas: T): EnvRegistry<T>;
454
+ /**
455
+ * 모든 환경변수를 명시적으로 검증 (SKIP_ENV_VALIDATION 무시)
456
+ *
457
+ * CLI `spfn env validate` 명령어에서 사용
458
+ *
459
+ * @param registries - 검증할 레지스트리 배열
460
+ * @returns 검증 결과
461
+ *
462
+ * @example
463
+ * ```typescript
464
+ * const result = validateAllEnv([coreRegistry, authRegistry]);
465
+ * if (!result.valid) {
466
+ * console.error('Missing env vars:', result.errors);
467
+ * process.exit(1);
468
+ * }
469
+ * ```
470
+ */
471
+ declare function validateAllEnv(registries: EnvRegistry<any>[]): EnvValidationResult;
472
+
473
+ export { type EnvLayer as E, type InferEnvType as I, EnvRegistry as a, type EnvSchemaCollection as b, type EnvValidationResult as c, type EnvVarSchema as d, createEnvRegistry as e, defineEnvSchema as f, envBoolean as g, envEnum as h, envJson as i, envNumber as j, envSecret as k, envString as l, envUrl as m, isClientAccessible as n, isNextjsAccessible as o, isServerOnly as p, isSpfnServerOnly as q, validateAllEnv as v };
@@ -1,8 +1,11 @@
1
- import { a as RouteDef, R as Router } from '../router-Qbssr11H.js';
2
- export { M as MergedInput, P as PaginatedResult, c as RouteAuthProfile, d as RouteBuilderContext, e as RouteContract, f as RouteHandlerFn, g as RouteInput, h as defineRouter, r as route } from '../router-Qbssr11H.js';
1
+ import { R as RouteDef } from '../route-builder-DRW-MSCJ.js';
2
+ export { M as MergedInput, P as PaginatedResult, a as RouteAuthProfile, b as RouteBuilderContext, c as RouteContract, d as RouteHandlerFn, e as RouteInput, r as route } from '../route-builder-DRW-MSCJ.js';
3
+ import { R as Router } from '../router-4YoIy5fN.js';
4
+ export { d as defineRouter, a as defineUnmappedRouter } from '../router-4YoIy5fN.js';
3
5
  import { Hono, MiddlewareHandler } from 'hono';
4
6
  import { HttpMethod } from './types.js';
5
- export { E as ExtractMiddlewareNames, a as NamedMiddleware, N as NamedMiddlewareFactory, d as defineMiddleware, b as defineMiddlewareFactory } from '../define-middleware-DfDP39Nq.js';
7
+ export { RouteEffect } from './types.js';
8
+ export { E as ExtractMiddlewareNames, a as NamedMiddleware, N as NamedMiddlewareFactory, d as defineMiddleware, b as defineMiddlewareFactory } from '../define-middleware-CVKgqo8S.js';
6
9
  import * as _sinclair_typebox from '@sinclair/typebox';
7
10
  import { TSchema, Kind } from '@sinclair/typebox';
8
11
  import 'hono/utils/http-status';
@@ -12,6 +12,8 @@ var RouteBuilder = class _RouteBuilder {
12
12
  _middlewares;
13
13
  _skipMiddlewares;
14
14
  _contract;
15
+ _effect;
16
+ _summary;
15
17
  /**
16
18
  * Create a new RouteBuilder with copied properties and optional overrides
17
19
  */
@@ -24,6 +26,8 @@ var RouteBuilder = class _RouteBuilder {
24
26
  builder._middlewares = overrides?.middlewares ?? this._middlewares;
25
27
  builder._skipMiddlewares = overrides?.skipMiddlewares ?? this._skipMiddlewares;
26
28
  builder._contract = overrides?.contract ?? this._contract;
29
+ builder._effect = overrides?.effect ?? this._effect;
30
+ builder._summary = overrides?.summary ?? this._summary;
27
31
  return builder;
28
32
  }
29
33
  /**
@@ -143,7 +147,7 @@ var RouteBuilder = class _RouteBuilder {
143
147
  * @example
144
148
  * ```ts
145
149
  * // Skip specific middlewares
146
- * route.get('/health')
150
+ * route.get('/status')
147
151
  * .skip(['auth', 'rateLimit'])
148
152
  * .handler(async (c) => c.json({ status: 'ok' }));
149
153
  *
@@ -194,6 +198,49 @@ var RouteBuilder = class _RouteBuilder {
194
198
  contract(contract) {
195
199
  return this.clone({ contract });
196
200
  }
201
+ /**
202
+ * Declare what invoking this route does
203
+ *
204
+ * Read by a command surface (`@spfn/core/surface`, `@spfn/core/ops`),
205
+ * which announces it in its manifest so a machine caller can tell a read
206
+ * from a write before it calls — `spfn ops call` asks for `--yes` before a
207
+ * `destructive` command. A surface built with `defineSurface` refuses an
208
+ * app command that does not declare one; on `/_ops` it is optional.
209
+ *
210
+ * Nothing else reads it: codegen, the route map, and the contract are
211
+ * unaffected, and a route outside any surface simply carries it.
212
+ *
213
+ * @example
214
+ * ```ts
215
+ * export const pageAdd = app.route.post('/pages')
216
+ * .input({ body: PageAddBody })
217
+ * .effect('write')
218
+ * .summary('Add a page to the blueprint')
219
+ * .handler(async (c) => { ... });
220
+ * ```
221
+ */
222
+ effect(effect) {
223
+ return this.clone({ effect });
224
+ }
225
+ /**
226
+ * Describe this route in one line for a command surface's manifest
227
+ *
228
+ * A surface announces it beside the command in `spfn ops list` and
229
+ * `--describe`, and refuses one that is empty or longer than 500
230
+ * characters. Codegen, the route map, and the contract ignore it. On a
231
+ * module command the module's own summary wins.
232
+ *
233
+ * @example
234
+ * ```ts
235
+ * export const pageAdd = app.route.post('/pages')
236
+ * .effect('write')
237
+ * .summary('Add a page to the blueprint')
238
+ * .handler(async (c) => { ... });
239
+ * ```
240
+ */
241
+ summary(summary) {
242
+ return this.clone({ summary });
243
+ }
197
244
  /**
198
245
  * Define handler function
199
246
  *
@@ -249,6 +296,8 @@ var RouteBuilder = class _RouteBuilder {
249
296
  middlewares: this._middlewares,
250
297
  skipMiddlewares: this._skipMiddlewares,
251
298
  contract: this._contract,
299
+ ...this._effect !== void 0 ? { effect: this._effect } : {},
300
+ ...this._summary !== void 0 ? { summary: this._summary } : {},
252
301
  handler: fn,
253
302
  _input: {},
254
303
  _interceptor: {},
@@ -273,43 +322,33 @@ var route = {
273
322
  };
274
323
 
275
324
  // src/route/router.ts
276
- function createRouterInstance(routes, packageRouters = [], globalMiddlewares = [], contractVersion = null) {
325
+ function createRouterInstance(state) {
277
326
  return {
278
- routes,
279
- _routes: routes,
280
- _packageRouters: packageRouters,
281
- _globalMiddlewares: globalMiddlewares,
282
- _contractVersion: contractVersion,
327
+ routes: state.routes,
328
+ _routes: state.routes,
329
+ _packageRouters: state.packageRouters,
330
+ _globalMiddlewares: state.globalMiddlewares,
331
+ _contractVersion: state.contractVersion,
332
+ _publishesRouteMap: state.publishesRouteMap,
283
333
  packages(routers) {
284
- const newPackageRouters = [...this._packageRouters, ...routers];
334
+ const newPackageRouters = [...state.packageRouters, ...routers];
285
335
  for (const pkgRouter of routers) {
286
336
  if (pkgRouter._packageRouters?.length > 0) {
287
337
  newPackageRouters.push(...pkgRouter._packageRouters);
288
338
  }
289
339
  }
290
- return createRouterInstance(
291
- this.routes,
292
- newPackageRouters,
293
- this._globalMiddlewares,
294
- this._contractVersion
295
- );
340
+ assertDistinctSurfaces(newPackageRouters);
341
+ return createRouterInstance({ ...state, packageRouters: newPackageRouters });
296
342
  },
297
343
  use(middlewares) {
298
- return createRouterInstance(
299
- this.routes,
300
- this._packageRouters,
301
- [...this._globalMiddlewares, ...middlewares],
302
- this._contractVersion
303
- );
344
+ return createRouterInstance({
345
+ ...state,
346
+ globalMiddlewares: [...state.globalMiddlewares, ...middlewares]
347
+ });
304
348
  },
305
349
  contractVersion(version) {
306
350
  assertContractVersion(version);
307
- return createRouterInstance(
308
- this.routes,
309
- this._packageRouters,
310
- this._globalMiddlewares,
311
- version
312
- );
351
+ return createRouterInstance({ ...state, contractVersion: version });
313
352
  }
314
353
  };
315
354
  }
@@ -320,8 +359,38 @@ function assertContractVersion(version) {
320
359
  );
321
360
  }
322
361
  }
362
+ function assertDistinctSurfaces(packageRouters) {
363
+ const namespaces = /* @__PURE__ */ new Set();
364
+ for (const router of packageRouters) {
365
+ const namespace = router._surfaceNamespace;
366
+ if (namespace === void 0) {
367
+ continue;
368
+ }
369
+ if (namespaces.has(namespace)) {
370
+ throw new Error(
371
+ `.packages() mounts two command surfaces on "${namespace}". Both serve ${namespace}/_manifest, so the second one's commands would answer while no manifest lists them. Mount one router per namespace.`
372
+ );
373
+ }
374
+ namespaces.add(namespace);
375
+ }
376
+ }
323
377
  function defineRouter(routes) {
324
- return createRouterInstance(routes);
378
+ return createRouterInstance({
379
+ routes,
380
+ packageRouters: [],
381
+ globalMiddlewares: [],
382
+ contractVersion: null,
383
+ publishesRouteMap: true
384
+ });
385
+ }
386
+ function defineUnmappedRouter(routes) {
387
+ return createRouterInstance({
388
+ routes,
389
+ packageRouters: [],
390
+ globalMiddlewares: [],
391
+ contractVersion: null,
392
+ publishesRouteMap: false
393
+ });
325
394
  }
326
395
  function FileSchema(options) {
327
396
  return Type.Unsafe({
@@ -864,6 +933,6 @@ function isHttpMethod(value) {
864
933
  var Nullable = (schema) => Type.Union([schema, Type.Null()]);
865
934
  var OptionalNullable = (schema) => Type.Optional(Type.Union([schema, Type.Null()]));
866
935
 
867
- export { FileArraySchema, FileSchema, Nullable, OptionalFileSchema, OptionalNullable, defineMiddleware, defineMiddlewareFactory, defineRouter, formatFileSize, getFileOptions, isFileArraySchema, isFileSchema, isHttpMethod, registerRoutes, route };
936
+ export { FileArraySchema, FileSchema, Nullable, OptionalFileSchema, OptionalNullable, defineMiddleware, defineMiddlewareFactory, defineRouter, defineUnmappedRouter, formatFileSize, getFileOptions, isFileArraySchema, isFileSchema, isHttpMethod, registerRoutes, route };
868
937
  //# sourceMappingURL=index.js.map
869
938
  //# sourceMappingURL=index.js.map