bunderstack 0.22.1 → 0.23.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/CHANGELOG.md +37 -0
- package/dist/api/catalog.d.ts +8 -0
- package/dist/api/catalog.d.ts.map +1 -0
- package/dist/api/catalog.js +56 -0
- package/dist/api/catalog.js.map +1 -0
- package/dist/api/registry.d.ts.map +1 -1
- package/dist/api/registry.js +1 -0
- package/dist/api/registry.js.map +1 -1
- package/dist/api/router.d.ts +3 -0
- package/dist/api/router.d.ts.map +1 -1
- package/dist/api/router.js +7 -2
- package/dist/api/router.js.map +1 -1
- package/dist/backend.d.ts.map +1 -1
- package/dist/backend.js +9 -1
- package/dist/backend.js.map +1 -1
- package/dist/blueprint.d.ts +21 -1
- package/dist/blueprint.d.ts.map +1 -1
- package/dist/blueprint.js +45 -17
- package/dist/blueprint.js.map +1 -1
- package/dist/env.d.ts +18 -0
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +1 -0
- package/dist/env.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/jobs/index.d.ts +1 -1
- package/dist/jobs/index.d.ts.map +1 -1
- package/dist/jobs/index.js.map +1 -1
- package/dist/jobs/runtime.d.ts +5 -1
- package/dist/jobs/runtime.d.ts.map +1 -1
- package/dist/jobs/runtime.js +15 -5
- package/dist/jobs/runtime.js.map +1 -1
- package/dist/jobs/worker.d.ts +6 -0
- package/dist/jobs/worker.d.ts.map +1 -1
- package/dist/jobs/worker.js +77 -21
- package/dist/jobs/worker.js.map +1 -1
- package/dist/manifest.d.ts +20 -0
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +68 -6
- package/dist/manifest.js.map +1 -1
- package/dist/readiness-probes.d.ts +4 -0
- package/dist/readiness-probes.d.ts.map +1 -0
- package/dist/readiness-probes.js +23 -0
- package/dist/readiness-probes.js.map +1 -0
- package/dist/readiness.d.ts +39 -0
- package/dist/readiness.d.ts.map +1 -0
- package/dist/readiness.js +72 -0
- package/dist/readiness.js.map +1 -0
- package/dist/runtime.d.ts +1 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +15 -5
- package/dist/runtime.js.map +1 -1
- package/llms-full.txt +2979 -0
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,43 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `bunderstack` will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.23.1] - 2026-08-31
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- **Continuous background-job concurrency.** Workers now fill declared
|
|
10
|
+
concurrency above the internal ten-row claim batch and refill each freed slot
|
|
11
|
+
immediately instead of waiting for the slowest job in a wave. Deterministic
|
|
12
|
+
fixture ticks retain complete-wave execution.
|
|
13
|
+
|
|
14
|
+
## [0.23.0] - 2026-08-29
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- **Environment metadata.** `env.meta` declares `sensitive` and `description` per
|
|
19
|
+
key. Server keys default to sensitive and client keys can never be — `PUBLIC_*`
|
|
20
|
+
values ship to the browser. Both reach the manifest and the blueprint; values
|
|
21
|
+
never do.
|
|
22
|
+
- **Application operation catalog.** The blueprint gains `api.operations`, listing
|
|
23
|
+
the procedures an application declared itself with an `effect` of `read`,
|
|
24
|
+
`mutation`, or `unknown`. Generated CRUD, storage, and realtime routes stay
|
|
25
|
+
derivable from `resources`.
|
|
26
|
+
- **Readiness endpoint.** `GET /api/readiness` reports database reachability,
|
|
27
|
+
schema provisioning, and background-queue backlog, and echoes
|
|
28
|
+
`BUNDERSTACK_REVISION` as `revision`. `GET /api/health` is unchanged.
|
|
29
|
+
- **`isSensitiveEnvVar(entry)`** exported from `bunderstack/blueprint`, encoding
|
|
30
|
+
the scope-based default for blueprints written before this release.
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
|
|
34
|
+
- **Forward-compatible blueprint parsing.** Every object in the blueprint schema
|
|
35
|
+
now preserves unknown keys instead of rejecting the file, so a host can read a
|
|
36
|
+
blueprint written by a newer generator. Run `bunderstack blueprint` to
|
|
37
|
+
regenerate; the new sections only appear after you do.
|
|
38
|
+
- The application router passed as a callback is now resolved once when
|
|
39
|
+
`bunderstack(config)` is called rather than at `start()`, so the manifest and
|
|
40
|
+
the running app describe the same router.
|
|
41
|
+
|
|
5
42
|
## [0.22.1] - 2026-08-28
|
|
6
43
|
|
|
7
44
|
### Added
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ApiOperation } from '../manifest.js';
|
|
2
|
+
/**
|
|
3
|
+
* Describes the procedures an application declared itself. Generated CRUD,
|
|
4
|
+
* storage, and realtime routes are excluded: a host derives those from the
|
|
5
|
+
* blueprint's `resources`.
|
|
6
|
+
*/
|
|
7
|
+
export declare function describeApiOperations(router: unknown): ApiOperation[];
|
|
8
|
+
//# sourceMappingURL=catalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../src/api/catalog.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AA2D/C;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,OAAO,GAAG,YAAY,EAAE,CAKrE"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// src/api/catalog.ts — static description of the application's own procedures.
|
|
2
|
+
import { getOpenAPIMeta } from '@orpc/openapi';
|
|
3
|
+
const READ_METHODS = new Set(['GET', 'HEAD', 'OPTIONS']);
|
|
4
|
+
const MAX_SUMMARY = 200;
|
|
5
|
+
function summaryText(value) {
|
|
6
|
+
if (typeof value !== 'string')
|
|
7
|
+
return undefined;
|
|
8
|
+
const collapsed = value.trim().replace(/\s+/g, ' ');
|
|
9
|
+
if (!collapsed)
|
|
10
|
+
return undefined;
|
|
11
|
+
return collapsed.length > MAX_SUMMARY
|
|
12
|
+
? `${collapsed.slice(0, MAX_SUMMARY - 1)}…`
|
|
13
|
+
: collapsed;
|
|
14
|
+
}
|
|
15
|
+
function walk(router, segments) {
|
|
16
|
+
const operations = [];
|
|
17
|
+
for (const [key, value] of Object.entries(router)) {
|
|
18
|
+
if (!value || typeof value !== 'object')
|
|
19
|
+
continue;
|
|
20
|
+
const current = [...segments, key];
|
|
21
|
+
if (!('~orpc' in value)) {
|
|
22
|
+
operations.push(...walk(value, current));
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
const meta = (getOpenAPIMeta(value) ?? {});
|
|
26
|
+
const handle = current.join('.');
|
|
27
|
+
const method = typeof meta.method === 'string' ? meta.method.toUpperCase() : undefined;
|
|
28
|
+
const summary = summaryText(meta.summary);
|
|
29
|
+
operations.push({
|
|
30
|
+
handle,
|
|
31
|
+
operationId: typeof meta.operationId === 'string' ? meta.operationId : handle,
|
|
32
|
+
// A procedure that declares no method could be anything. Calling it a
|
|
33
|
+
// read would tell a host it is safe to invoke without asking anyone.
|
|
34
|
+
effect: method === undefined
|
|
35
|
+
? 'unknown'
|
|
36
|
+
: READ_METHODS.has(method)
|
|
37
|
+
? 'read'
|
|
38
|
+
: 'mutation',
|
|
39
|
+
...(method === undefined ? {} : { method }),
|
|
40
|
+
...(typeof meta.path === 'string' ? { path: meta.path } : {}),
|
|
41
|
+
...(summary === undefined ? {} : { summary }),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return operations;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Describes the procedures an application declared itself. Generated CRUD,
|
|
48
|
+
* storage, and realtime routes are excluded: a host derives those from the
|
|
49
|
+
* blueprint's `resources`.
|
|
50
|
+
*/
|
|
51
|
+
export function describeApiOperations(router) {
|
|
52
|
+
if (!router || typeof router !== 'object')
|
|
53
|
+
return [];
|
|
54
|
+
return walk(router, []).sort((left, right) => left.handle.localeCompare(right.handle));
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=catalog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog.js","sourceRoot":"","sources":["../../src/api/catalog.ts"],"names":[],"mappings":"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","sourcesContent":["// src/api/catalog.ts — static description of the application's own procedures.\nimport { getOpenAPIMeta } from '@orpc/openapi'\n\nimport type { ApiOperation } from '../manifest'\n\nconst READ_METHODS = new Set(['GET', 'HEAD', 'OPTIONS'])\nconst MAX_SUMMARY = 200\n\nfunction summaryText(value: unknown): string | undefined {\n if (typeof value !== 'string') return undefined\n const collapsed = value.trim().replace(/\\s+/g, ' ')\n if (!collapsed) return undefined\n return collapsed.length > MAX_SUMMARY\n ? `${collapsed.slice(0, MAX_SUMMARY - 1)}…`\n : collapsed\n}\n\nfunction walk(\n router: Record<string, unknown>,\n segments: string[],\n): ApiOperation[] {\n const operations: ApiOperation[] = []\n\n for (const [key, value] of Object.entries(router)) {\n if (!value || typeof value !== 'object') continue\n const current = [...segments, key]\n\n if (!('~orpc' in value)) {\n operations.push(...walk(value as Record<string, unknown>, current))\n continue\n }\n\n const meta = (getOpenAPIMeta(value as never) ?? {}) as Record<\n string,\n unknown\n >\n const handle = current.join('.')\n const method =\n typeof meta.method === 'string' ? meta.method.toUpperCase() : undefined\n const summary = summaryText(meta.summary)\n\n operations.push({\n handle,\n operationId:\n typeof meta.operationId === 'string' ? meta.operationId : handle,\n // A procedure that declares no method could be anything. Calling it a\n // read would tell a host it is safe to invoke without asking anyone.\n effect:\n method === undefined\n ? 'unknown'\n : READ_METHODS.has(method)\n ? 'read'\n : 'mutation',\n ...(method === undefined ? {} : { method }),\n ...(typeof meta.path === 'string' ? { path: meta.path } : {}),\n ...(summary === undefined ? {} : { summary }),\n })\n }\n\n return operations\n}\n\n/**\n * Describes the procedures an application declared itself. Generated CRUD,\n * storage, and realtime routes are excluded: a host derives those from the\n * blueprint's `resources`.\n */\nexport function describeApiOperations(router: unknown): ApiOperation[] {\n if (!router || typeof router !== 'object') return []\n return walk(router as Record<string, unknown>, []).sort((left, right) =>\n left.handle.localeCompare(right.handle),\n )\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/api/registry.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/api/registry.ts"],"names":[],"mappings":"AAmBA,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,gBAAgB,EAAE,CAAA;CAC5B;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,uBAAuB;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACtC,YAAY,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,gBAAgB,CAAC,CAAA;IAChE,gFAAgF;IAChF,mBAAmB,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;CAC1C;AAYD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CActE;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,GAAE,kBAAuB,GAC/B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAqBzB;AAED,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,MAAM,GAAE,MAAM,EAAO,GACpB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAmCzB;AAiHD,wBAAsB,gBAAgB,CACpC,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,WAAW,CAAC,CAgFtB"}
|
package/dist/api/registry.js
CHANGED
package/dist/api/registry.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/api/registry.ts"],"names":[],"mappings":"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","sourcesContent":["import { getOpenAPIMeta } from '@orpc/openapi'\n\nconst RESERVED_EXACT = new Set([\n '/api/health',\n '/api/openapi.json',\n '/api/realtime',\n '/api/rpc',\n '/api/auth',\n '/api/files',\n])\nconst RESERVED_PREFIXES = ['/api/rpc/', '/api/auth/', '/api/files/']\n\nfunction collisionForBunderstackPath(path: string): string | undefined {\n if (RESERVED_EXACT.has(path)) return 'it is reserved by bunderstack'\n const prefix = RESERVED_PREFIXES.find((value) => path.startsWith(value))\n return prefix ? `\"${prefix}*\" is reserved by bunderstack` : undefined\n}\n\nexport interface ApiRegistryEntry {\n handle: string\n operationId: string\n method: string\n path: string\n source: string\n}\n\nexport interface ApiRegistry {\n entries: ApiRegistryEntry[]\n}\n\nexport interface ForeignSpecEntry {\n spec: Record<string, unknown>\n prefix?: string\n source?: string\n}\n\nexport interface BuildApiRegistryOptions {\n nativeRouter?: Record<string, unknown>\n foreignSpecs?: Array<Record<string, unknown> | ForeignSpecEntry>\n /** Native procedures generated by Bunderstack itself may own reserved paths. */\n reservedCoreHandles?: ReadonlySet<string>\n}\n\nconst HTTP_METHODS = new Set([\n 'GET',\n 'POST',\n 'PUT',\n 'PATCH',\n 'DELETE',\n 'HEAD',\n 'OPTIONS',\n])\n\nexport function normalizeApiPath(path: string, prefix?: string): string {\n let cleanPath = path.startsWith('/') ? path : '/' + path\n if (prefix) {\n let cleanPrefix = prefix.startsWith('/') ? prefix : '/' + prefix\n if (cleanPrefix.endsWith('/')) {\n cleanPrefix = cleanPrefix.slice(0, -1)\n }\n if (cleanPath === cleanPrefix || cleanPath.startsWith(cleanPrefix + '/')) {\n // Path already starts with prefix\n } else {\n cleanPath = cleanPrefix + cleanPath\n }\n }\n return cleanPath.replace(/\\/+/g, '/')\n}\n\nexport interface ForeignSpecOptions {\n prefix?: string\n source?: string\n}\n\nexport function normalizeForeignOpenAPISpec(\n spec: Record<string, unknown>,\n options: ForeignSpecOptions = {},\n): Record<string, unknown> {\n const prefix = options.prefix\n const source = options.source\n const cloned: Record<string, unknown> = JSON.parse(JSON.stringify(spec))\n\n if (cloned.paths && typeof cloned.paths === 'object') {\n const normalizedPaths: Record<string, unknown> = {}\n for (const [routePath, pathItem] of Object.entries(\n cloned.paths as Record<string, unknown>,\n )) {\n const canonicalPath = normalizeApiPath(routePath, prefix)\n normalizedPaths[canonicalPath] = pathItem\n }\n cloned.paths = normalizedPaths\n }\n\n if (source) {\n cloned['x-bunderstack-source'] = source\n }\n\n return cloned\n}\n\nexport function mergeApiRoutersStrict(\n target: Record<string, unknown>,\n source?: Record<string, unknown>,\n prefix: string[] = [],\n): Record<string, unknown> {\n if (!source) return { ...target }\n const result: Record<string, unknown> = { ...target }\n\n for (const [key, val] of Object.entries(source)) {\n const currentPath = [...prefix, key]\n const handle = currentPath.join('.')\n const targetVal = result[key]\n\n if (targetVal !== undefined) {\n const isTargetNamespace =\n typeof targetVal === 'object' &&\n targetVal !== null &&\n !('~orpc' in targetVal)\n\n const isSourceNamespace =\n typeof val === 'object' && val !== null && !('~orpc' in val)\n\n if (isTargetNamespace && isSourceNamespace) {\n result[key] = mergeApiRoutersStrict(\n targetVal as Record<string, unknown>,\n val as Record<string, unknown>,\n currentPath,\n )\n } else {\n throw new Error(\n `[bunderstack] Router merge collision at handle \"${handle}\": duplicate procedure or namespace collision`,\n )\n }\n } else {\n result[key] = val\n }\n }\n\n return result\n}\n\nfunction normalizePathSegments(path: string): string[] {\n return path.split('/').filter(Boolean)\n}\n\nfunction isParamSegment(segment: string): boolean {\n return (\n (segment.startsWith('{') && segment.endsWith('}')) ||\n segment.startsWith(':')\n )\n}\n\nfunction normalizePathForCollision(path: string): string {\n const segments = normalizePathSegments(path)\n const normalized = segments.map((seg) =>\n isParamSegment(seg) ? '{param}' : seg,\n )\n return '/' + normalized.join('/')\n}\n\nfunction walkNativeRouter(\n obj: Record<string, unknown>,\n pathSegments: string[] = [],\n): ApiRegistryEntry[] {\n const entries: ApiRegistryEntry[] = []\n\n for (const [key, value] of Object.entries(obj)) {\n if (!value || typeof value !== 'object') continue\n const currentSegments = [...pathSegments, key]\n\n if ('~orpc' in value) {\n const meta = (getOpenAPIMeta(value as any) || {}) as Record<\n string,\n unknown\n >\n const handle = currentSegments.join('.')\n const method = ((meta.method as string) || 'GET').toUpperCase()\n const routePath = (meta.path as string) || '/' + currentSegments.join('/')\n const operationId =\n typeof meta.operationId === 'string' ? meta.operationId : handle\n\n entries.push({\n handle,\n operationId,\n method,\n path: routePath,\n source: 'native',\n })\n } else {\n entries.push(\n ...walkNativeRouter(value as Record<string, unknown>, currentSegments),\n )\n }\n }\n\n return entries\n}\n\nfunction extractForeignEntries(\n specs: Array<Record<string, unknown> | ForeignSpecEntry>,\n): ApiRegistryEntry[] {\n const entries: ApiRegistryEntry[] = []\n\n for (const item of specs) {\n if (!item) continue\n let specObj: Record<string, unknown>\n let source = 'foreign'\n\n if ('spec' in item && typeof item.spec === 'object' && item.spec !== null) {\n const entry = item as ForeignSpecEntry\n source = entry.source || 'foreign'\n specObj = normalizeForeignOpenAPISpec(entry.spec, {\n prefix: entry.prefix,\n source,\n })\n } else {\n const rawSpec = item as Record<string, unknown>\n source = (rawSpec['x-bunderstack-source'] as string) || 'foreign'\n specObj = normalizeForeignOpenAPISpec(rawSpec, { source })\n }\n\n if (!specObj || typeof specObj.paths !== 'object' || !specObj.paths)\n continue\n const paths = specObj.paths as Record<string, Record<string, unknown>>\n\n for (const [routePath, pathItem] of Object.entries(paths)) {\n if (!pathItem || typeof pathItem !== 'object') continue\n\n for (const [methodKey, operation] of Object.entries(pathItem)) {\n const uppercaseMethod = methodKey.toUpperCase()\n if (!HTTP_METHODS.has(uppercaseMethod)) continue\n if (!operation || typeof operation !== 'object') continue\n\n const op = operation as Record<string, unknown>\n const handle = `foreign:${source}:${uppercaseMethod}:${routePath}`\n const operationId =\n typeof op.operationId === 'string' ? op.operationId : handle\n\n entries.push({\n handle,\n operationId,\n method: uppercaseMethod,\n path: routePath,\n source,\n })\n }\n }\n }\n\n return entries\n}\n\nexport async function buildApiRegistry(\n options: BuildApiRegistryOptions = {},\n): Promise<ApiRegistry> {\n const entries: ApiRegistryEntry[] = []\n\n if (options.nativeRouter) {\n entries.push(...walkNativeRouter(options.nativeRouter))\n }\n\n if (options.foreignSpecs) {\n entries.push(...extractForeignEntries(options.foreignSpecs))\n }\n\n const errors: string[] = []\n\n for (const entry of entries) {\n if (entry.source !== 'native') continue\n if (options.reservedCoreHandles?.has(entry.handle)) continue\n const reason = collisionForBunderstackPath(entry.path)\n if (reason) {\n errors.push(\n `Reserved route collision on ${entry.method} ${entry.path}: native handle \"${entry.handle}\" conflicts because ${reason}`,\n )\n }\n }\n\n // Check duplicate handles\n const handleMap = new Map<string, ApiRegistryEntry>()\n for (const entry of entries) {\n const existing = handleMap.get(entry.handle)\n if (existing) {\n errors.push(\n `Duplicate handle \"${entry.handle}\": collision between ${existing.source} (${existing.method} ${existing.path}) and ${entry.source} (${entry.method} ${entry.path})`,\n )\n } else {\n handleMap.set(entry.handle, entry)\n }\n }\n\n // Check duplicate operation IDs\n const opIdMap = new Map<string, ApiRegistryEntry>()\n for (const entry of entries) {\n const existing = opIdMap.get(entry.operationId)\n if (existing) {\n errors.push(\n `Duplicate operation ID \"${entry.operationId}\": collision between ${existing.source} (${existing.handle}) and ${entry.source} (${entry.handle})`,\n )\n } else {\n opIdMap.set(entry.operationId, entry)\n }\n }\n\n // Check method/path collisions (exact and parameter ambiguity)\n const routePatternMap = new Map<string, ApiRegistryEntry>()\n for (const entry of entries) {\n const normalizedPath = normalizePathForCollision(entry.path)\n const routeKey = `${entry.method} ${normalizedPath}`\n\n const existing = routePatternMap.get(routeKey)\n if (existing) {\n if (existing.path === entry.path) {\n errors.push(\n `Exact method/path collision on ${entry.method} ${entry.path}: collision between ${existing.source} (${existing.handle}) and ${entry.source} (${entry.handle})`,\n )\n } else {\n errors.push(\n `Ambiguous parameter path collision on ${entry.method} (${existing.path} vs ${entry.path}): collision between ${existing.source} (${existing.handle}) and ${entry.source} (${entry.handle})`,\n )\n }\n } else {\n routePatternMap.set(routeKey, entry)\n }\n }\n\n if (errors.length > 0) {\n throw new Error(\n `[bunderstack] Route registry validation failed (${errors.length} error(s)):\\n` +\n errors.join('\\n'),\n )\n }\n\n return { entries }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/api/registry.ts"],"names":[],"mappings":"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","sourcesContent":["import { getOpenAPIMeta } from '@orpc/openapi'\n\nconst RESERVED_EXACT = new Set([\n '/api/health',\n '/api/readiness',\n '/api/openapi.json',\n '/api/realtime',\n '/api/rpc',\n '/api/auth',\n '/api/files',\n])\nconst RESERVED_PREFIXES = ['/api/rpc/', '/api/auth/', '/api/files/']\n\nfunction collisionForBunderstackPath(path: string): string | undefined {\n if (RESERVED_EXACT.has(path)) return 'it is reserved by bunderstack'\n const prefix = RESERVED_PREFIXES.find((value) => path.startsWith(value))\n return prefix ? `\"${prefix}*\" is reserved by bunderstack` : undefined\n}\n\nexport interface ApiRegistryEntry {\n handle: string\n operationId: string\n method: string\n path: string\n source: string\n}\n\nexport interface ApiRegistry {\n entries: ApiRegistryEntry[]\n}\n\nexport interface ForeignSpecEntry {\n spec: Record<string, unknown>\n prefix?: string\n source?: string\n}\n\nexport interface BuildApiRegistryOptions {\n nativeRouter?: Record<string, unknown>\n foreignSpecs?: Array<Record<string, unknown> | ForeignSpecEntry>\n /** Native procedures generated by Bunderstack itself may own reserved paths. */\n reservedCoreHandles?: ReadonlySet<string>\n}\n\nconst HTTP_METHODS = new Set([\n 'GET',\n 'POST',\n 'PUT',\n 'PATCH',\n 'DELETE',\n 'HEAD',\n 'OPTIONS',\n])\n\nexport function normalizeApiPath(path: string, prefix?: string): string {\n let cleanPath = path.startsWith('/') ? path : '/' + path\n if (prefix) {\n let cleanPrefix = prefix.startsWith('/') ? prefix : '/' + prefix\n if (cleanPrefix.endsWith('/')) {\n cleanPrefix = cleanPrefix.slice(0, -1)\n }\n if (cleanPath === cleanPrefix || cleanPath.startsWith(cleanPrefix + '/')) {\n // Path already starts with prefix\n } else {\n cleanPath = cleanPrefix + cleanPath\n }\n }\n return cleanPath.replace(/\\/+/g, '/')\n}\n\nexport interface ForeignSpecOptions {\n prefix?: string\n source?: string\n}\n\nexport function normalizeForeignOpenAPISpec(\n spec: Record<string, unknown>,\n options: ForeignSpecOptions = {},\n): Record<string, unknown> {\n const prefix = options.prefix\n const source = options.source\n const cloned: Record<string, unknown> = JSON.parse(JSON.stringify(spec))\n\n if (cloned.paths && typeof cloned.paths === 'object') {\n const normalizedPaths: Record<string, unknown> = {}\n for (const [routePath, pathItem] of Object.entries(\n cloned.paths as Record<string, unknown>,\n )) {\n const canonicalPath = normalizeApiPath(routePath, prefix)\n normalizedPaths[canonicalPath] = pathItem\n }\n cloned.paths = normalizedPaths\n }\n\n if (source) {\n cloned['x-bunderstack-source'] = source\n }\n\n return cloned\n}\n\nexport function mergeApiRoutersStrict(\n target: Record<string, unknown>,\n source?: Record<string, unknown>,\n prefix: string[] = [],\n): Record<string, unknown> {\n if (!source) return { ...target }\n const result: Record<string, unknown> = { ...target }\n\n for (const [key, val] of Object.entries(source)) {\n const currentPath = [...prefix, key]\n const handle = currentPath.join('.')\n const targetVal = result[key]\n\n if (targetVal !== undefined) {\n const isTargetNamespace =\n typeof targetVal === 'object' &&\n targetVal !== null &&\n !('~orpc' in targetVal)\n\n const isSourceNamespace =\n typeof val === 'object' && val !== null && !('~orpc' in val)\n\n if (isTargetNamespace && isSourceNamespace) {\n result[key] = mergeApiRoutersStrict(\n targetVal as Record<string, unknown>,\n val as Record<string, unknown>,\n currentPath,\n )\n } else {\n throw new Error(\n `[bunderstack] Router merge collision at handle \"${handle}\": duplicate procedure or namespace collision`,\n )\n }\n } else {\n result[key] = val\n }\n }\n\n return result\n}\n\nfunction normalizePathSegments(path: string): string[] {\n return path.split('/').filter(Boolean)\n}\n\nfunction isParamSegment(segment: string): boolean {\n return (\n (segment.startsWith('{') && segment.endsWith('}')) ||\n segment.startsWith(':')\n )\n}\n\nfunction normalizePathForCollision(path: string): string {\n const segments = normalizePathSegments(path)\n const normalized = segments.map((seg) =>\n isParamSegment(seg) ? '{param}' : seg,\n )\n return '/' + normalized.join('/')\n}\n\nfunction walkNativeRouter(\n obj: Record<string, unknown>,\n pathSegments: string[] = [],\n): ApiRegistryEntry[] {\n const entries: ApiRegistryEntry[] = []\n\n for (const [key, value] of Object.entries(obj)) {\n if (!value || typeof value !== 'object') continue\n const currentSegments = [...pathSegments, key]\n\n if ('~orpc' in value) {\n const meta = (getOpenAPIMeta(value as any) || {}) as Record<\n string,\n unknown\n >\n const handle = currentSegments.join('.')\n const method = ((meta.method as string) || 'GET').toUpperCase()\n const routePath = (meta.path as string) || '/' + currentSegments.join('/')\n const operationId =\n typeof meta.operationId === 'string' ? meta.operationId : handle\n\n entries.push({\n handle,\n operationId,\n method,\n path: routePath,\n source: 'native',\n })\n } else {\n entries.push(\n ...walkNativeRouter(value as Record<string, unknown>, currentSegments),\n )\n }\n }\n\n return entries\n}\n\nfunction extractForeignEntries(\n specs: Array<Record<string, unknown> | ForeignSpecEntry>,\n): ApiRegistryEntry[] {\n const entries: ApiRegistryEntry[] = []\n\n for (const item of specs) {\n if (!item) continue\n let specObj: Record<string, unknown>\n let source = 'foreign'\n\n if ('spec' in item && typeof item.spec === 'object' && item.spec !== null) {\n const entry = item as ForeignSpecEntry\n source = entry.source || 'foreign'\n specObj = normalizeForeignOpenAPISpec(entry.spec, {\n prefix: entry.prefix,\n source,\n })\n } else {\n const rawSpec = item as Record<string, unknown>\n source = (rawSpec['x-bunderstack-source'] as string) || 'foreign'\n specObj = normalizeForeignOpenAPISpec(rawSpec, { source })\n }\n\n if (!specObj || typeof specObj.paths !== 'object' || !specObj.paths)\n continue\n const paths = specObj.paths as Record<string, Record<string, unknown>>\n\n for (const [routePath, pathItem] of Object.entries(paths)) {\n if (!pathItem || typeof pathItem !== 'object') continue\n\n for (const [methodKey, operation] of Object.entries(pathItem)) {\n const uppercaseMethod = methodKey.toUpperCase()\n if (!HTTP_METHODS.has(uppercaseMethod)) continue\n if (!operation || typeof operation !== 'object') continue\n\n const op = operation as Record<string, unknown>\n const handle = `foreign:${source}:${uppercaseMethod}:${routePath}`\n const operationId =\n typeof op.operationId === 'string' ? op.operationId : handle\n\n entries.push({\n handle,\n operationId,\n method: uppercaseMethod,\n path: routePath,\n source,\n })\n }\n }\n }\n\n return entries\n}\n\nexport async function buildApiRegistry(\n options: BuildApiRegistryOptions = {},\n): Promise<ApiRegistry> {\n const entries: ApiRegistryEntry[] = []\n\n if (options.nativeRouter) {\n entries.push(...walkNativeRouter(options.nativeRouter))\n }\n\n if (options.foreignSpecs) {\n entries.push(...extractForeignEntries(options.foreignSpecs))\n }\n\n const errors: string[] = []\n\n for (const entry of entries) {\n if (entry.source !== 'native') continue\n if (options.reservedCoreHandles?.has(entry.handle)) continue\n const reason = collisionForBunderstackPath(entry.path)\n if (reason) {\n errors.push(\n `Reserved route collision on ${entry.method} ${entry.path}: native handle \"${entry.handle}\" conflicts because ${reason}`,\n )\n }\n }\n\n // Check duplicate handles\n const handleMap = new Map<string, ApiRegistryEntry>()\n for (const entry of entries) {\n const existing = handleMap.get(entry.handle)\n if (existing) {\n errors.push(\n `Duplicate handle \"${entry.handle}\": collision between ${existing.source} (${existing.method} ${existing.path}) and ${entry.source} (${entry.method} ${entry.path})`,\n )\n } else {\n handleMap.set(entry.handle, entry)\n }\n }\n\n // Check duplicate operation IDs\n const opIdMap = new Map<string, ApiRegistryEntry>()\n for (const entry of entries) {\n const existing = opIdMap.get(entry.operationId)\n if (existing) {\n errors.push(\n `Duplicate operation ID \"${entry.operationId}\": collision between ${existing.source} (${existing.handle}) and ${entry.source} (${entry.handle})`,\n )\n } else {\n opIdMap.set(entry.operationId, entry)\n }\n }\n\n // Check method/path collisions (exact and parameter ambiguity)\n const routePatternMap = new Map<string, ApiRegistryEntry>()\n for (const entry of entries) {\n const normalizedPath = normalizePathForCollision(entry.path)\n const routeKey = `${entry.method} ${normalizedPath}`\n\n const existing = routePatternMap.get(routeKey)\n if (existing) {\n if (existing.path === entry.path) {\n errors.push(\n `Exact method/path collision on ${entry.method} ${entry.path}: collision between ${existing.source} (${existing.handle}) and ${entry.source} (${entry.handle})`,\n )\n } else {\n errors.push(\n `Ambiguous parameter path collision on ${entry.method} (${existing.path} vs ${entry.path}): collision between ${existing.source} (${existing.handle}) and ${entry.source} (${entry.handle})`,\n )\n }\n } else {\n routePatternMap.set(routeKey, entry)\n }\n }\n\n if (errors.length > 0) {\n throw new Error(\n `[bunderstack] Route registry validation failed (${errors.length} error(s)):\\n` +\n errors.join('\\n'),\n )\n }\n\n return { entries }\n}\n"]}
|
package/dist/api/router.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import '@orpc/openapi/extensions/route';
|
|
2
2
|
import type { AnyMiddleware } from '@orpc/server';
|
|
3
|
+
import type { ReadinessReport } from '../readiness.js';
|
|
3
4
|
export interface BuildApiRouterOptions {
|
|
4
5
|
crud: Record<string, unknown>;
|
|
5
6
|
storage: Record<string, unknown>;
|
|
@@ -7,6 +8,8 @@ export interface BuildApiRouterOptions {
|
|
|
7
8
|
custom?: Record<string, unknown>;
|
|
8
9
|
/** Applied to every procedure in the graph, outermost first. */
|
|
9
10
|
middleware?: AnyMiddleware[];
|
|
11
|
+
/** Deployment readiness probe. Always answers 200; read `status` in the body. */
|
|
12
|
+
readiness: () => Promise<ReadinessReport>;
|
|
10
13
|
}
|
|
11
14
|
export declare function buildApiRouter(options: BuildApiRouterOptions): Record<string, unknown>;
|
|
12
15
|
//# sourceMappingURL=router.d.ts.map
|
package/dist/api/router.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/api/router.ts"],"names":[],"mappings":"AAAA,OAAO,gCAAgC,CAAA;AACvC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/api/router.ts"],"names":[],"mappings":"AAAA,OAAO,gCAAgC,CAAA;AACvC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAIjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAMnD,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC7B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAChC,gEAAgE;IAChE,UAAU,CAAC,EAAE,aAAa,EAAE,CAAA;IAC5B,iFAAiF;IACjF,SAAS,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,CAAA;CAC1C;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,qBAAqB,2BAsC5D"}
|
package/dist/api/router.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import '@orpc/openapi/extensions/route';
|
|
2
2
|
import * as v from 'valibot';
|
|
3
|
+
import { readinessReportSchema } from '../readiness.js';
|
|
3
4
|
import { createApiBuilder } from './builder.js';
|
|
4
5
|
import { mergeApiRoutersStrict } from './registry.js';
|
|
5
6
|
export function buildApiRouter(options) {
|
|
@@ -8,8 +9,12 @@ export function buildApiRouter(options) {
|
|
|
8
9
|
.route({ method: 'GET', path: '/api/health', tags: ['system'] })
|
|
9
10
|
.output(v.strictObject({ status: v.literal('ok') }))
|
|
10
11
|
.handler(() => ({ status: 'ok' }));
|
|
12
|
+
const readiness = builder.public
|
|
13
|
+
.route({ method: 'GET', path: '/api/readiness', tags: ['system'] })
|
|
14
|
+
.output(readinessReportSchema)
|
|
15
|
+
.handler(() => options.readiness());
|
|
11
16
|
const merged = [
|
|
12
|
-
{ health },
|
|
17
|
+
{ health, readiness },
|
|
13
18
|
options.crud,
|
|
14
19
|
options.storage,
|
|
15
20
|
options.realtime,
|
|
@@ -19,7 +24,7 @@ export function buildApiRouter(options) {
|
|
|
19
24
|
if (middleware.length === 0)
|
|
20
25
|
return merged;
|
|
21
26
|
// `.router()` applies the builder's middleware to every procedure inside, so
|
|
22
|
-
// one list covers CRUD, storage, realtime, health, and custom procedures
|
|
27
|
+
// one list covers CRUD, storage, realtime, health, readiness, and custom procedures
|
|
23
28
|
// instead of only the bases an application happens to declare.
|
|
24
29
|
const withMiddleware = middleware.reduce((acc, mw) => acc.use(mw), builder.public);
|
|
25
30
|
return withMiddleware.router(merged);
|
package/dist/api/router.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.js","sourceRoot":"","sources":["../../src/api/router.ts"],"names":[],"mappings":"AAAA,OAAO,gCAAgC,CAAA;AAGvC,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"router.js","sourceRoot":"","sources":["../../src/api/router.ts"],"names":[],"mappings":"AAAA,OAAO,gCAAgC,CAAA;AAGvC,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAI5B,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAA;AAC5C,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAalD,MAAM,UAAU,cAAc,CAAC,OAA8B;IAC3D,MAAM,OAAO,GAAG,gBAAgB,EAG7B,CAAA;IACH,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM;SAC1B,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC;SAC/D,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;SACnD,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,IAAa,EAAE,CAAC,CAAC,CAAA;IAE7C,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM;SAC7B,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC;SAClE,MAAM,CAAC,qBAAqB,CAAC;SAC7B,OAAO,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,CAAA;IAErC,MAAM,MAAM,GAAG;QACb,EAAE,MAAM,EAAE,SAAS,EAAE;QACrB,OAAO,CAAC,IAAI;QACZ,OAAO,CAAC,OAAO;QACf,OAAO,CAAC,QAAQ;QAChB,OAAO,CAAC,MAAM;KACf,CAAC,MAAM,CACN,CAAC,MAAM,EAAE,QAAQ,EAAE,EAAE,CAAC,qBAAqB,CAAC,MAAM,EAAE,QAAQ,CAAC,EAC7D,EAAE,CACH,CAAA;IAED,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,EAAE,CAAA;IAC3C,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,MAAM,CAAA;IAE1C,6EAA6E;IAC7E,oFAAoF;IACpF,+DAA+D;IAC/D,MAAM,cAAc,GAAG,UAAU,CAAC,MAAM,CACtC,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,EAAW,CAAC,EACjC,OAAO,CAAC,MAAM,CACf,CAAA;IAED,OAAO,cAAc,CAAC,MAAM,CAAC,MAAe,CAA4B,CAAA;AAC1E,CAAC","sourcesContent":["import '@orpc/openapi/extensions/route'\nimport type { AnyMiddleware } from '@orpc/server'\n\nimport * as v from 'valibot'\n\nimport type { ReadinessReport } from '../readiness'\n\nimport { readinessReportSchema } from '../readiness'\nimport { createApiBuilder } from './builder'\nimport { mergeApiRoutersStrict } from './registry'\n\nexport interface BuildApiRouterOptions {\n crud: Record<string, unknown>\n storage: Record<string, unknown>\n realtime?: Record<string, unknown>\n custom?: Record<string, unknown>\n /** Applied to every procedure in the graph, outermost first. */\n middleware?: AnyMiddleware[]\n /** Deployment readiness probe. Always answers 200; read `status` in the body. */\n readiness: () => Promise<ReadinessReport>\n}\n\nexport function buildApiRouter(options: BuildApiRouterOptions) {\n const builder = createApiBuilder<\n Record<string, unknown>,\n Record<string, unknown>\n >()\n const health = builder.public\n .route({ method: 'GET', path: '/api/health', tags: ['system'] })\n .output(v.strictObject({ status: v.literal('ok') }))\n .handler(() => ({ status: 'ok' as const }))\n\n const readiness = builder.public\n .route({ method: 'GET', path: '/api/readiness', tags: ['system'] })\n .output(readinessReportSchema)\n .handler(() => options.readiness())\n\n const merged = [\n { health, readiness },\n options.crud,\n options.storage,\n options.realtime,\n options.custom,\n ].reduce<Record<string, unknown>>(\n (router, addition) => mergeApiRoutersStrict(router, addition),\n {},\n )\n\n const middleware = options.middleware ?? []\n if (middleware.length === 0) return merged\n\n // `.router()` applies the builder's middleware to every procedure inside, so\n // one list covers CRUD, storage, realtime, health, readiness, and custom procedures\n // instead of only the bases an application happens to declare.\n const withMiddleware = middleware.reduce(\n (acc, mw) => acc.use(mw as never),\n builder.public,\n )\n\n return withMiddleware.router(merged as never) as Record<string, unknown>\n}\n"]}
|
package/dist/backend.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,IAAI,aAAa,EAAE,MAAM,cAAc,CAAA;AAE9D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"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"}
|
package/dist/backend.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { createApiBuilder } from './api/builder.js';
|
|
2
|
+
import { describeApiOperations } from './api/catalog.js';
|
|
1
3
|
import { BACKEND_INTERNALS } from './backend-internals.js';
|
|
2
4
|
import { detectDialect } from './dialect.js';
|
|
3
5
|
import { emailProviderTag } from './email.js';
|
|
@@ -18,6 +20,11 @@ export function bunderstack(config) {
|
|
|
18
20
|
: undefined;
|
|
19
21
|
if (jobsDefs)
|
|
20
22
|
validateJobsDefs(jobsDefs);
|
|
23
|
+
// Resolved here rather than at start: the manifest has to describe the same
|
|
24
|
+
// router the runtime will serve, and `bunderstack blueprint` never starts.
|
|
25
|
+
const customApiRouter = typeof config.api === 'function'
|
|
26
|
+
? config.api(createApiBuilder())
|
|
27
|
+
: config.api;
|
|
21
28
|
const manifest = buildManifest({
|
|
22
29
|
schema: config.schema,
|
|
23
30
|
dialect,
|
|
@@ -26,6 +33,7 @@ export function bunderstack(config) {
|
|
|
26
33
|
envConfig: config.env,
|
|
27
34
|
emailProvider: emailProviderTag(config.email),
|
|
28
35
|
realtime: Boolean(config.realtime),
|
|
36
|
+
api: describeApiOperations(customApiRouter),
|
|
29
37
|
jobs: config.storage
|
|
30
38
|
? {
|
|
31
39
|
...jobsDefs,
|
|
@@ -37,7 +45,7 @@ export function bunderstack(config) {
|
|
|
37
45
|
}
|
|
38
46
|
: jobsDefs,
|
|
39
47
|
});
|
|
40
|
-
const start = (source, overrides = {}) => materializeBunderstack({ ...config, jobs: jobsDefs }, source, overrides);
|
|
48
|
+
const start = (source, overrides = {}) => materializeBunderstack({ ...config, jobs: jobsDefs, api: customApiRouter }, source, overrides);
|
|
41
49
|
let backend;
|
|
42
50
|
const test = (async (options) => {
|
|
43
51
|
const testing = await import('./testing.js');
|
package/dist/backend.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backend.js","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"backend.js","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AnyRouter as AnyORPCRouter } from '@orpc/server'\n\nimport type { TableAccessInput } from './access'\nimport type { BunderstackApiBuilder } from './api/builder'\nimport type { BunderstackConfig } from './config'\nimport type { EnvConfigInput, ValidatedEnv } from './env'\nimport type { BunderstackJobsBuilder, JobsDefs } from './jobs'\nimport type { BunderstackManifest } from './manifest'\nimport type { BunderstackApp, BucketNamesOf, RuntimeOverrides } from './runtime'\nimport type { StorageConfigInput } from './storage/buckets'\nimport type { TestMethod, TestOptions } from './testing/fixture'\n\nimport { createApiBuilder } from './api/builder'\nimport { describeApiOperations } from './api/catalog'\nimport { BACKEND_INTERNALS, type BackendInternals } from './backend-internals'\nimport { detectDialect } from './dialect'\nimport { emailProviderTag } from './email'\nimport { createJobsBuilder, validateJobsDefs } from './jobs'\nimport { buildManifest } from './manifest'\nimport { materializeBunderstack } from './runtime'\nimport {\n STORAGE_SWEEP_JOB_NAME,\n STORAGE_SWEEP_SCHEDULE,\n} from './storage/background'\nimport { resolveBuckets } from './storage/buckets'\n\nexport type StartOptions = {\n env?: Record<string, string | undefined>\n}\n\nexport type BunderstackBackend<TApp> = {\n readonly manifest: BunderstackManifest\n start(options?: StartOptions): Promise<TApp>\n test: TestMethod<TApp>\n readonly [BACKEND_INTERNALS]: BackendInternals<TApp>\n}\n\nexport type BunderstackDefinitionConfig<\n TSchema extends Record<string, unknown>,\n TAccess extends Record<string, TableAccessInput> | undefined = undefined,\n TStorage extends StorageConfigInput | undefined = undefined,\n TEnv extends EnvConfigInput | undefined = undefined,\n TJobsDefs extends JobsDefs | undefined = undefined,\n TCustomApiRouter extends AnyORPCRouter | undefined = undefined,\n TRealtime = undefined,\n> = Omit<\n BunderstackConfig<TSchema, TAccess, TStorage, TEnv, TCustomApiRouter>,\n 'realtime'\n> & {\n realtime?: TRealtime\n jobs?:\n | TJobsDefs\n | ((\n builder: BunderstackJobsBuilder<TSchema, ValidatedEnv<TEnv>>,\n ) => TJobsDefs)\n}\n\nexport function isBunderstackBackend(\n value: unknown,\n): value is BunderstackBackend<unknown> {\n return (\n typeof value === 'object' && value !== null && BACKEND_INTERNALS in value\n )\n}\n\nexport function bunderstack<\n TSchema extends Record<string, unknown>,\n const TAccess extends Record<string, TableAccessInput> | undefined =\n undefined,\n const TStorage extends StorageConfigInput | undefined = undefined,\n const TEnv extends EnvConfigInput | undefined = undefined,\n const TJobsDefs extends JobsDefs | undefined = undefined,\n TCustomApiRouter extends AnyORPCRouter | undefined = undefined,\n const TRealtime extends BunderstackConfig<\n TSchema,\n TAccess,\n TStorage,\n TEnv,\n TCustomApiRouter\n >['realtime'] = undefined,\n>(\n config: BunderstackDefinitionConfig<\n TSchema,\n TAccess,\n TStorage,\n TEnv,\n TJobsDefs,\n TCustomApiRouter,\n TRealtime\n >,\n): BunderstackBackend<\n BunderstackApp<\n TSchema,\n TAccess,\n BucketNamesOf<TStorage>,\n TEnv,\n TJobsDefs,\n TCustomApiRouter,\n TRealtime\n >\n> {\n const dialect = detectDialect(config.schema)\n const jobsDefs = config.jobs\n ? typeof config.jobs === 'function'\n ? config.jobs(createJobsBuilder<TSchema, ValidatedEnv<TEnv>>())\n : config.jobs\n : undefined\n if (jobsDefs) validateJobsDefs(jobsDefs)\n\n // Resolved here rather than at start: the manifest has to describe the same\n // router the runtime will serve, and `bunderstack blueprint` never starts.\n const customApiRouter =\n typeof config.api === 'function'\n ? (\n config.api as (\n builder: BunderstackApiBuilder<TSchema, ValidatedEnv<TEnv>>,\n ) => TCustomApiRouter\n )(createApiBuilder<TSchema, ValidatedEnv<TEnv>>())\n : config.api\n\n const manifest = buildManifest({\n schema: config.schema,\n dialect,\n migrationsDirectory: config.database.migrations ?? './migrations',\n storage: resolveBuckets(config.storage, {}),\n envConfig: config.env,\n emailProvider: emailProviderTag(config.email),\n realtime: Boolean(config.realtime),\n api: describeApiOperations(customApiRouter),\n jobs: config.storage\n ? {\n ...jobsDefs,\n [STORAGE_SWEEP_JOB_NAME]: {\n kind: 'cron',\n schedule: STORAGE_SWEEP_SCHEDULE,\n handler: () => {},\n },\n }\n : jobsDefs,\n })\n\n type App = BunderstackApp<\n TSchema,\n TAccess,\n BucketNamesOf<TStorage>,\n TEnv,\n TJobsDefs,\n TCustomApiRouter,\n TRealtime\n >\n\n const start = (\n source: Record<string, string | undefined>,\n overrides: RuntimeOverrides = {},\n ): Promise<App> =>\n materializeBunderstack(\n { ...config, jobs: jobsDefs, api: customApiRouter } as never,\n source,\n overrides,\n ) as Promise<App>\n\n let backend: BunderstackBackend<App>\n const test = (async (options) => {\n const testing = await import('./testing')\n return testing.createTestApp(backend, options)\n }) as TestMethod<App>\n test.configure = (options) =>\n (async (overrides: TestOptions = {}) => {\n const testing = await import('./testing')\n return testing.configureTestApp(backend, options)(overrides)\n }) as never\n backend = {\n manifest,\n start: ({ env } = {}) =>\n start(env ?? (process.env as Record<string, string | undefined>)),\n test,\n [BACKEND_INTERNALS]: { declaration: { config, jobsDefs }, start },\n }\n return backend\n}\n"]}
|
package/dist/blueprint.d.ts
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import type { BunderstackManifest } from './manifest.js';
|
|
2
2
|
export type MigrationMode = 'migrations' | 'push';
|
|
3
3
|
export type ApplicationFramework = 'tanstack-start' | 'solid' | 'bun-ssr' | 'custom';
|
|
4
|
+
/**
|
|
5
|
+
* Blueprint form of an environment key. `sensitive` and `description` are
|
|
6
|
+
* optional because blueprints committed before 0.23.0 do not carry them.
|
|
7
|
+
*/
|
|
8
|
+
export type BlueprintEnvVar = {
|
|
9
|
+
key: string;
|
|
10
|
+
required: boolean;
|
|
11
|
+
scope: 'server' | 'client';
|
|
12
|
+
sensitive?: boolean;
|
|
13
|
+
description?: string;
|
|
14
|
+
};
|
|
4
15
|
export type BunderstackBlueprint = {
|
|
5
16
|
version: 1;
|
|
6
17
|
generator: {
|
|
@@ -28,7 +39,11 @@ export type BunderstackBlueprint = {
|
|
|
28
39
|
required: true;
|
|
29
40
|
};
|
|
30
41
|
};
|
|
31
|
-
environment:
|
|
42
|
+
environment: BlueprintEnvVar[];
|
|
43
|
+
/** Application-declared procedures. Absent in blueprints written before 0.23.0. */
|
|
44
|
+
api?: {
|
|
45
|
+
operations: BunderstackManifest['api']['operations'];
|
|
46
|
+
};
|
|
32
47
|
background: BunderstackManifest['background'] & {
|
|
33
48
|
worker: {
|
|
34
49
|
required: boolean;
|
|
@@ -36,6 +51,11 @@ export type BunderstackBlueprint = {
|
|
|
36
51
|
};
|
|
37
52
|
};
|
|
38
53
|
export declare function parseBlueprint(value: unknown): BunderstackBlueprint;
|
|
54
|
+
/**
|
|
55
|
+
* Whether a host must treat this key as a secret. A blueprint generated before
|
|
56
|
+
* 0.23.0 has no flag, and server keys are secret by default.
|
|
57
|
+
*/
|
|
58
|
+
export declare function isSensitiveEnvVar(entry: BlueprintEnvVar): boolean;
|
|
39
59
|
export declare function blueprintFromManifest(args: {
|
|
40
60
|
manifest: BunderstackManifest;
|
|
41
61
|
generatorVersion: string;
|
package/dist/blueprint.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"blueprint.d.ts","sourceRoot":"","sources":["../src/blueprint.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAKrD,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,MAAM,CAAA;AACjD,MAAM,MAAM,oBAAoB,GAC5B,gBAAgB,GAChB,OAAO,GACP,SAAS,GACT,QAAQ,CAAA;AAEZ,MAAM,MAAM,oBAAoB,GAAG;IACjC,OAAO,EAAE,CAAC,CAAA;IACV,SAAS,EAAE;QAAE,IAAI,EAAE,aAAa,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;IACnD,WAAW,EAAE;QACX,SAAS,EAAE,oBAAoB,CAAA;QAC/B,OAAO,EAAE;YAAE,KAAK,EAAE,OAAO,CAAC;YAAC,KAAK,EAAE,OAAO,CAAC;YAAC,MAAM,CAAC,EAAE,QAAQ,CAAA;SAAE,CAAA;KAC/D,CAAA;IACD,WAAW,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,CAAC,CAAA;KAAE,CAAA;IAClD,SAAS,EAAE;QACT,QAAQ,EAAE,mBAAmB,CAAC,UAAU,CAAC,GAAG;YAAE,aAAa,EAAE,aAAa,CAAA;SAAE,CAAA;QAC5E,OAAO,EAAE,mBAAmB,CAAC,SAAS,CAAC,CAAA;QACvC,QAAQ,CAAC,EAAE;YAAE,QAAQ,EAAE,IAAI,CAAA;SAAE,CAAA;KAC9B,CAAA;IACD,WAAW,EAAE,mBAAmB,CAAC,
|
|
1
|
+
{"version":3,"file":"blueprint.d.ts","sourceRoot":"","sources":["../src/blueprint.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAKrD,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,MAAM,CAAA;AACjD,MAAM,MAAM,oBAAoB,GAC5B,gBAAgB,GAChB,OAAO,GACP,SAAS,GACT,QAAQ,CAAA;AAEZ;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,OAAO,CAAA;IACjB,KAAK,EAAE,QAAQ,GAAG,QAAQ,CAAA;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,OAAO,EAAE,CAAC,CAAA;IACV,SAAS,EAAE;QAAE,IAAI,EAAE,aAAa,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;IACnD,WAAW,EAAE;QACX,SAAS,EAAE,oBAAoB,CAAA;QAC/B,OAAO,EAAE;YAAE,KAAK,EAAE,OAAO,CAAC;YAAC,KAAK,EAAE,OAAO,CAAC;YAAC,MAAM,CAAC,EAAE,QAAQ,CAAA;SAAE,CAAA;KAC/D,CAAA;IACD,WAAW,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,CAAC,CAAA;KAAE,CAAA;IAClD,SAAS,EAAE;QACT,QAAQ,EAAE,mBAAmB,CAAC,UAAU,CAAC,GAAG;YAAE,aAAa,EAAE,aAAa,CAAA;SAAE,CAAA;QAC5E,OAAO,EAAE,mBAAmB,CAAC,SAAS,CAAC,CAAA;QACvC,QAAQ,CAAC,EAAE;YAAE,QAAQ,EAAE,IAAI,CAAA;SAAE,CAAA;KAC9B,CAAA;IACD,WAAW,EAAE,eAAe,EAAE,CAAA;IAC9B,mFAAmF;IACnF,GAAG,CAAC,EAAE;QAAE,UAAU,EAAE,mBAAmB,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAA;KAAE,CAAA;IAC9D,UAAU,EAAE,mBAAmB,CAAC,YAAY,CAAC,GAAG;QAC9C,MAAM,EAAE;YAAE,QAAQ,EAAE,OAAO,CAAA;SAAE,CAAA;KAC9B,CAAA;CACF,CAAA;AAkID,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,oBAAoB,CA4DnE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAEjE;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE;IAC1C,QAAQ,EAAE,mBAAmB,CAAA;IAC7B,gBAAgB,EAAE,MAAM,CAAA;IACxB,KAAK,EAAE,MAAM,CAAA;IACb,aAAa,EAAE,aAAa,CAAA;IAC5B,SAAS,CAAC,EAAE,oBAAoB,CAAA;CACjC,GAAG,oBAAoB,CA2CvB;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,oBAAoB,CAEvE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,oBAAoB,GAAG,MAAM,CAmBtE"}
|
package/dist/blueprint.js
CHANGED
|
@@ -15,58 +15,76 @@ const cronSchedule = v.pipe(nonEmpty, v.check((value) => {
|
|
|
15
15
|
return false;
|
|
16
16
|
}
|
|
17
17
|
}, 'invalid cron schedule'));
|
|
18
|
-
|
|
18
|
+
// Hosts parse blueprints committed by applications that upgrade bunderstack on
|
|
19
|
+
// their own schedule, so a section written by a newer generator has to survive
|
|
20
|
+
// an older parser instead of failing the whole file.
|
|
21
|
+
function open(entries) {
|
|
22
|
+
return v.objectWithRest(entries, v.unknown());
|
|
23
|
+
}
|
|
24
|
+
const blueprintSchema = open({
|
|
19
25
|
version: v.literal(1),
|
|
20
|
-
generator:
|
|
26
|
+
generator: open({
|
|
21
27
|
name: v.literal('bunderstack'),
|
|
22
28
|
version: nonEmpty,
|
|
23
29
|
}),
|
|
24
|
-
application:
|
|
30
|
+
application: open({
|
|
25
31
|
framework: v.picklist(['tanstack-start', 'solid', 'bun-ssr', 'custom']),
|
|
26
|
-
scripts:
|
|
32
|
+
scripts: open({
|
|
27
33
|
build: v.literal('build'),
|
|
28
34
|
start: v.literal('start'),
|
|
29
35
|
worker: v.optional(v.literal('worker')),
|
|
30
36
|
}),
|
|
31
37
|
}),
|
|
32
|
-
bunderstack:
|
|
38
|
+
bunderstack: open({
|
|
33
39
|
entry: relativePath,
|
|
34
40
|
manifestVersion: v.literal(3),
|
|
35
41
|
}),
|
|
36
|
-
resources:
|
|
37
|
-
database:
|
|
42
|
+
resources: open({
|
|
43
|
+
database: open({
|
|
38
44
|
dialect: v.picklist(['sqlite', 'pg']),
|
|
39
45
|
migrationsDirectory: relativePath,
|
|
40
46
|
migrationMode: v.picklist(['migrations', 'push']),
|
|
41
|
-
tables: v.array(
|
|
47
|
+
tables: v.array(open({
|
|
42
48
|
exportName: nonEmpty,
|
|
43
49
|
physicalName: nonEmpty,
|
|
44
50
|
system: v.boolean(),
|
|
45
51
|
})),
|
|
46
52
|
}),
|
|
47
|
-
storage:
|
|
53
|
+
storage: open({
|
|
48
54
|
defaultBucket: nonEmpty,
|
|
49
|
-
buckets: v.array(
|
|
55
|
+
buckets: v.array(open({
|
|
50
56
|
name: nonEmpty,
|
|
51
57
|
visibility: v.picklist(['public', 'private']),
|
|
52
58
|
})),
|
|
53
59
|
}),
|
|
54
|
-
realtime: v.optional(
|
|
60
|
+
realtime: v.optional(open({ required: v.literal(true) })),
|
|
55
61
|
}),
|
|
56
|
-
environment: v.array(
|
|
62
|
+
environment: v.array(open({
|
|
57
63
|
key: nonEmpty,
|
|
58
64
|
required: v.boolean(),
|
|
59
65
|
scope: v.picklist(['server', 'client']),
|
|
66
|
+
sensitive: v.optional(v.boolean()),
|
|
67
|
+
description: v.optional(v.pipe(nonEmpty, v.maxLength(200))),
|
|
68
|
+
})),
|
|
69
|
+
api: v.optional(open({
|
|
70
|
+
operations: v.array(open({
|
|
71
|
+
handle: nonEmpty,
|
|
72
|
+
operationId: nonEmpty,
|
|
73
|
+
effect: v.picklist(['read', 'mutation', 'unknown']),
|
|
74
|
+
method: v.optional(nonEmpty),
|
|
75
|
+
path: v.optional(nonEmpty),
|
|
76
|
+
summary: v.optional(v.pipe(nonEmpty, v.maxLength(200))),
|
|
77
|
+
})),
|
|
60
78
|
})),
|
|
61
|
-
background:
|
|
62
|
-
worker:
|
|
63
|
-
jobs: v.array(
|
|
64
|
-
cron: v.array(
|
|
79
|
+
background: open({
|
|
80
|
+
worker: open({ required: v.boolean() }),
|
|
81
|
+
jobs: v.array(open({ name: nonEmpty })),
|
|
82
|
+
cron: v.array(open({
|
|
65
83
|
name: nonEmpty,
|
|
66
84
|
schedule: cronSchedule,
|
|
67
85
|
timezone: v.literal('UTC'),
|
|
68
86
|
})),
|
|
69
|
-
maintenance: v.array(
|
|
87
|
+
maintenance: v.array(open({
|
|
70
88
|
name: v.literal('storage-sweep'),
|
|
71
89
|
schedule: cronSchedule,
|
|
72
90
|
timezone: v.literal('UTC'),
|
|
@@ -90,6 +108,8 @@ export function parseBlueprint(value) {
|
|
|
90
108
|
rejectDuplicates('database export table', blueprint.resources.database.tables.map((entry) => entry.exportName));
|
|
91
109
|
rejectDuplicates('storage bucket', blueprint.resources.storage.buckets.map((entry) => entry.name));
|
|
92
110
|
rejectDuplicates('environment key', blueprint.environment.map((entry) => entry.key));
|
|
111
|
+
if (blueprint.api)
|
|
112
|
+
rejectDuplicates('api operation', blueprint.api.operations.map((entry) => entry.handle));
|
|
93
113
|
rejectDuplicates('background job', blueprint.background.jobs.map((entry) => entry.name));
|
|
94
114
|
rejectDuplicates('background cron', blueprint.background.cron.map((entry) => entry.name));
|
|
95
115
|
rejectDuplicates('background maintenance', blueprint.background.maintenance.map((entry) => entry.name));
|
|
@@ -105,6 +125,13 @@ export function parseBlueprint(value) {
|
|
|
105
125
|
}
|
|
106
126
|
return blueprint;
|
|
107
127
|
}
|
|
128
|
+
/**
|
|
129
|
+
* Whether a host must treat this key as a secret. A blueprint generated before
|
|
130
|
+
* 0.23.0 has no flag, and server keys are secret by default.
|
|
131
|
+
*/
|
|
132
|
+
export function isSensitiveEnvVar(entry) {
|
|
133
|
+
return entry.sensitive ?? entry.scope === 'server';
|
|
134
|
+
}
|
|
108
135
|
export function blueprintFromManifest(args) {
|
|
109
136
|
const workerRequired = args.manifest.background.jobs.length > 0;
|
|
110
137
|
return parseBlueprint({
|
|
@@ -134,6 +161,7 @@ export function blueprintFromManifest(args) {
|
|
|
134
161
|
: {}),
|
|
135
162
|
},
|
|
136
163
|
environment: sortBy(args.manifest.environment, (entry) => entry.key),
|
|
164
|
+
api: { operations: args.manifest.api.operations },
|
|
137
165
|
background: {
|
|
138
166
|
worker: { required: workerRequired },
|
|
139
167
|
jobs: sortBy(args.manifest.background.jobs, (entry) => entry.name),
|