bunderstack 0.22.1 → 0.23.0
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 +28 -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/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.map +1 -1
- package/dist/runtime.js +13 -0
- package/dist/runtime.js.map +1 -1
- package/package.json +1 -1
package/dist/blueprint.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"blueprint.js","sourceRoot":"","sources":["../src/blueprint.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAC5B,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,MAAM,CAAA;AAIvC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACvC,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAA;AA4B1D,MAAM,QAAQ,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAA;AACnD,MAAM,YAAY,GAAG,CAAC,CAAC,IAAI,CACzB,QAAQ,EACR,CAAC,CAAC,KAAK,CACL,CAAC,KAAK,EAAE,EAAE,CACR,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC;IACtB,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;IACrB,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,EAAE,IAAI,IAAI,KAAK,IAAI,CAAC,EAChE,iDAAiD,CAClD,CACF,CAAA;AACD,MAAM,YAAY,GAAG,CAAC,CAAC,IAAI,CACzB,QAAQ,EACR,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IAChB,IAAI,CAAC;QACH,SAAS,CAAC,KAAK,CAAC,CAAA;QAChB,OAAO,IAAI,CAAA;IACb,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC,EAAE,uBAAuB,CAAC,CAC5B,CAAA;AAED,MAAM,eAAe,GAAG,CAAC,CAAC,YAAY,CAAC;IACrC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACrB,SAAS,EAAE,CAAC,CAAC,YAAY,CAAC;QACxB,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC;QAC9B,OAAO,EAAE,QAAQ;KAClB,CAAC;IACF,WAAW,EAAE,CAAC,CAAC,YAAY,CAAC;QAC1B,SAAS,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,gBAAgB,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;QACvE,OAAO,EAAE,CAAC,CAAC,YAAY,CAAC;YACtB,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;YACzB,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;YACzB,MAAM,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;SACxC,CAAC;KACH,CAAC;IACF,WAAW,EAAE,CAAC,CAAC,YAAY,CAAC;QAC1B,KAAK,EAAE,YAAY;QACnB,eAAe,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;KAC9B,CAAC;IACF,SAAS,EAAE,CAAC,CAAC,YAAY,CAAC;QACxB,QAAQ,EAAE,CAAC,CAAC,YAAY,CAAC;YACvB,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;YACrC,mBAAmB,EAAE,YAAY;YACjC,aAAa,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;YACjD,MAAM,EAAE,CAAC,CAAC,KAAK,CACb,CAAC,CAAC,YAAY,CAAC;gBACb,UAAU,EAAE,QAAQ;gBACpB,YAAY,EAAE,QAAQ;gBACtB,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE;aACpB,CAAC,CACH;SACF,CAAC;QACF,OAAO,EAAE,CAAC,CAAC,YAAY,CAAC;YACtB,aAAa,EAAE,QAAQ;YACvB,OAAO,EAAE,CAAC,CAAC,KAAK,CACd,CAAC,CAAC,YAAY,CAAC;gBACb,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;aAC9C,CAAC,CACH;SACF,CAAC;QACF,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;KACpE,CAAC;IACF,WAAW,EAAE,CAAC,CAAC,KAAK,CAClB,CAAC,CAAC,YAAY,CAAC;QACb,GAAG,EAAE,QAAQ;QACb,QAAQ,EAAE,CAAC,CAAC,OAAO,EAAE;QACrB,KAAK,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;KACxC,CAAC,CACH;IACD,UAAU,EAAE,CAAC,CAAC,YAAY,CAAC;QACzB,MAAM,EAAE,CAAC,CAAC,YAAY,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;QACjD,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;QACjD,IAAI,EAAE,CAAC,CAAC,KAAK,CACX,CAAC,CAAC,YAAY,CAAC;YACb,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,YAAY;YACtB,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;SAC3B,CAAC,CACH;QACD,WAAW,EAAE,CAAC,CAAC,KAAK,CAClB,CAAC,CAAC,YAAY,CAAC;YACb,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC;YAChC,QAAQ,EAAE,YAAY;YACtB,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;SAC3B,CAAC,CACH;KACF,CAAC;CACH,CAAC,CAAA;AAEF,SAAS,MAAM,CAAI,OAAqB,EAAE,GAAyB;IACjE,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;AAChF,CAAC;AAED,SAAS,gBAAgB,CAAC,UAAkB,EAAE,MAAyB;IACrE,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAA;IAC9B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,2BAA2B,UAAU,KAAK,KAAK,GAAG,CAAC,CAAA;QACrE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;IACjB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,MAAM,SAAS,GAAG,sBAAsB,CACtC,eAAe,EACf,KAAK,EACL,WAAW,CACY,CAAA;IACzB,gBAAgB,CACd,yBAAyB,EACzB,SAAS,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CACvE,CAAA;IACD,gBAAgB,CACd,uBAAuB,EACvB,SAAS,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,CACrE,CAAA;IACD,gBAAgB,CACd,gBAAgB,EAChB,SAAS,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAC/D,CAAA;IACD,gBAAgB,CACd,iBAAiB,EACjB,SAAS,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAChD,CAAA;IACD,gBAAgB,CACd,gBAAgB,EAChB,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CACrD,CAAA;IACD,gBAAgB,CACd,iBAAiB,EACjB,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CACrD,CAAA;IACD,gBAAgB,CACd,wBAAwB,EACxB,SAAS,CAAC,UAAU,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAC5D,CAAA;IACD,IACE,CAAC,SAAS,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CACvC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,SAAS,CAAC,SAAS,CAAC,OAAO,CAAC,aAAa,CACtE,EACD,CAAC;QACD,MAAM,IAAI,KAAK,CACb,yEAAyE,CAC1E,CAAA;IACH,CAAC;IACD,MAAM,cAAc,GAAG,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAA;IAC3D,IAAI,SAAS,CAAC,UAAU,CAAC,MAAM,CAAC,QAAQ,KAAK,cAAc,EAAE,CAAC;QAC5D,MAAM,IAAI,KAAK,CACb,yEAAyE,CAC1E,CAAA;IACH,CAAC;IACD,IAAI,OAAO,CAAC,SAAS,CAAC,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,cAAc,EAAE,CAAC;QACrE,MAAM,IAAI,KAAK,CACb,wEAAwE,CACzE,CAAA;IACH,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,IAMrC;IACC,MAAM,cAAc,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAA;IAC/D,OAAO,cAAc,CAAC;QACpB,OAAO,EAAE,CAAC;QACV,SAAS,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC,gBAAgB,EAAE;QAClE,WAAW,EAAE;YACX,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,gBAAgB;YAC7C,OAAO,EAAE;gBACP,KAAK,EAAE,OAAO;gBACd,KAAK,EAAE,OAAO;gBACd,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAChD;SACF;QACD,WAAW,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,eAAe,EAAE,CAAC,EAAE;QACtD,SAAS,EAAE;YACT,QAAQ,EAAE;gBACR,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ;gBACzB,aAAa,EAAE,IAAI,CAAC,aAAa;gBACjC,MAAM,EAAE,MAAM,CACZ,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAC7B,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,YAAY,CAC9B;aACF;YACD,OAAO,EAAE;gBACP,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO;gBACxB,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC;aACtE;YACD,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ;gBACjC,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE;gBAClC,CAAC,CAAC,EAAE,CAAC;SACR;QACD,WAAW,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC;QACpE,UAAU,EAAE;YACV,MAAM,EAAE,EAAE,QAAQ,EAAE,cAAc,EAAE;YACpC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC;YAClE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC;YAClE,WAAW,EAAE,MAAM,CACjB,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,WAAW,EACpC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CACtB;SACF;KACF,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,MAAc;IAC/C,OAAO,cAAc,CAAC,KAAK,CAAC,MAAM,CAAY,CAAC,CAAA;AACjD,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,KAA2B;IAC5D,MAAM,SAAS,GAAG,cAAc,CAAC,KAAK,CAAC,CAAA;IACvC,MAAM,OAAO,GAAG;QACd,qBAAqB,EAAE,KAAK;QAC5B,cAAc,EAAE,OAAO;QACvB,iBAAiB,EAAE,OAAO;QAC1B,SAAS,EAAE,CAAC;KACJ,CAAA;IACV,MAAM,MAAM,GAAG,SAAS,CACtB,kBAAkB,CAAC,SAAS,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,EACjD,OAAO,CACR,CAAA;IACD,OAAO,MAAM,CAAC,OAAO,CACnB,yBAAyB,EACzB,CAAC,MAAM,EAAE,MAAc,EAAE,KAAa,EAAE,EAAE;QACxC,MAAM,QAAQ,GAAG,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;QAClE,OAAO,GAAG,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAA;IAC/C,CAAC,CACF,CAAA;AACH,CAAC","sourcesContent":["import * as v from 'valibot'\nimport { parse, stringify } from 'yaml'\n\nimport type { BunderstackManifest } from './manifest'\n\nimport { parseCron } from './jobs/cron'\nimport { validateStandardSchema } from './standard-schema'\n\nexport type MigrationMode = 'migrations' | 'push'\nexport type ApplicationFramework =\n | 'tanstack-start'\n | 'solid'\n | 'bun-ssr'\n | 'custom'\n\nexport type BunderstackBlueprint = {\n version: 1\n generator: { name: 'bunderstack'; version: string }\n application: {\n framework: ApplicationFramework\n scripts: { build: 'build'; start: 'start'; worker?: 'worker' }\n }\n bunderstack: { entry: string; manifestVersion: 3 }\n resources: {\n database: BunderstackManifest['database'] & { migrationMode: MigrationMode }\n storage: BunderstackManifest['storage']\n realtime?: { required: true }\n }\n environment: BunderstackManifest['environment']\n background: BunderstackManifest['background'] & {\n worker: { required: boolean }\n }\n}\n\nconst nonEmpty = v.pipe(v.string(), v.minLength(1))\nconst relativePath = v.pipe(\n nonEmpty,\n v.check(\n (value) =>\n !value.startsWith('/') &&\n !value.includes('\\\\') &&\n value.split('/').every((part) => part !== '' && part !== '..'),\n 'entry must be a relative path without traversal',\n ),\n)\nconst cronSchedule = v.pipe(\n nonEmpty,\n v.check((value) => {\n try {\n parseCron(value)\n return true\n } catch {\n return false\n }\n }, 'invalid cron schedule'),\n)\n\nconst blueprintSchema = v.strictObject({\n version: v.literal(1),\n generator: v.strictObject({\n name: v.literal('bunderstack'),\n version: nonEmpty,\n }),\n application: v.strictObject({\n framework: v.picklist(['tanstack-start', 'solid', 'bun-ssr', 'custom']),\n scripts: v.strictObject({\n build: v.literal('build'),\n start: v.literal('start'),\n worker: v.optional(v.literal('worker')),\n }),\n }),\n bunderstack: v.strictObject({\n entry: relativePath,\n manifestVersion: v.literal(3),\n }),\n resources: v.strictObject({\n database: v.strictObject({\n dialect: v.picklist(['sqlite', 'pg']),\n migrationsDirectory: relativePath,\n migrationMode: v.picklist(['migrations', 'push']),\n tables: v.array(\n v.strictObject({\n exportName: nonEmpty,\n physicalName: nonEmpty,\n system: v.boolean(),\n }),\n ),\n }),\n storage: v.strictObject({\n defaultBucket: nonEmpty,\n buckets: v.array(\n v.strictObject({\n name: nonEmpty,\n visibility: v.picklist(['public', 'private']),\n }),\n ),\n }),\n realtime: v.optional(v.strictObject({ required: v.literal(true) })),\n }),\n environment: v.array(\n v.strictObject({\n key: nonEmpty,\n required: v.boolean(),\n scope: v.picklist(['server', 'client']),\n }),\n ),\n background: v.strictObject({\n worker: v.strictObject({ required: v.boolean() }),\n jobs: v.array(v.strictObject({ name: nonEmpty })),\n cron: v.array(\n v.strictObject({\n name: nonEmpty,\n schedule: cronSchedule,\n timezone: v.literal('UTC'),\n }),\n ),\n maintenance: v.array(\n v.strictObject({\n name: v.literal('storage-sweep'),\n schedule: cronSchedule,\n timezone: v.literal('UTC'),\n }),\n ),\n }),\n})\n\nfunction sortBy<T>(entries: readonly T[], key: (entry: T) => string): T[] {\n return [...entries].sort((left, right) => key(left).localeCompare(key(right)))\n}\n\nfunction rejectDuplicates(collection: string, values: readonly string[]): void {\n const seen = new Set<string>()\n for (const value of values) {\n if (seen.has(value))\n throw new Error(`[bunderstack] duplicate ${collection} \"${value}\"`)\n seen.add(value)\n }\n}\n\nexport function parseBlueprint(value: unknown): BunderstackBlueprint {\n const blueprint = validateStandardSchema(\n blueprintSchema,\n value,\n 'blueprint',\n ) as BunderstackBlueprint\n rejectDuplicates(\n 'database physical table',\n blueprint.resources.database.tables.map((entry) => entry.physicalName),\n )\n rejectDuplicates(\n 'database export table',\n blueprint.resources.database.tables.map((entry) => entry.exportName),\n )\n rejectDuplicates(\n 'storage bucket',\n blueprint.resources.storage.buckets.map((entry) => entry.name),\n )\n rejectDuplicates(\n 'environment key',\n blueprint.environment.map((entry) => entry.key),\n )\n rejectDuplicates(\n 'background job',\n blueprint.background.jobs.map((entry) => entry.name),\n )\n rejectDuplicates(\n 'background cron',\n blueprint.background.cron.map((entry) => entry.name),\n )\n rejectDuplicates(\n 'background maintenance',\n blueprint.background.maintenance.map((entry) => entry.name),\n )\n if (\n !blueprint.resources.storage.buckets.some(\n (bucket) => bucket.name === blueprint.resources.storage.defaultBucket,\n )\n ) {\n throw new Error(\n '[bunderstack] storage defaultBucket must be declared in storage buckets',\n )\n }\n const workerRequired = blueprint.background.jobs.length > 0\n if (blueprint.background.worker.required !== workerRequired) {\n throw new Error(\n '[bunderstack] background worker.required must match declared queue jobs',\n )\n }\n if (Boolean(blueprint.application.scripts.worker) !== workerRequired) {\n throw new Error(\n '[bunderstack] application worker script must match declared queue jobs',\n )\n }\n return blueprint\n}\n\nexport function blueprintFromManifest(args: {\n manifest: BunderstackManifest\n generatorVersion: string\n entry: string\n migrationMode: MigrationMode\n framework?: ApplicationFramework\n}): BunderstackBlueprint {\n const workerRequired = args.manifest.background.jobs.length > 0\n return parseBlueprint({\n version: 1,\n generator: { name: 'bunderstack', version: args.generatorVersion },\n application: {\n framework: args.framework ?? 'tanstack-start',\n scripts: {\n build: 'build',\n start: 'start',\n ...(workerRequired ? { worker: 'worker' } : {}),\n },\n },\n bunderstack: { entry: args.entry, manifestVersion: 3 },\n resources: {\n database: {\n ...args.manifest.database,\n migrationMode: args.migrationMode,\n tables: sortBy(\n args.manifest.database.tables,\n (entry) => entry.physicalName,\n ),\n },\n storage: {\n ...args.manifest.storage,\n buckets: sortBy(args.manifest.storage.buckets, (entry) => entry.name),\n },\n ...(args.manifest.realtime.required\n ? { realtime: { required: true } }\n : {}),\n },\n environment: sortBy(args.manifest.environment, (entry) => entry.key),\n background: {\n worker: { required: workerRequired },\n jobs: sortBy(args.manifest.background.jobs, (entry) => entry.name),\n cron: sortBy(args.manifest.background.cron, (entry) => entry.name),\n maintenance: sortBy(\n args.manifest.background.maintenance,\n (entry) => entry.name,\n ),\n },\n })\n}\n\nexport function parseBlueprintYaml(source: string): BunderstackBlueprint {\n return parseBlueprint(parse(source) as unknown)\n}\n\nexport function serializeBlueprint(value: BunderstackBlueprint): string {\n const blueprint = parseBlueprint(value)\n const options = {\n aliasDuplicateObjects: false,\n defaultKeyType: 'PLAIN',\n defaultStringType: 'PLAIN',\n lineWidth: 0,\n } as const\n const source = stringify(\n parseBlueprintYaml(stringify(blueprint, options)),\n options,\n )\n return source.replace(\n /^(\\s*schedule: )(.+)$/gm,\n (_match, prefix: string, value: string) => {\n const schedule = value.startsWith('\"') ? JSON.parse(value) : value\n return `${prefix}${JSON.stringify(schedule)}`\n },\n )\n}\n"]}
|
|
1
|
+
{"version":3,"file":"blueprint.js","sourceRoot":"","sources":["../src/blueprint.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAC5B,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,MAAM,CAAA;AAIvC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACvC,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAA;AA0C1D,MAAM,QAAQ,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAA;AACnD,MAAM,YAAY,GAAG,CAAC,CAAC,IAAI,CACzB,QAAQ,EACR,CAAC,CAAC,KAAK,CACL,CAAC,KAAK,EAAE,EAAE,CACR,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC;IACtB,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;IACrB,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,EAAE,IAAI,IAAI,KAAK,IAAI,CAAC,EAChE,iDAAiD,CAClD,CACF,CAAA;AACD,MAAM,YAAY,GAAG,CAAC,CAAC,IAAI,CACzB,QAAQ,EACR,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IAChB,IAAI,CAAC;QACH,SAAS,CAAC,KAAK,CAAC,CAAA;QAChB,OAAO,IAAI,CAAA;IACb,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC,EAAE,uBAAuB,CAAC,CAC5B,CAAA;AAED,+EAA+E;AAC/E,+EAA+E;AAC/E,qDAAqD;AACrD,SAAS,IAAI,CAAmC,OAAiB;IAC/D,OAAO,CAAC,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAA;AAC/C,CAAC;AAED,MAAM,eAAe,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACrB,SAAS,EAAE,IAAI,CAAC;QACd,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC;QAC9B,OAAO,EAAE,QAAQ;KAClB,CAAC;IACF,WAAW,EAAE,IAAI,CAAC;QAChB,SAAS,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,gBAAgB,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;QACvE,OAAO,EAAE,IAAI,CAAC;YACZ,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;YACzB,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;YACzB,MAAM,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;SACxC,CAAC;KACH,CAAC;IACF,WAAW,EAAE,IAAI,CAAC;QAChB,KAAK,EAAE,YAAY;QACnB,eAAe,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;KAC9B,CAAC;IACF,SAAS,EAAE,IAAI,CAAC;QACd,QAAQ,EAAE,IAAI,CAAC;YACb,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;YACrC,mBAAmB,EAAE,YAAY;YACjC,aAAa,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;YACjD,MAAM,EAAE,CAAC,CAAC,KAAK,CACb,IAAI,CAAC;gBACH,UAAU,EAAE,QAAQ;gBACpB,YAAY,EAAE,QAAQ;gBACtB,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE;aACpB,CAAC,CACH;SACF,CAAC;QACF,OAAO,EAAE,IAAI,CAAC;YACZ,aAAa,EAAE,QAAQ;YACvB,OAAO,EAAE,CAAC,CAAC,KAAK,CACd,IAAI,CAAC;gBACH,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;aAC9C,CAAC,CACH;SACF,CAAC;QACF,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;KAC1D,CAAC;IACF,WAAW,EAAE,CAAC,CAAC,KAAK,CAClB,IAAI,CAAC;QACH,GAAG,EAAE,QAAQ;QACb,QAAQ,EAAE,CAAC,CAAC,OAAO,EAAE;QACrB,KAAK,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QACvC,SAAS,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;QAClC,WAAW,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;KAC5D,CAAC,CACH;IACD,GAAG,EAAE,CAAC,CAAC,QAAQ,CACb,IAAI,CAAC;QACH,UAAU,EAAE,CAAC,CAAC,KAAK,CACjB,IAAI,CAAC;YACH,MAAM,EAAE,QAAQ;YAChB,WAAW,EAAE,QAAQ;YACrB,MAAM,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;YACnD,MAAM,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC;YAC5B,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC;YAC1B,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;SACxD,CAAC,CACH;KACF,CAAC,CACH;IACD,UAAU,EAAE,IAAI,CAAC;QACf,MAAM,EAAE,IAAI,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;QACvC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;QACvC,IAAI,EAAE,CAAC,CAAC,KAAK,CACX,IAAI,CAAC;YACH,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,YAAY;YACtB,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;SAC3B,CAAC,CACH;QACD,WAAW,EAAE,CAAC,CAAC,KAAK,CAClB,IAAI,CAAC;YACH,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC;YAChC,QAAQ,EAAE,YAAY;YACtB,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;SAC3B,CAAC,CACH;KACF,CAAC;CACH,CAAC,CAAA;AAEF,SAAS,MAAM,CAAI,OAAqB,EAAE,GAAyB;IACjE,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;AAChF,CAAC;AAED,SAAS,gBAAgB,CAAC,UAAkB,EAAE,MAAyB;IACrE,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAA;IAC9B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,2BAA2B,UAAU,KAAK,KAAK,GAAG,CAAC,CAAA;QACrE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;IACjB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,MAAM,SAAS,GAAG,sBAAsB,CACtC,eAAe,EACf,KAAK,EACL,WAAW,CACY,CAAA;IACzB,gBAAgB,CACd,yBAAyB,EACzB,SAAS,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CACvE,CAAA;IACD,gBAAgB,CACd,uBAAuB,EACvB,SAAS,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,CACrE,CAAA;IACD,gBAAgB,CACd,gBAAgB,EAChB,SAAS,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAC/D,CAAA;IACD,gBAAgB,CACd,iBAAiB,EACjB,SAAS,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAChD,CAAA;IACD,IAAI,SAAS,CAAC,GAAG;QACf,gBAAgB,CACd,eAAe,EACf,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CACtD,CAAA;IACH,gBAAgB,CACd,gBAAgB,EAChB,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CACrD,CAAA;IACD,gBAAgB,CACd,iBAAiB,EACjB,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CACrD,CAAA;IACD,gBAAgB,CACd,wBAAwB,EACxB,SAAS,CAAC,UAAU,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAC5D,CAAA;IACD,IACE,CAAC,SAAS,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CACvC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,SAAS,CAAC,SAAS,CAAC,OAAO,CAAC,aAAa,CACtE,EACD,CAAC;QACD,MAAM,IAAI,KAAK,CACb,yEAAyE,CAC1E,CAAA;IACH,CAAC;IACD,MAAM,cAAc,GAAG,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAA;IAC3D,IAAI,SAAS,CAAC,UAAU,CAAC,MAAM,CAAC,QAAQ,KAAK,cAAc,EAAE,CAAC;QAC5D,MAAM,IAAI,KAAK,CACb,yEAAyE,CAC1E,CAAA;IACH,CAAC;IACD,IAAI,OAAO,CAAC,SAAS,CAAC,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,cAAc,EAAE,CAAC;QACrE,MAAM,IAAI,KAAK,CACb,wEAAwE,CACzE,CAAA;IACH,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAsB;IACtD,OAAO,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,KAAK,KAAK,QAAQ,CAAA;AACpD,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,IAMrC;IACC,MAAM,cAAc,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAA;IAC/D,OAAO,cAAc,CAAC;QACpB,OAAO,EAAE,CAAC;QACV,SAAS,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC,gBAAgB,EAAE;QAClE,WAAW,EAAE;YACX,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,gBAAgB;YAC7C,OAAO,EAAE;gBACP,KAAK,EAAE,OAAO;gBACd,KAAK,EAAE,OAAO;gBACd,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAChD;SACF;QACD,WAAW,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,eAAe,EAAE,CAAC,EAAE;QACtD,SAAS,EAAE;YACT,QAAQ,EAAE;gBACR,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ;gBACzB,aAAa,EAAE,IAAI,CAAC,aAAa;gBACjC,MAAM,EAAE,MAAM,CACZ,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAC7B,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,YAAY,CAC9B;aACF;YACD,OAAO,EAAE;gBACP,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO;gBACxB,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC;aACtE;YACD,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ;gBACjC,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE;gBAClC,CAAC,CAAC,EAAE,CAAC;SACR;QACD,WAAW,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC;QACpE,GAAG,EAAE,EAAE,UAAU,EAAE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,EAAE;QACjD,UAAU,EAAE;YACV,MAAM,EAAE,EAAE,QAAQ,EAAE,cAAc,EAAE;YACpC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC;YAClE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC;YAClE,WAAW,EAAE,MAAM,CACjB,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,WAAW,EACpC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CACtB;SACF;KACF,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,MAAc;IAC/C,OAAO,cAAc,CAAC,KAAK,CAAC,MAAM,CAAY,CAAC,CAAA;AACjD,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,KAA2B;IAC5D,MAAM,SAAS,GAAG,cAAc,CAAC,KAAK,CAAC,CAAA;IACvC,MAAM,OAAO,GAAG;QACd,qBAAqB,EAAE,KAAK;QAC5B,cAAc,EAAE,OAAO;QACvB,iBAAiB,EAAE,OAAO;QAC1B,SAAS,EAAE,CAAC;KACJ,CAAA;IACV,MAAM,MAAM,GAAG,SAAS,CACtB,kBAAkB,CAAC,SAAS,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,EACjD,OAAO,CACR,CAAA;IACD,OAAO,MAAM,CAAC,OAAO,CACnB,yBAAyB,EACzB,CAAC,MAAM,EAAE,MAAc,EAAE,KAAa,EAAE,EAAE;QACxC,MAAM,QAAQ,GAAG,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;QAClE,OAAO,GAAG,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAA;IAC/C,CAAC,CACF,CAAA;AACH,CAAC","sourcesContent":["import * as v from 'valibot'\nimport { parse, stringify } from 'yaml'\n\nimport type { BunderstackManifest } from './manifest'\n\nimport { parseCron } from './jobs/cron'\nimport { validateStandardSchema } from './standard-schema'\n\nexport type MigrationMode = 'migrations' | 'push'\nexport type ApplicationFramework =\n | 'tanstack-start'\n | 'solid'\n | 'bun-ssr'\n | 'custom'\n\n/**\n * Blueprint form of an environment key. `sensitive` and `description` are\n * optional because blueprints committed before 0.23.0 do not carry them.\n */\nexport type BlueprintEnvVar = {\n key: string\n required: boolean\n scope: 'server' | 'client'\n sensitive?: boolean\n description?: string\n}\n\nexport type BunderstackBlueprint = {\n version: 1\n generator: { name: 'bunderstack'; version: string }\n application: {\n framework: ApplicationFramework\n scripts: { build: 'build'; start: 'start'; worker?: 'worker' }\n }\n bunderstack: { entry: string; manifestVersion: 3 }\n resources: {\n database: BunderstackManifest['database'] & { migrationMode: MigrationMode }\n storage: BunderstackManifest['storage']\n realtime?: { required: true }\n }\n environment: BlueprintEnvVar[]\n /** Application-declared procedures. Absent in blueprints written before 0.23.0. */\n api?: { operations: BunderstackManifest['api']['operations'] }\n background: BunderstackManifest['background'] & {\n worker: { required: boolean }\n }\n}\n\nconst nonEmpty = v.pipe(v.string(), v.minLength(1))\nconst relativePath = v.pipe(\n nonEmpty,\n v.check(\n (value) =>\n !value.startsWith('/') &&\n !value.includes('\\\\') &&\n value.split('/').every((part) => part !== '' && part !== '..'),\n 'entry must be a relative path without traversal',\n ),\n)\nconst cronSchedule = v.pipe(\n nonEmpty,\n v.check((value) => {\n try {\n parseCron(value)\n return true\n } catch {\n return false\n }\n }, 'invalid cron schedule'),\n)\n\n// Hosts parse blueprints committed by applications that upgrade bunderstack on\n// their own schedule, so a section written by a newer generator has to survive\n// an older parser instead of failing the whole file.\nfunction open<TEntries extends v.ObjectEntries>(entries: TEntries) {\n return v.objectWithRest(entries, v.unknown())\n}\n\nconst blueprintSchema = open({\n version: v.literal(1),\n generator: open({\n name: v.literal('bunderstack'),\n version: nonEmpty,\n }),\n application: open({\n framework: v.picklist(['tanstack-start', 'solid', 'bun-ssr', 'custom']),\n scripts: open({\n build: v.literal('build'),\n start: v.literal('start'),\n worker: v.optional(v.literal('worker')),\n }),\n }),\n bunderstack: open({\n entry: relativePath,\n manifestVersion: v.literal(3),\n }),\n resources: open({\n database: open({\n dialect: v.picklist(['sqlite', 'pg']),\n migrationsDirectory: relativePath,\n migrationMode: v.picklist(['migrations', 'push']),\n tables: v.array(\n open({\n exportName: nonEmpty,\n physicalName: nonEmpty,\n system: v.boolean(),\n }),\n ),\n }),\n storage: open({\n defaultBucket: nonEmpty,\n buckets: v.array(\n open({\n name: nonEmpty,\n visibility: v.picklist(['public', 'private']),\n }),\n ),\n }),\n realtime: v.optional(open({ required: v.literal(true) })),\n }),\n environment: v.array(\n open({\n key: nonEmpty,\n required: v.boolean(),\n scope: v.picklist(['server', 'client']),\n sensitive: v.optional(v.boolean()),\n description: v.optional(v.pipe(nonEmpty, v.maxLength(200))),\n }),\n ),\n api: v.optional(\n open({\n operations: v.array(\n open({\n handle: nonEmpty,\n operationId: nonEmpty,\n effect: v.picklist(['read', 'mutation', 'unknown']),\n method: v.optional(nonEmpty),\n path: v.optional(nonEmpty),\n summary: v.optional(v.pipe(nonEmpty, v.maxLength(200))),\n }),\n ),\n }),\n ),\n background: open({\n worker: open({ required: v.boolean() }),\n jobs: v.array(open({ name: nonEmpty })),\n cron: v.array(\n open({\n name: nonEmpty,\n schedule: cronSchedule,\n timezone: v.literal('UTC'),\n }),\n ),\n maintenance: v.array(\n open({\n name: v.literal('storage-sweep'),\n schedule: cronSchedule,\n timezone: v.literal('UTC'),\n }),\n ),\n }),\n})\n\nfunction sortBy<T>(entries: readonly T[], key: (entry: T) => string): T[] {\n return [...entries].sort((left, right) => key(left).localeCompare(key(right)))\n}\n\nfunction rejectDuplicates(collection: string, values: readonly string[]): void {\n const seen = new Set<string>()\n for (const value of values) {\n if (seen.has(value))\n throw new Error(`[bunderstack] duplicate ${collection} \"${value}\"`)\n seen.add(value)\n }\n}\n\nexport function parseBlueprint(value: unknown): BunderstackBlueprint {\n const blueprint = validateStandardSchema(\n blueprintSchema,\n value,\n 'blueprint',\n ) as BunderstackBlueprint\n rejectDuplicates(\n 'database physical table',\n blueprint.resources.database.tables.map((entry) => entry.physicalName),\n )\n rejectDuplicates(\n 'database export table',\n blueprint.resources.database.tables.map((entry) => entry.exportName),\n )\n rejectDuplicates(\n 'storage bucket',\n blueprint.resources.storage.buckets.map((entry) => entry.name),\n )\n rejectDuplicates(\n 'environment key',\n blueprint.environment.map((entry) => entry.key),\n )\n if (blueprint.api)\n rejectDuplicates(\n 'api operation',\n blueprint.api.operations.map((entry) => entry.handle),\n )\n rejectDuplicates(\n 'background job',\n blueprint.background.jobs.map((entry) => entry.name),\n )\n rejectDuplicates(\n 'background cron',\n blueprint.background.cron.map((entry) => entry.name),\n )\n rejectDuplicates(\n 'background maintenance',\n blueprint.background.maintenance.map((entry) => entry.name),\n )\n if (\n !blueprint.resources.storage.buckets.some(\n (bucket) => bucket.name === blueprint.resources.storage.defaultBucket,\n )\n ) {\n throw new Error(\n '[bunderstack] storage defaultBucket must be declared in storage buckets',\n )\n }\n const workerRequired = blueprint.background.jobs.length > 0\n if (blueprint.background.worker.required !== workerRequired) {\n throw new Error(\n '[bunderstack] background worker.required must match declared queue jobs',\n )\n }\n if (Boolean(blueprint.application.scripts.worker) !== workerRequired) {\n throw new Error(\n '[bunderstack] application worker script must match declared queue jobs',\n )\n }\n return blueprint\n}\n\n/**\n * Whether a host must treat this key as a secret. A blueprint generated before\n * 0.23.0 has no flag, and server keys are secret by default.\n */\nexport function isSensitiveEnvVar(entry: BlueprintEnvVar): boolean {\n return entry.sensitive ?? entry.scope === 'server'\n}\n\nexport function blueprintFromManifest(args: {\n manifest: BunderstackManifest\n generatorVersion: string\n entry: string\n migrationMode: MigrationMode\n framework?: ApplicationFramework\n}): BunderstackBlueprint {\n const workerRequired = args.manifest.background.jobs.length > 0\n return parseBlueprint({\n version: 1,\n generator: { name: 'bunderstack', version: args.generatorVersion },\n application: {\n framework: args.framework ?? 'tanstack-start',\n scripts: {\n build: 'build',\n start: 'start',\n ...(workerRequired ? { worker: 'worker' } : {}),\n },\n },\n bunderstack: { entry: args.entry, manifestVersion: 3 },\n resources: {\n database: {\n ...args.manifest.database,\n migrationMode: args.migrationMode,\n tables: sortBy(\n args.manifest.database.tables,\n (entry) => entry.physicalName,\n ),\n },\n storage: {\n ...args.manifest.storage,\n buckets: sortBy(args.manifest.storage.buckets, (entry) => entry.name),\n },\n ...(args.manifest.realtime.required\n ? { realtime: { required: true } }\n : {}),\n },\n environment: sortBy(args.manifest.environment, (entry) => entry.key),\n api: { operations: args.manifest.api.operations },\n background: {\n worker: { required: workerRequired },\n jobs: sortBy(args.manifest.background.jobs, (entry) => entry.name),\n cron: sortBy(args.manifest.background.cron, (entry) => entry.name),\n maintenance: sortBy(\n args.manifest.background.maintenance,\n (entry) => entry.name,\n ),\n },\n })\n}\n\nexport function parseBlueprintYaml(source: string): BunderstackBlueprint {\n return parseBlueprint(parse(source) as unknown)\n}\n\nexport function serializeBlueprint(value: BunderstackBlueprint): string {\n const blueprint = parseBlueprint(value)\n const options = {\n aliasDuplicateObjects: false,\n defaultKeyType: 'PLAIN',\n defaultStringType: 'PLAIN',\n lineWidth: 0,\n } as const\n const source = stringify(\n parseBlueprintYaml(stringify(blueprint, options)),\n options,\n )\n return source.replace(\n /^(\\s*schedule: )(.+)$/gm,\n (_match, prefix: string, value: string) => {\n const schedule = value.startsWith('\"') ? JSON.parse(value) : value\n return `${prefix}${JSON.stringify(schedule)}`\n },\n )\n}\n"]}
|
package/dist/env.d.ts
CHANGED
|
@@ -1,10 +1,26 @@
|
|
|
1
1
|
import type { StandardSchemaV1 } from '@standard-schema/spec';
|
|
2
2
|
export declare const CLIENT_PREFIX: "PUBLIC_";
|
|
3
|
+
/**
|
|
4
|
+
* Static, value-free metadata about an environment key. It reaches the manifest
|
|
5
|
+
* and the committed blueprint, so it must never contain a value or a secret.
|
|
6
|
+
*/
|
|
7
|
+
export type EnvVarMeta = {
|
|
8
|
+
/**
|
|
9
|
+
* Whether the value is a secret. Defaults to `true` for server keys and
|
|
10
|
+
* `false` for client keys. A client key can never be sensitive: `PUBLIC_*`
|
|
11
|
+
* values are compiled into the browser bundle.
|
|
12
|
+
*/
|
|
13
|
+
sensitive?: boolean;
|
|
14
|
+
/** Prose shown to whoever fills this key in. Max 200 characters. */
|
|
15
|
+
description?: string;
|
|
16
|
+
};
|
|
3
17
|
export type EnvConfigInput = {
|
|
4
18
|
server?: Record<string, StandardSchemaV1>;
|
|
5
19
|
client?: Record<string, StandardSchemaV1>;
|
|
6
20
|
/** Explicit value source for client vars (e.g. Vite's import.meta.env). */
|
|
7
21
|
runtimeEnv?: Record<string, unknown>;
|
|
22
|
+
/** Value-free metadata per key, keyed by the key's own name. */
|
|
23
|
+
meta?: Record<string, EnvVarMeta>;
|
|
8
24
|
};
|
|
9
25
|
export type BunderstackRole = 'all' | 'web' | 'worker';
|
|
10
26
|
/** Vars bunderstack itself consumes, always validated. */
|
|
@@ -19,6 +35,8 @@ export type BaseEnv = {
|
|
|
19
35
|
BUNDERSTACK_EMAIL_PROVIDER?: string;
|
|
20
36
|
BUNDERSTACK_EMAIL_FROM?: string;
|
|
21
37
|
BUNDERHOST_ENVIRONMENT_ID?: string;
|
|
38
|
+
/** Deployed commit SHA, injected by the platform. Reported by readiness. */
|
|
39
|
+
BUNDERSTACK_REVISION?: string;
|
|
22
40
|
BUNDERSTACK_ROLE: BunderstackRole;
|
|
23
41
|
};
|
|
24
42
|
type InferVars<T> = T extends Record<string, StandardSchemaV1> ? {
|
package/dist/env.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAO7D,eAAO,MAAM,aAAa,EAAG,SAAkB,CAAA;AAE/C,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;IACzC,2EAA2E;IAC3E,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAO7D,eAAO,MAAM,aAAa,EAAG,SAAkB,CAAA;AAE/C;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;IACzC,2EAA2E;IAC3E,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACpC,gEAAgE;IAChE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;CAClC,CAAA;AAED,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,KAAK,GAAG,QAAQ,CAAA;AAItD,0DAA0D;AAC1D,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,YAAY,EAAE,MAAM,CAAA;IACpB,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,0BAA0B,CAAC,EAAE,MAAM,CAAA;IACnC,sBAAsB,CAAC,EAAE,MAAM,CAAA;IAC/B,yBAAyB,CAAC,EAAE,MAAM,CAAA;IAClC,4EAA4E;IAC5E,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B,gBAAgB,EAAE,eAAe,CAAA;CAClC,CAAA;AAED,KAAK,SAAS,CAAC,CAAC,IACd,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GACtC;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,gBAAgB,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,GACtD,OAAO,CAAA;AAGb,MAAM,MAAM,YAAY,CAAC,IAAI,SAAS,cAAc,GAAG,SAAS,IAAI;IAClE,IAAI;CACL,SAAS,CAAC,cAAc,CAAC,GACtB,OAAO,GACL,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC,GACtC,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC,GACxC,OAAO,CAAA;AAEX,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAA;gBAEb,MAAM,EAAE,MAAM,EAAE;CAK7B;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,+EAA+E;IAC/E,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,yEAAyE;IACzE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;IAC3C,kEAAkE;IAClE,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B,CAAA;AAqCD,wBAAgB,WAAW,CAAC,IAAI,SAAS,cAAc,GAAG,SAAS,EACjE,SAAS,EAAE,IAAI,EACf,OAAO,GAAE,kBAAuB,GAC/B,YAAY,CAAC,IAAI,CAAC,CA6CpB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,SAAS,cAAc,EACzD,SAAS,EAAE,IAAI,GACd,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAsB3B"}
|
package/dist/env.js
CHANGED
|
@@ -49,6 +49,7 @@ export function validateEnv(envConfig, options = {}) {
|
|
|
49
49
|
BUNDERSTACK_EMAIL_PROVIDER: source.BUNDERSTACK_EMAIL_PROVIDER,
|
|
50
50
|
BUNDERSTACK_EMAIL_FROM: source.BUNDERSTACK_EMAIL_FROM,
|
|
51
51
|
BUNDERHOST_ENVIRONMENT_ID: source.BUNDERHOST_ENVIRONMENT_ID,
|
|
52
|
+
BUNDERSTACK_REVISION: source.BUNDERSTACK_REVISION,
|
|
52
53
|
BUNDERSTACK_ROLE: (source.BUNDERSTACK_ROLE ?? 'all'),
|
|
53
54
|
};
|
|
54
55
|
if (isProduction && !source.AUTH_SECRET) {
|
package/dist/env.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env.js","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"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","sourcesContent":["// src/env.ts — env validation. Browser-safe: type-only Standard Schema import.\nimport type { StandardSchemaV1 } from '@standard-schema/spec'\n\nimport {\n StandardSchemaValidationError,\n validateStandardSchema,\n} from './standard-schema'\n\nexport const CLIENT_PREFIX = 'PUBLIC_' as const\n\nexport type EnvConfigInput = {\n server?: Record<string, StandardSchemaV1>\n client?: Record<string, StandardSchemaV1>\n /** Explicit value source for client vars (e.g. Vite's import.meta.env). */\n runtimeEnv?: Record<string, unknown>\n}\n\nexport type BunderstackRole = 'all' | 'web' | 'worker'\n\nconst ROLES: readonly BunderstackRole[] = ['all', 'web', 'worker']\n\n/** Vars bunderstack itself consumes, always validated. */\nexport type BaseEnv = {\n NODE_ENV?: string\n DATABASE_URL: string\n DATABASE_AUTH_TOKEN?: string\n AUTH_SECRET: string\n REDIS_URL?: string\n RESEND_API_KEY?: string\n SMTP_URL?: string\n BUNDERSTACK_EMAIL_PROVIDER?: string\n BUNDERSTACK_EMAIL_FROM?: string\n BUNDERHOST_ENVIRONMENT_ID?: string\n BUNDERSTACK_ROLE: BunderstackRole\n}\n\ntype InferVars<T> =\n T extends Record<string, StandardSchemaV1>\n ? { [K in keyof T]: StandardSchemaV1.InferOutput<T[K]> }\n : unknown\n\n// Non-distributive so `ValidatedEnv<undefined>` is BaseEnv, not `never`.\nexport type ValidatedEnv<TEnv extends EnvConfigInput | undefined> = [\n TEnv,\n] extends [EnvConfigInput]\n ? BaseEnv &\n InferVars<NonNullable<TEnv>['server']> &\n InferVars<NonNullable<TEnv>['client']>\n : BaseEnv\n\nexport class BunderstackEnvError extends Error {\n readonly issues: string[]\n\n constructor(issues: string[]) {\n super(`Invalid environment:\\n - ${issues.join('\\n - ')}`)\n this.name = 'BunderstackEnvError'\n this.issues = issues\n }\n}\n\nexport type ValidateEnvOptions = {\n /** String tag of the configured email provider ('resend' | 'smtp'), if any. */\n emailProvider?: string\n /** Value source; defaults to process.env. Tests pass this explicitly. */\n source?: Record<string, string | undefined>\n /** Dialect-aware DATABASE_URL fallback; bunderstack passes it. */\n defaultDatabaseUrl?: string\n}\n\nconst DEV_AUTH_SECRET = 'dev-secret-change-in-prod'\n\nfunction validateSection(\n section: Record<string, StandardSchemaV1> | undefined,\n kind: 'server' | 'client',\n source: Record<string, unknown>,\n issues: string[],\n out: Record<string, unknown>,\n) {\n for (const [key, schema] of Object.entries(section ?? {})) {\n const isPublic = key.startsWith(CLIENT_PREFIX)\n if (kind === 'server' && isPublic) {\n issues.push(\n `${key}: server vars must not start with ${CLIENT_PREFIX} (move it to env.client)`,\n )\n continue\n }\n if (kind === 'client' && !isPublic) {\n issues.push(\n `${key}: client vars must start with ${CLIENT_PREFIX} (rename it or move it to env.server)`,\n )\n continue\n }\n try {\n out[key] = validateStandardSchema(schema, source[key], 'env')\n } catch (error) {\n if (!(error instanceof StandardSchemaValidationError)) throw error\n for (const issue of error.issues) {\n const path = issue.path.map(String).join('.')\n issues.push(`${key}${path ? `.${path}` : ''}: ${issue.message}`)\n }\n }\n }\n}\n\nexport function validateEnv<TEnv extends EnvConfigInput | undefined>(\n envConfig: TEnv,\n options: ValidateEnvOptions = {},\n): ValidatedEnv<TEnv> {\n const source =\n options.source ?? (process.env as Record<string, string | undefined>)\n const issues: string[] = []\n const isProduction = source.NODE_ENV === 'production'\n\n const base: BaseEnv = {\n NODE_ENV: source.NODE_ENV,\n DATABASE_URL:\n source.DATABASE_URL ?? options.defaultDatabaseUrl ?? 'file:./data.db',\n DATABASE_AUTH_TOKEN: source.DATABASE_AUTH_TOKEN,\n AUTH_SECRET: source.AUTH_SECRET ?? DEV_AUTH_SECRET,\n REDIS_URL: source.REDIS_URL,\n RESEND_API_KEY: source.RESEND_API_KEY,\n SMTP_URL: source.SMTP_URL,\n BUNDERSTACK_EMAIL_PROVIDER: source.BUNDERSTACK_EMAIL_PROVIDER,\n BUNDERSTACK_EMAIL_FROM: source.BUNDERSTACK_EMAIL_FROM,\n BUNDERHOST_ENVIRONMENT_ID: source.BUNDERHOST_ENVIRONMENT_ID,\n BUNDERSTACK_ROLE: (source.BUNDERSTACK_ROLE ?? 'all') as BunderstackRole,\n }\n if (isProduction && !source.AUTH_SECRET) {\n issues.push('AUTH_SECRET: required in production')\n }\n if (\n source.BUNDERSTACK_ROLE !== undefined &&\n !ROLES.includes(source.BUNDERSTACK_ROLE as BunderstackRole)\n ) {\n issues.push(\n `BUNDERSTACK_ROLE: must be one of ${ROLES.join(', ')} (got \"${String(source.BUNDERSTACK_ROLE)}\")`,\n )\n }\n if (options.emailProvider === 'resend' && !source.RESEND_API_KEY) {\n issues.push(\"RESEND_API_KEY: required when email provider is 'resend'\")\n }\n if (options.emailProvider === 'smtp' && !source.SMTP_URL) {\n issues.push(\"SMTP_URL: required when email provider is 'smtp'\")\n }\n\n const userVars: Record<string, unknown> = {}\n validateSection(envConfig?.server, 'server', source, issues, userVars)\n validateSection(envConfig?.client, 'client', source, issues, userVars)\n\n if (issues.length > 0) throw new BunderstackEnvError(issues)\n return { ...base, ...userVars } as ValidatedEnv<TEnv>\n}\n\n/**\n * Browser-side companion (t3-env style): validates ONLY the client section.\n * Server keys exist on the returned object as traps that throw on access, so\n * a leaked import fails loudly instead of silently reading undefined.\n */\nexport function createClientEnv<TEnv extends EnvConfigInput>(\n envConfig: TEnv,\n): InferVars<TEnv['client']> {\n const source =\n envConfig.runtimeEnv ??\n (typeof process !== 'undefined'\n ? (process.env as Record<string, unknown>)\n : {})\n const issues: string[] = []\n const values: Record<string, unknown> = {}\n validateSection(envConfig.client, 'client', source, issues, values)\n if (issues.length > 0) throw new BunderstackEnvError(issues)\n\n const serverKeys = new Set(Object.keys(envConfig.server ?? {}))\n return new Proxy(values, {\n get(target, prop) {\n if (typeof prop === 'string' && serverKeys.has(prop)) {\n throw new Error(\n `${prop} is server-only and not available in client env`,\n )\n }\n return Reflect.get(target, prop)\n },\n }) as InferVars<TEnv['client']>\n}\n"]}
|
|
1
|
+
{"version":3,"file":"env.js","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,6BAA6B,EAC7B,sBAAsB,GACvB,MAAM,mBAAmB,CAAA;AAE1B,MAAM,CAAC,MAAM,aAAa,GAAG,SAAkB,CAAA;AA4B/C,MAAM,KAAK,GAA+B,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAA;AAiClE,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IACnC,MAAM,CAAU;IAEzB,YAAY,MAAgB;QAC1B,KAAK,CAAC,6BAA6B,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;QAC3D,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAA;QACjC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;IACtB,CAAC;CACF;AAWD,MAAM,eAAe,GAAG,2BAA2B,CAAA;AAEnD,SAAS,eAAe,CACtB,OAAqD,EACrD,IAAyB,EACzB,MAA+B,EAC/B,MAAgB,EAChB,GAA4B;IAE5B,KAAK,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,CAAC;QAC1D,MAAM,QAAQ,GAAG,GAAG,CAAC,UAAU,CAAC,aAAa,CAAC,CAAA;QAC9C,IAAI,IAAI,KAAK,QAAQ,IAAI,QAAQ,EAAE,CAAC;YAClC,MAAM,CAAC,IAAI,CACT,GAAG,GAAG,qCAAqC,aAAa,0BAA0B,CACnF,CAAA;YACD,SAAQ;QACV,CAAC;QACD,IAAI,IAAI,KAAK,QAAQ,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnC,MAAM,CAAC,IAAI,CACT,GAAG,GAAG,iCAAiC,aAAa,uCAAuC,CAC5F,CAAA;YACD,SAAQ;QACV,CAAC;QACD,IAAI,CAAC;YACH,GAAG,CAAC,GAAG,CAAC,GAAG,sBAAsB,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,CAAA;QAC/D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,CAAC,KAAK,YAAY,6BAA6B,CAAC;gBAAE,MAAM,KAAK,CAAA;YAClE,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;gBACjC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;gBAC7C,MAAM,CAAC,IAAI,CAAC,GAAG,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAA;YAClE,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,UAAU,WAAW,CACzB,SAAe,EACf,UAA8B,EAAE;IAEhC,MAAM,MAAM,GACV,OAAO,CAAC,MAAM,IAAK,OAAO,CAAC,GAA0C,CAAA;IACvE,MAAM,MAAM,GAAa,EAAE,CAAA;IAC3B,MAAM,YAAY,GAAG,MAAM,CAAC,QAAQ,KAAK,YAAY,CAAA;IAErD,MAAM,IAAI,GAAY;QACpB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,YAAY,EACV,MAAM,CAAC,YAAY,IAAI,OAAO,CAAC,kBAAkB,IAAI,gBAAgB;QACvE,mBAAmB,EAAE,MAAM,CAAC,mBAAmB;QAC/C,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,eAAe;QAClD,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,cAAc,EAAE,MAAM,CAAC,cAAc;QACrC,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,0BAA0B,EAAE,MAAM,CAAC,0BAA0B;QAC7D,sBAAsB,EAAE,MAAM,CAAC,sBAAsB;QACrD,yBAAyB,EAAE,MAAM,CAAC,yBAAyB;QAC3D,oBAAoB,EAAE,MAAM,CAAC,oBAAoB;QACjD,gBAAgB,EAAE,CAAC,MAAM,CAAC,gBAAgB,IAAI,KAAK,CAAoB;KACxE,CAAA;IACD,IAAI,YAAY,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;QACxC,MAAM,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAA;IACpD,CAAC;IACD,IACE,MAAM,CAAC,gBAAgB,KAAK,SAAS;QACrC,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,gBAAmC,CAAC,EAC3D,CAAC;QACD,MAAM,CAAC,IAAI,CACT,oCAAoC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAClG,CAAA;IACH,CAAC;IACD,IAAI,OAAO,CAAC,aAAa,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;QACjE,MAAM,CAAC,IAAI,CAAC,0DAA0D,CAAC,CAAA;IACzE,CAAC;IACD,IAAI,OAAO,CAAC,aAAa,KAAK,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;QACzD,MAAM,CAAC,IAAI,CAAC,kDAAkD,CAAC,CAAA;IACjE,CAAC;IAED,MAAM,QAAQ,GAA4B,EAAE,CAAA;IAC5C,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAA;IACtE,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAA;IAEtE,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,MAAM,IAAI,mBAAmB,CAAC,MAAM,CAAC,CAAA;IAC5D,OAAO,EAAE,GAAG,IAAI,EAAE,GAAG,QAAQ,EAAwB,CAAA;AACvD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAC7B,SAAe;IAEf,MAAM,MAAM,GACV,SAAS,CAAC,UAAU;QACpB,CAAC,OAAO,OAAO,KAAK,WAAW;YAC7B,CAAC,CAAE,OAAO,CAAC,GAA+B;YAC1C,CAAC,CAAC,EAAE,CAAC,CAAA;IACT,MAAM,MAAM,GAAa,EAAE,CAAA;IAC3B,MAAM,MAAM,GAA4B,EAAE,CAAA;IAC1C,eAAe,CAAC,SAAS,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IACnE,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,MAAM,IAAI,mBAAmB,CAAC,MAAM,CAAC,CAAA;IAE5D,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,CAAA;IAC/D,OAAO,IAAI,KAAK,CAAC,MAAM,EAAE;QACvB,GAAG,CAAC,MAAM,EAAE,IAAI;YACd,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBACrD,MAAM,IAAI,KAAK,CACb,GAAG,IAAI,iDAAiD,CACzD,CAAA;YACH,CAAC;YACD,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;QAClC,CAAC;KACF,CAA8B,CAAA;AACjC,CAAC","sourcesContent":["// src/env.ts — env validation. Browser-safe: type-only Standard Schema import.\nimport type { StandardSchemaV1 } from '@standard-schema/spec'\n\nimport {\n StandardSchemaValidationError,\n validateStandardSchema,\n} from './standard-schema'\n\nexport const CLIENT_PREFIX = 'PUBLIC_' as const\n\n/**\n * Static, value-free metadata about an environment key. It reaches the manifest\n * and the committed blueprint, so it must never contain a value or a secret.\n */\nexport type EnvVarMeta = {\n /**\n * Whether the value is a secret. Defaults to `true` for server keys and\n * `false` for client keys. A client key can never be sensitive: `PUBLIC_*`\n * values are compiled into the browser bundle.\n */\n sensitive?: boolean\n /** Prose shown to whoever fills this key in. Max 200 characters. */\n description?: string\n}\n\nexport type EnvConfigInput = {\n server?: Record<string, StandardSchemaV1>\n client?: Record<string, StandardSchemaV1>\n /** Explicit value source for client vars (e.g. Vite's import.meta.env). */\n runtimeEnv?: Record<string, unknown>\n /** Value-free metadata per key, keyed by the key's own name. */\n meta?: Record<string, EnvVarMeta>\n}\n\nexport type BunderstackRole = 'all' | 'web' | 'worker'\n\nconst ROLES: readonly BunderstackRole[] = ['all', 'web', 'worker']\n\n/** Vars bunderstack itself consumes, always validated. */\nexport type BaseEnv = {\n NODE_ENV?: string\n DATABASE_URL: string\n DATABASE_AUTH_TOKEN?: string\n AUTH_SECRET: string\n REDIS_URL?: string\n RESEND_API_KEY?: string\n SMTP_URL?: string\n BUNDERSTACK_EMAIL_PROVIDER?: string\n BUNDERSTACK_EMAIL_FROM?: string\n BUNDERHOST_ENVIRONMENT_ID?: string\n /** Deployed commit SHA, injected by the platform. Reported by readiness. */\n BUNDERSTACK_REVISION?: string\n BUNDERSTACK_ROLE: BunderstackRole\n}\n\ntype InferVars<T> =\n T extends Record<string, StandardSchemaV1>\n ? { [K in keyof T]: StandardSchemaV1.InferOutput<T[K]> }\n : unknown\n\n// Non-distributive so `ValidatedEnv<undefined>` is BaseEnv, not `never`.\nexport type ValidatedEnv<TEnv extends EnvConfigInput | undefined> = [\n TEnv,\n] extends [EnvConfigInput]\n ? BaseEnv &\n InferVars<NonNullable<TEnv>['server']> &\n InferVars<NonNullable<TEnv>['client']>\n : BaseEnv\n\nexport class BunderstackEnvError extends Error {\n readonly issues: string[]\n\n constructor(issues: string[]) {\n super(`Invalid environment:\\n - ${issues.join('\\n - ')}`)\n this.name = 'BunderstackEnvError'\n this.issues = issues\n }\n}\n\nexport type ValidateEnvOptions = {\n /** String tag of the configured email provider ('resend' | 'smtp'), if any. */\n emailProvider?: string\n /** Value source; defaults to process.env. Tests pass this explicitly. */\n source?: Record<string, string | undefined>\n /** Dialect-aware DATABASE_URL fallback; bunderstack passes it. */\n defaultDatabaseUrl?: string\n}\n\nconst DEV_AUTH_SECRET = 'dev-secret-change-in-prod'\n\nfunction validateSection(\n section: Record<string, StandardSchemaV1> | undefined,\n kind: 'server' | 'client',\n source: Record<string, unknown>,\n issues: string[],\n out: Record<string, unknown>,\n) {\n for (const [key, schema] of Object.entries(section ?? {})) {\n const isPublic = key.startsWith(CLIENT_PREFIX)\n if (kind === 'server' && isPublic) {\n issues.push(\n `${key}: server vars must not start with ${CLIENT_PREFIX} (move it to env.client)`,\n )\n continue\n }\n if (kind === 'client' && !isPublic) {\n issues.push(\n `${key}: client vars must start with ${CLIENT_PREFIX} (rename it or move it to env.server)`,\n )\n continue\n }\n try {\n out[key] = validateStandardSchema(schema, source[key], 'env')\n } catch (error) {\n if (!(error instanceof StandardSchemaValidationError)) throw error\n for (const issue of error.issues) {\n const path = issue.path.map(String).join('.')\n issues.push(`${key}${path ? `.${path}` : ''}: ${issue.message}`)\n }\n }\n }\n}\n\nexport function validateEnv<TEnv extends EnvConfigInput | undefined>(\n envConfig: TEnv,\n options: ValidateEnvOptions = {},\n): ValidatedEnv<TEnv> {\n const source =\n options.source ?? (process.env as Record<string, string | undefined>)\n const issues: string[] = []\n const isProduction = source.NODE_ENV === 'production'\n\n const base: BaseEnv = {\n NODE_ENV: source.NODE_ENV,\n DATABASE_URL:\n source.DATABASE_URL ?? options.defaultDatabaseUrl ?? 'file:./data.db',\n DATABASE_AUTH_TOKEN: source.DATABASE_AUTH_TOKEN,\n AUTH_SECRET: source.AUTH_SECRET ?? DEV_AUTH_SECRET,\n REDIS_URL: source.REDIS_URL,\n RESEND_API_KEY: source.RESEND_API_KEY,\n SMTP_URL: source.SMTP_URL,\n BUNDERSTACK_EMAIL_PROVIDER: source.BUNDERSTACK_EMAIL_PROVIDER,\n BUNDERSTACK_EMAIL_FROM: source.BUNDERSTACK_EMAIL_FROM,\n BUNDERHOST_ENVIRONMENT_ID: source.BUNDERHOST_ENVIRONMENT_ID,\n BUNDERSTACK_REVISION: source.BUNDERSTACK_REVISION,\n BUNDERSTACK_ROLE: (source.BUNDERSTACK_ROLE ?? 'all') as BunderstackRole,\n }\n if (isProduction && !source.AUTH_SECRET) {\n issues.push('AUTH_SECRET: required in production')\n }\n if (\n source.BUNDERSTACK_ROLE !== undefined &&\n !ROLES.includes(source.BUNDERSTACK_ROLE as BunderstackRole)\n ) {\n issues.push(\n `BUNDERSTACK_ROLE: must be one of ${ROLES.join(', ')} (got \"${String(source.BUNDERSTACK_ROLE)}\")`,\n )\n }\n if (options.emailProvider === 'resend' && !source.RESEND_API_KEY) {\n issues.push(\"RESEND_API_KEY: required when email provider is 'resend'\")\n }\n if (options.emailProvider === 'smtp' && !source.SMTP_URL) {\n issues.push(\"SMTP_URL: required when email provider is 'smtp'\")\n }\n\n const userVars: Record<string, unknown> = {}\n validateSection(envConfig?.server, 'server', source, issues, userVars)\n validateSection(envConfig?.client, 'client', source, issues, userVars)\n\n if (issues.length > 0) throw new BunderstackEnvError(issues)\n return { ...base, ...userVars } as ValidatedEnv<TEnv>\n}\n\n/**\n * Browser-side companion (t3-env style): validates ONLY the client section.\n * Server keys exist on the returned object as traps that throw on access, so\n * a leaked import fails loudly instead of silently reading undefined.\n */\nexport function createClientEnv<TEnv extends EnvConfigInput>(\n envConfig: TEnv,\n): InferVars<TEnv['client']> {\n const source =\n envConfig.runtimeEnv ??\n (typeof process !== 'undefined'\n ? (process.env as Record<string, unknown>)\n : {})\n const issues: string[] = []\n const values: Record<string, unknown> = {}\n validateSection(envConfig.client, 'client', source, issues, values)\n if (issues.length > 0) throw new BunderstackEnvError(issues)\n\n const serverKeys = new Set(Object.keys(envConfig.server ?? {}))\n return new Proxy(values, {\n get(target, prop) {\n if (typeof prop === 'string' && serverKeys.has(prop)) {\n throw new Error(\n `${prop} is server-only and not available in client env`,\n )\n }\n return Reflect.get(target, prop)\n },\n }) as InferVars<TEnv['client']>\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -11,7 +11,9 @@ export type { AuthConfigContext, AuthConfigFactory, AuthConfigInput, BetterAuthC
|
|
|
11
11
|
export { validateEnv, createClientEnv, BunderstackEnvError } from './env.js';
|
|
12
12
|
export type { EnvConfigInput, BaseEnv, ValidatedEnv } from './env.js';
|
|
13
13
|
export { buildManifest, parseManifest } from './manifest.js';
|
|
14
|
-
export type { BunderstackManifest, ManifestEnvVar } from './manifest.js';
|
|
14
|
+
export type { ApiOperation, ApiOperationEffect, BunderstackManifest, ManifestEnvVar, } from './manifest.js';
|
|
15
|
+
export { buildReadinessReport, readinessReportSchema } from './readiness.js';
|
|
16
|
+
export type { ReadinessCheck, ReadinessCheckName, ReadinessCode, ReadinessReport, ReadinessStatus, } from './readiness.js';
|
|
15
17
|
export { createEmail } from './email.js';
|
|
16
18
|
export type { EmailMessage, EmailAdapter, EmailConfigInput, EmailFacade, } from './email.js';
|
|
17
19
|
export { createJobsBuilder } from './jobs/index.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAA;AACzB,YAAY,EACV,mBAAmB,EACnB,qBAAqB,EACrB,YAAY,EACZ,aAAa,EACb,cAAc,EACd,aAAa,GACd,MAAM,WAAW,CAAA;AAClB,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC1C,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACtD,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,MAAM,CAAA;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAC3C,YAAY,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAA;AACpD,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AACvE,YAAY,EACV,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,GACf,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,OAAO,CAAA;AACzE,YAAY,EAAE,cAAc,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,OAAO,CAAA;AAClE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AACzD,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ export { BunderstackError } from './errors.js';
|
|
|
5
5
|
export { resolveConfig, resolveAuthConfig, defineAuth } from './config.js';
|
|
6
6
|
export { validateEnv, createClientEnv, BunderstackEnvError } from './env.js';
|
|
7
7
|
export { buildManifest, parseManifest } from './manifest.js';
|
|
8
|
+
export { buildReadinessReport, readinessReportSchema } from './readiness.js';
|
|
8
9
|
export { createEmail } from './email.js';
|
|
9
10
|
export { createJobsBuilder } from './jobs/index.js';
|
|
10
11
|
export { defineAccess, validateAndResolveAccess, checkAccess, AUTH_TABLE_NAMES, } from './access.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAA;AASzB,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAG1C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAE3C,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AASvE,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,OAAO,CAAA;AAEzE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAA;AASzB,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAG1C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAE3C,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AASvE,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,OAAO,CAAA;AAEzE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAOzD,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAQzE,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAOrC,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAoBhD,OAAO,EACL,YAAY,EACZ,wBAAwB,EACxB,WAAW,EACX,gBAAgB,GACjB,MAAM,UAAU,CAAA;AAOjB,OAAO,EACL,MAAM,EACN,QAAQ,IAAI,cAAc,EAC1B,KAAK,IAAI,WAAW,EACpB,QAAQ,GACT,MAAM,UAAU,CAAA;AAejB,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAMxD,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAU3D,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EACrB,gBAAgB,EAChB,2BAA2B,GAC5B,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA","sourcesContent":["export * from './backend'\nexport type {\n AppRunWorkerOptions,\n AppStartWorkerOptions,\n AuthInstance,\n BucketNamesOf,\n BunderstackApp,\n StorageFacade,\n} from './runtime'\nexport { listSpec } from './api/list-spec'\nexport type { ListSpecOptions } from './api/list-spec'\nexport type { BunderstackDb, BunderstackTx } from './db'\nexport { MAX_LIST_LIMIT } from './list-query'\nexport { BunderstackError } from './errors'\nexport type { BunderstackErrorCode } from './errors'\nexport { resolveConfig, resolveAuthConfig, defineAuth } from './config'\nexport type {\n AuthConfigContext,\n AuthConfigFactory,\n AuthConfigInput,\n BetterAuthConfig,\n BunderstackConfig,\n ResolvedConfig,\n} from './config'\nexport { validateEnv, createClientEnv, BunderstackEnvError } from './env'\nexport type { EnvConfigInput, BaseEnv, ValidatedEnv } from './env'\nexport { buildManifest, parseManifest } from './manifest'\nexport type {\n ApiOperation,\n ApiOperationEffect,\n BunderstackManifest,\n ManifestEnvVar,\n} from './manifest'\nexport { buildReadinessReport, readinessReportSchema } from './readiness'\nexport type {\n ReadinessCheck,\n ReadinessCheckName,\n ReadinessCode,\n ReadinessReport,\n ReadinessStatus,\n} from './readiness'\nexport { createEmail } from './email'\nexport type {\n EmailMessage,\n EmailAdapter,\n EmailConfigInput,\n EmailFacade,\n} from './email'\nexport { createJobsBuilder } from './jobs/index'\nexport type {\n BunderstackJobContext,\n BunderstackJobsBuilder,\n BackgroundDefinition,\n BackgroundDefs,\n CronDefinition,\n CronInvocation,\n EnqueueOptions,\n JobContext,\n JobDefinition,\n JobsDefs,\n JobsFacade,\n JobsRuntimeFacade,\n QueueJobDefinition,\n QueueJobKeys,\n RunWorkerOptions,\n StartWorkerOptions,\n WorkerHandle,\n} from './jobs/index'\nexport {\n defineAccess,\n validateAndResolveAccess,\n checkAccess,\n AUTH_TABLE_NAMES,\n} from './access'\nexport type {\n TableAccessInput,\n OperationRule,\n AccessContext,\n AccessUser,\n} from './access'\nexport {\n typeid,\n generate as generateTypeId,\n parse as parseTypeId,\n asTypeId,\n} from './typeid'\nexport type { TypeId } from './typeid'\nexport type {\n DatabaseAdapter,\n DatabaseConnection,\n DatabaseConnectionResult,\n} from './database/adapter'\nexport type { StorageAdapter } from './storage/index'\nexport type {\n StorageConfigInput,\n BucketConfigInput,\n ResolvedBucket,\n} from './storage/buckets'\nexport type { TransformSpec } from './storage/thumbnails'\nexport type { RealtimeAction } from './realtime/publisher'\nexport { createRealtimeFacade } from './realtime/facade'\nexport type {\n RealtimeFacade,\n RealtimeTransport,\n SchemaTable,\n} from './realtime/facade'\nexport { createApiBuilder, defineApi } from './api/builder'\nexport type { BunderstackApiBuilder, ApiFactory } from './api/builder'\nexport type { ApiContext } from './api/context'\nexport type {\n CrudApiRouterFor,\n ExposedApiTables,\n MergeApiRouterTypes,\n UnifiedApiRouter,\n} from './api/types'\nexport type { TableCrudProcedures } from './api/crud-router'\nexport {\n buildApiRegistry,\n mergeApiRoutersStrict,\n normalizeApiPath,\n normalizeForeignOpenAPISpec,\n} from './api/registry'\nexport { mergeOpenAPISpecs } from './api/openapi'\n"]}
|
package/dist/manifest.d.ts
CHANGED
|
@@ -6,6 +6,22 @@ export type ManifestEnvVar = {
|
|
|
6
6
|
key: string;
|
|
7
7
|
required: boolean;
|
|
8
8
|
scope: 'server' | 'client';
|
|
9
|
+
sensitive: boolean;
|
|
10
|
+
description?: string;
|
|
11
|
+
};
|
|
12
|
+
export type ApiOperationEffect = 'read' | 'mutation' | 'unknown';
|
|
13
|
+
/**
|
|
14
|
+
* One application-declared procedure. `effect` is derived from the declared
|
|
15
|
+
* HTTP method; `unknown` means the application declared no route, and a host
|
|
16
|
+
* must treat it as at least as dangerous as a mutation.
|
|
17
|
+
*/
|
|
18
|
+
export type ApiOperation = {
|
|
19
|
+
handle: string;
|
|
20
|
+
operationId: string;
|
|
21
|
+
effect: ApiOperationEffect;
|
|
22
|
+
method?: string;
|
|
23
|
+
path?: string;
|
|
24
|
+
summary?: string;
|
|
9
25
|
};
|
|
10
26
|
export type BunderstackManifest = {
|
|
11
27
|
version: 3;
|
|
@@ -29,6 +45,9 @@ export type BunderstackManifest = {
|
|
|
29
45
|
required: boolean;
|
|
30
46
|
};
|
|
31
47
|
environment: ManifestEnvVar[];
|
|
48
|
+
api: {
|
|
49
|
+
operations: ApiOperation[];
|
|
50
|
+
};
|
|
32
51
|
background: {
|
|
33
52
|
jobs: {
|
|
34
53
|
name: string;
|
|
@@ -55,5 +74,6 @@ export declare function buildManifest(args: {
|
|
|
55
74
|
emailProvider: string | undefined;
|
|
56
75
|
realtime: boolean;
|
|
57
76
|
jobs: JobsDefs | undefined;
|
|
77
|
+
api: ApiOperation[];
|
|
58
78
|
}): BunderstackManifest;
|
|
59
79
|
//# sourceMappingURL=manifest.d.ts.map
|
package/dist/manifest.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,KAAK,EAAE,cAAc,
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,KAAK,EAAE,cAAc,EAAc,MAAM,OAAO,CAAA;AACvD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,KAAK,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAA;AAe/E,MAAM,MAAM,cAAc,GAAG;IAC3B,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,OAAO,CAAA;IACjB,KAAK,EAAE,QAAQ,GAAG,QAAQ,CAAA;IAC1B,SAAS,EAAE,OAAO,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,UAAU,GAAG,SAAS,CAAA;AAEhE;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,kBAAkB,CAAA;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,CAAC,CAAA;IACV,QAAQ,EAAE;QACR,OAAO,EAAE,OAAO,CAAA;QAChB,mBAAmB,EAAE,MAAM,CAAA;QAC3B,MAAM,EAAE;YAAE,UAAU,EAAE,MAAM,CAAC;YAAC,YAAY,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,OAAO,CAAA;SAAE,EAAE,CAAA;KACxE,CAAA;IACD,OAAO,EAAE;QACP,aAAa,EAAE,MAAM,CAAA;QACrB,OAAO,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,UAAU,EAAE,cAAc,CAAC,YAAY,CAAC,CAAA;SAAE,EAAE,CAAA;KACtE,CAAA;IACD,QAAQ,EAAE;QAAE,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAA;IAC/B,WAAW,EAAE,cAAc,EAAE,CAAA;IAC7B,GAAG,EAAE;QAAE,UAAU,EAAE,YAAY,EAAE,CAAA;KAAE,CAAA;IACnC,UAAU,EAAE;QACV,IAAI,EAAE;YAAE,IAAI,EAAE,MAAM,CAAA;SAAE,EAAE,CAAA;QACxB,IAAI,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,KAAK,CAAA;SAAE,EAAE,CAAA;QAC3D,WAAW,EAAE;YACX,IAAI,EAAE,eAAe,CAAA;YACrB,QAAQ,EAAE,MAAM,CAAA;YAChB,QAAQ,EAAE,KAAK,CAAA;SAChB,EAAE,CAAA;KACJ,CAAA;CACF,CAAA;AAkMD,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,mBAAmB,CAuCjE;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE;IAClC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC/B,OAAO,EAAE,OAAO,CAAA;IAChB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,OAAO,EAAE,sBAAsB,CAAA;IAC/B,SAAS,EAAE,cAAc,GAAG,SAAS,CAAA;IACrC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAA;IACjC,QAAQ,EAAE,OAAO,CAAA;IACjB,IAAI,EAAE,QAAQ,GAAG,SAAS,CAAA;IAC1B,GAAG,EAAE,YAAY,EAAE,CAAA;CACpB,GAAG,mBAAmB,CA+FtB"}
|
package/dist/manifest.js
CHANGED
|
@@ -3,6 +3,7 @@ import * as v from 'valibot';
|
|
|
3
3
|
import { bunderstackEmailEvents, bunderstackEmails, bunderstackFiles, bunderstackIdempotency, bunderstackJobs, } from './internal-tables.js';
|
|
4
4
|
import { parseCron } from './jobs/cron.js';
|
|
5
5
|
import { StandardSchemaValidationError, validateStandardSchema, } from './standard-schema.js';
|
|
6
|
+
const MAX_ENV_DESCRIPTION = 200;
|
|
6
7
|
const nonEmpty = v.pipe(v.string(), v.minLength(1));
|
|
7
8
|
const migrationDirectory = v.pipe(nonEmpty, v.check((value) => value.startsWith('/') ||
|
|
8
9
|
(!value.includes('\\') &&
|
|
@@ -39,7 +40,19 @@ const manifestSchema = v.strictObject({
|
|
|
39
40
|
key: nonEmpty,
|
|
40
41
|
required: v.boolean(),
|
|
41
42
|
scope: v.picklist(['server', 'client']),
|
|
43
|
+
sensitive: v.boolean(),
|
|
44
|
+
description: v.optional(v.pipe(nonEmpty, v.maxLength(MAX_ENV_DESCRIPTION))),
|
|
42
45
|
})),
|
|
46
|
+
api: v.strictObject({
|
|
47
|
+
operations: v.array(v.strictObject({
|
|
48
|
+
handle: nonEmpty,
|
|
49
|
+
operationId: nonEmpty,
|
|
50
|
+
effect: v.picklist(['read', 'mutation', 'unknown']),
|
|
51
|
+
method: v.optional(nonEmpty),
|
|
52
|
+
path: v.optional(nonEmpty),
|
|
53
|
+
summary: v.optional(v.pipe(nonEmpty, v.maxLength(200))),
|
|
54
|
+
})),
|
|
55
|
+
}),
|
|
43
56
|
background: v.strictObject({
|
|
44
57
|
jobs: v.array(v.strictObject({ name: nonEmpty })),
|
|
45
58
|
cron: v.array(v.strictObject({
|
|
@@ -71,7 +84,15 @@ function describeTables(schema) {
|
|
|
71
84
|
? [{ exportName, physicalName: getTableName(value), system: false }]
|
|
72
85
|
: []), (entry) => entry.physicalName);
|
|
73
86
|
}
|
|
74
|
-
function
|
|
87
|
+
function envDescription(key, value) {
|
|
88
|
+
const description = value.trim().replace(/\s+/g, ' ');
|
|
89
|
+
if (!description)
|
|
90
|
+
throw new Error(`[bunderstack] env.meta.${key}.description must not be empty`);
|
|
91
|
+
if (description.length > MAX_ENV_DESCRIPTION)
|
|
92
|
+
throw new Error(`[bunderstack] env.meta.${key}.description must be at most ${MAX_ENV_DESCRIPTION} characters`);
|
|
93
|
+
return description;
|
|
94
|
+
}
|
|
95
|
+
function describeSection(section, scope, meta) {
|
|
75
96
|
return Object.entries(section ?? {}).map(([key, schema]) => {
|
|
76
97
|
let required = false;
|
|
77
98
|
try {
|
|
@@ -82,7 +103,20 @@ function describeSection(section, scope) {
|
|
|
82
103
|
throw error;
|
|
83
104
|
required = true;
|
|
84
105
|
}
|
|
85
|
-
|
|
106
|
+
const entry = meta?.[key];
|
|
107
|
+
const sensitive = entry?.sensitive ?? scope === 'server';
|
|
108
|
+
if (scope === 'client' && sensitive) {
|
|
109
|
+
throw new Error(`[bunderstack] env.meta.${key}.sensitive must be false: client values ship to the browser`);
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
key,
|
|
113
|
+
required,
|
|
114
|
+
scope,
|
|
115
|
+
sensitive,
|
|
116
|
+
...(entry?.description === undefined
|
|
117
|
+
? {}
|
|
118
|
+
: { description: envDescription(key, entry.description) }),
|
|
119
|
+
};
|
|
86
120
|
});
|
|
87
121
|
}
|
|
88
122
|
function systemTables() {
|
|
@@ -120,20 +154,45 @@ export function parseManifest(value) {
|
|
|
120
154
|
rejectDuplicates('database export table', manifest.database.tables.map((entry) => entry.exportName));
|
|
121
155
|
rejectDuplicates('storage bucket', manifest.storage.buckets.map((entry) => entry.name));
|
|
122
156
|
rejectDuplicates('environment key', manifest.environment.map((entry) => entry.key));
|
|
157
|
+
rejectDuplicates('api operation', manifest.api.operations.map((entry) => entry.handle));
|
|
123
158
|
rejectDuplicates('background job', manifest.background.jobs.map((entry) => entry.name));
|
|
124
159
|
rejectDuplicates('background cron', manifest.background.cron.map((entry) => entry.name));
|
|
125
160
|
rejectDuplicates('background maintenance', manifest.background.maintenance.map((entry) => entry.name));
|
|
126
161
|
return manifest;
|
|
127
162
|
}
|
|
128
163
|
export function buildManifest(args) {
|
|
164
|
+
const declaredKeys = new Set([
|
|
165
|
+
...Object.keys(args.envConfig?.server ?? {}),
|
|
166
|
+
...Object.keys(args.envConfig?.client ?? {}),
|
|
167
|
+
]);
|
|
168
|
+
for (const key of Object.keys(args.envConfig?.meta ?? {})) {
|
|
169
|
+
if (!declaredKeys.has(key))
|
|
170
|
+
throw new Error(`[bunderstack] env.meta references undeclared key "${key}"`);
|
|
171
|
+
}
|
|
129
172
|
const environment = [
|
|
130
|
-
...describeSection(args.envConfig?.server, 'server'),
|
|
131
|
-
...describeSection(args.envConfig?.client, 'client'),
|
|
173
|
+
...describeSection(args.envConfig?.server, 'server', args.envConfig?.meta),
|
|
174
|
+
...describeSection(args.envConfig?.client, 'client', args.envConfig?.meta),
|
|
132
175
|
...(args.emailProvider === 'resend'
|
|
133
|
-
? [
|
|
176
|
+
? [
|
|
177
|
+
{
|
|
178
|
+
key: 'RESEND_API_KEY',
|
|
179
|
+
required: true,
|
|
180
|
+
scope: 'server',
|
|
181
|
+
sensitive: true,
|
|
182
|
+
description: 'Resend API key used to send transactional email',
|
|
183
|
+
},
|
|
184
|
+
]
|
|
134
185
|
: []),
|
|
135
186
|
...(args.emailProvider === 'smtp'
|
|
136
|
-
? [
|
|
187
|
+
? [
|
|
188
|
+
{
|
|
189
|
+
key: 'SMTP_URL',
|
|
190
|
+
required: true,
|
|
191
|
+
scope: 'server',
|
|
192
|
+
sensitive: true,
|
|
193
|
+
description: 'SMTP connection URL used to send transactional email',
|
|
194
|
+
},
|
|
195
|
+
]
|
|
137
196
|
: []),
|
|
138
197
|
];
|
|
139
198
|
rejectDuplicates('environment key', environment.map((entry) => entry.key));
|
|
@@ -153,6 +212,9 @@ export function buildManifest(args) {
|
|
|
153
212
|
},
|
|
154
213
|
realtime: { required: args.realtime },
|
|
155
214
|
environment: sortBy(environment, (entry) => entry.key),
|
|
215
|
+
api: {
|
|
216
|
+
operations: [...args.api].sort((left, right) => left.handle.localeCompare(right.handle)),
|
|
217
|
+
},
|
|
156
218
|
background: {
|
|
157
219
|
jobs: sortBy(Object.entries(args.jobs ?? {})
|
|
158
220
|
.filter(([, def]) => def.kind === 'job')
|
package/dist/manifest.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"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","sourcesContent":["import type { StandardSchemaV1 } from '@standard-schema/spec'\n\nimport { getTableName, isTable } from 'drizzle-orm'\nimport * as v from 'valibot'\n\nimport type { Dialect } from './dialect'\nimport type { EnvConfigInput } from './env'\nimport type { JobsDefs } from './jobs/define'\nimport type { ResolvedBucket, ResolvedStorageBuckets } from './storage/buckets'\n\nimport {\n bunderstackEmailEvents,\n bunderstackEmails,\n bunderstackFiles,\n bunderstackIdempotency,\n bunderstackJobs,\n} from './internal-tables'\nimport { parseCron } from './jobs/cron'\nimport {\n StandardSchemaValidationError,\n validateStandardSchema,\n} from './standard-schema'\n\nexport type ManifestEnvVar = {\n key: string\n required: boolean\n scope: 'server' | 'client'\n}\n\nexport type BunderstackManifest = {\n version: 3\n database: {\n dialect: Dialect\n migrationsDirectory: string\n tables: { exportName: string; physicalName: string; system: boolean }[]\n }\n storage: {\n defaultBucket: string\n buckets: { name: string; visibility: ResolvedBucket['visibility'] }[]\n }\n realtime: { required: boolean }\n environment: ManifestEnvVar[]\n background: {\n jobs: { name: string }[]\n cron: { name: string; schedule: string; timezone: 'UTC' }[]\n maintenance: {\n name: 'storage-sweep'\n schedule: string\n timezone: 'UTC'\n }[]\n }\n}\n\nconst nonEmpty = v.pipe(v.string(), v.minLength(1))\nconst migrationDirectory = v.pipe(\n nonEmpty,\n v.check(\n (value) =>\n value.startsWith('/') ||\n (!value.includes('\\\\') &&\n value.split('/').every((part) => part !== '..' && part !== '')),\n 'migrationsDirectory must be an absolute path or a relative path without traversal',\n ),\n)\nconst cronSchedule = v.pipe(\n nonEmpty,\n v.check((value) => {\n try {\n parseCron(value)\n return true\n } catch {\n return false\n }\n }, 'invalid cron schedule'),\n)\n\nconst manifestSchema = v.strictObject({\n version: v.literal(3),\n database: v.strictObject({\n dialect: v.picklist(['sqlite', 'pg']),\n migrationsDirectory: migrationDirectory,\n tables: v.array(\n v.strictObject({\n exportName: nonEmpty,\n physicalName: nonEmpty,\n system: v.boolean(),\n }),\n ),\n }),\n storage: v.strictObject({\n defaultBucket: nonEmpty,\n buckets: v.array(\n v.strictObject({\n name: nonEmpty,\n visibility: v.picklist(['public', 'private']),\n }),\n ),\n }),\n realtime: v.strictObject({ required: v.boolean() }),\n environment: v.array(\n v.strictObject({\n key: nonEmpty,\n required: v.boolean(),\n scope: v.picklist(['server', 'client']),\n }),\n ),\n background: v.strictObject({\n jobs: v.array(v.strictObject({ name: nonEmpty })),\n cron: v.array(\n v.strictObject({\n name: nonEmpty,\n schedule: cronSchedule,\n timezone: v.literal('UTC'),\n }),\n ),\n maintenance: v.array(\n v.strictObject({\n name: v.literal('storage-sweep'),\n schedule: cronSchedule,\n timezone: v.literal('UTC'),\n }),\n ),\n }),\n})\n\nfunction sortBy<T>(entries: readonly T[], key: (entry: T) => string): T[] {\n return [...entries].sort((left, right) => key(left).localeCompare(key(right)))\n}\n\nfunction rejectDuplicates(collection: string, values: readonly string[]): void {\n const seen = new Set<string>()\n for (const value of values) {\n if (seen.has(value))\n throw new Error(`[bunderstack] duplicate ${collection} \"${value}\"`)\n seen.add(value)\n }\n}\n\nfunction describeTables(schema: Record<string, unknown>) {\n const systemNames = new Set<string>(\n systemTables().map((table) => table.physicalName),\n )\n return sortBy(\n Object.entries(schema).flatMap(([exportName, value]) =>\n isTable(value) && !systemNames.has(getTableName(value))\n ? [{ exportName, physicalName: getTableName(value), system: false }]\n : [],\n ),\n (entry) => entry.physicalName,\n )\n}\n\nfunction describeSection(\n section: Record<string, StandardSchemaV1> | undefined,\n scope: ManifestEnvVar['scope'],\n): ManifestEnvVar[] {\n return Object.entries(section ?? {}).map(([key, schema]) => {\n let required = false\n try {\n validateStandardSchema(schema, undefined, 'env')\n } catch (error) {\n if (!(error instanceof StandardSchemaValidationError)) throw error\n required = true\n }\n return { key, required, scope }\n })\n}\n\nfunction systemTables() {\n return [\n {\n exportName: '_system.emailEvents',\n physicalName: getTableName(bunderstackEmailEvents),\n system: true,\n },\n {\n exportName: '_system.emails',\n physicalName: getTableName(bunderstackEmails),\n system: true,\n },\n {\n exportName: '_system.files',\n physicalName: getTableName(bunderstackFiles),\n system: true,\n },\n {\n exportName: '_system.idempotency',\n physicalName: getTableName(bunderstackIdempotency),\n system: true,\n },\n {\n exportName: '_system.jobs',\n physicalName: getTableName(bunderstackJobs),\n system: true,\n },\n ]\n}\n\nexport function parseManifest(value: unknown): BunderstackManifest {\n const manifest = validateStandardSchema(\n manifestSchema,\n value,\n 'manifest',\n ) as BunderstackManifest\n rejectDuplicates(\n 'database physical table',\n manifest.database.tables.map((entry) => entry.physicalName),\n )\n rejectDuplicates(\n 'database export table',\n manifest.database.tables.map((entry) => entry.exportName),\n )\n rejectDuplicates(\n 'storage bucket',\n manifest.storage.buckets.map((entry) => entry.name),\n )\n rejectDuplicates(\n 'environment key',\n manifest.environment.map((entry) => entry.key),\n )\n rejectDuplicates(\n 'background job',\n manifest.background.jobs.map((entry) => entry.name),\n )\n rejectDuplicates(\n 'background cron',\n manifest.background.cron.map((entry) => entry.name),\n )\n rejectDuplicates(\n 'background maintenance',\n manifest.background.maintenance.map((entry) => entry.name),\n )\n return manifest\n}\n\nexport function buildManifest(args: {\n schema: Record<string, unknown>\n dialect: Dialect\n migrationsDirectory: string\n storage: ResolvedStorageBuckets\n envConfig: EnvConfigInput | undefined\n emailProvider: string | undefined\n realtime: boolean\n jobs: JobsDefs | undefined\n}): BunderstackManifest {\n const environment = [\n ...describeSection(args.envConfig?.server, 'server'),\n ...describeSection(args.envConfig?.client, 'client'),\n ...(args.emailProvider === 'resend'\n ? [{ key: 'RESEND_API_KEY', required: true, scope: 'server' as const }]\n : []),\n ...(args.emailProvider === 'smtp'\n ? [{ key: 'SMTP_URL', required: true, scope: 'server' as const }]\n : []),\n ]\n rejectDuplicates(\n 'environment key',\n environment.map((entry) => entry.key),\n )\n\n return parseManifest({\n version: 3,\n database: {\n dialect: args.dialect,\n migrationsDirectory: args.migrationsDirectory,\n tables: sortBy(\n [...systemTables(), ...describeTables(args.schema)],\n (entry) => entry.physicalName,\n ),\n },\n storage: {\n defaultBucket: args.storage.defaultBucket,\n buckets: sortBy(\n [...args.storage.buckets.values()].map((bucket) => ({\n name: bucket.name,\n visibility: bucket.visibility,\n })),\n (bucket) => bucket.name,\n ),\n },\n realtime: { required: args.realtime },\n environment: sortBy(environment, (entry) => entry.key),\n background: {\n jobs: sortBy(\n Object.entries(args.jobs ?? {})\n .filter(([, def]) => def.kind === 'job')\n .map(([name]) => ({ name })),\n (entry) => entry.name,\n ),\n cron: sortBy(\n Object.entries(args.jobs ?? {})\n .filter(([, def]) => def.kind === 'cron')\n .map(([name, def]) => ({\n name,\n schedule: def.kind === 'cron' ? def.schedule : '',\n timezone: 'UTC' as const,\n })),\n (entry) => entry.name,\n ),\n maintenance: [\n {\n name: 'storage-sweep',\n schedule: '0 4 * * *',\n timezone: 'UTC' as const,\n },\n ],\n },\n })\n}\n"]}
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"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","sourcesContent":["import type { StandardSchemaV1 } from '@standard-schema/spec'\n\nimport { getTableName, isTable } from 'drizzle-orm'\nimport * as v from 'valibot'\n\nimport type { Dialect } from './dialect'\nimport type { EnvConfigInput, EnvVarMeta } from './env'\nimport type { JobsDefs } from './jobs/define'\nimport type { ResolvedBucket, ResolvedStorageBuckets } from './storage/buckets'\n\nimport {\n bunderstackEmailEvents,\n bunderstackEmails,\n bunderstackFiles,\n bunderstackIdempotency,\n bunderstackJobs,\n} from './internal-tables'\nimport { parseCron } from './jobs/cron'\nimport {\n StandardSchemaValidationError,\n validateStandardSchema,\n} from './standard-schema'\n\nexport type ManifestEnvVar = {\n key: string\n required: boolean\n scope: 'server' | 'client'\n sensitive: boolean\n description?: string\n}\n\nexport type ApiOperationEffect = 'read' | 'mutation' | 'unknown'\n\n/**\n * One application-declared procedure. `effect` is derived from the declared\n * HTTP method; `unknown` means the application declared no route, and a host\n * must treat it as at least as dangerous as a mutation.\n */\nexport type ApiOperation = {\n handle: string\n operationId: string\n effect: ApiOperationEffect\n method?: string\n path?: string\n summary?: string\n}\n\nexport type BunderstackManifest = {\n version: 3\n database: {\n dialect: Dialect\n migrationsDirectory: string\n tables: { exportName: string; physicalName: string; system: boolean }[]\n }\n storage: {\n defaultBucket: string\n buckets: { name: string; visibility: ResolvedBucket['visibility'] }[]\n }\n realtime: { required: boolean }\n environment: ManifestEnvVar[]\n api: { operations: ApiOperation[] }\n background: {\n jobs: { name: string }[]\n cron: { name: string; schedule: string; timezone: 'UTC' }[]\n maintenance: {\n name: 'storage-sweep'\n schedule: string\n timezone: 'UTC'\n }[]\n }\n}\n\nconst MAX_ENV_DESCRIPTION = 200\n\nconst nonEmpty = v.pipe(v.string(), v.minLength(1))\nconst migrationDirectory = v.pipe(\n nonEmpty,\n v.check(\n (value) =>\n value.startsWith('/') ||\n (!value.includes('\\\\') &&\n value.split('/').every((part) => part !== '..' && part !== '')),\n 'migrationsDirectory must be an absolute path or a relative path without traversal',\n ),\n)\nconst cronSchedule = v.pipe(\n nonEmpty,\n v.check((value) => {\n try {\n parseCron(value)\n return true\n } catch {\n return false\n }\n }, 'invalid cron schedule'),\n)\n\nconst manifestSchema = v.strictObject({\n version: v.literal(3),\n database: v.strictObject({\n dialect: v.picklist(['sqlite', 'pg']),\n migrationsDirectory: migrationDirectory,\n tables: v.array(\n v.strictObject({\n exportName: nonEmpty,\n physicalName: nonEmpty,\n system: v.boolean(),\n }),\n ),\n }),\n storage: v.strictObject({\n defaultBucket: nonEmpty,\n buckets: v.array(\n v.strictObject({\n name: nonEmpty,\n visibility: v.picklist(['public', 'private']),\n }),\n ),\n }),\n realtime: v.strictObject({ required: v.boolean() }),\n environment: v.array(\n v.strictObject({\n key: nonEmpty,\n required: v.boolean(),\n scope: v.picklist(['server', 'client']),\n sensitive: v.boolean(),\n description: v.optional(\n v.pipe(nonEmpty, v.maxLength(MAX_ENV_DESCRIPTION)),\n ),\n }),\n ),\n api: v.strictObject({\n operations: v.array(\n v.strictObject({\n handle: nonEmpty,\n operationId: nonEmpty,\n effect: v.picklist(['read', 'mutation', 'unknown']),\n method: v.optional(nonEmpty),\n path: v.optional(nonEmpty),\n summary: v.optional(v.pipe(nonEmpty, v.maxLength(200))),\n }),\n ),\n }),\n background: v.strictObject({\n jobs: v.array(v.strictObject({ name: nonEmpty })),\n cron: v.array(\n v.strictObject({\n name: nonEmpty,\n schedule: cronSchedule,\n timezone: v.literal('UTC'),\n }),\n ),\n maintenance: v.array(\n v.strictObject({\n name: v.literal('storage-sweep'),\n schedule: cronSchedule,\n timezone: v.literal('UTC'),\n }),\n ),\n }),\n})\n\nfunction sortBy<T>(entries: readonly T[], key: (entry: T) => string): T[] {\n return [...entries].sort((left, right) => key(left).localeCompare(key(right)))\n}\n\nfunction rejectDuplicates(collection: string, values: readonly string[]): void {\n const seen = new Set<string>()\n for (const value of values) {\n if (seen.has(value))\n throw new Error(`[bunderstack] duplicate ${collection} \"${value}\"`)\n seen.add(value)\n }\n}\n\nfunction describeTables(schema: Record<string, unknown>) {\n const systemNames = new Set<string>(\n systemTables().map((table) => table.physicalName),\n )\n return sortBy(\n Object.entries(schema).flatMap(([exportName, value]) =>\n isTable(value) && !systemNames.has(getTableName(value))\n ? [{ exportName, physicalName: getTableName(value), system: false }]\n : [],\n ),\n (entry) => entry.physicalName,\n )\n}\n\nfunction envDescription(key: string, value: string): string {\n const description = value.trim().replace(/\\s+/g, ' ')\n if (!description)\n throw new Error(\n `[bunderstack] env.meta.${key}.description must not be empty`,\n )\n if (description.length > MAX_ENV_DESCRIPTION)\n throw new Error(\n `[bunderstack] env.meta.${key}.description must be at most ${MAX_ENV_DESCRIPTION} characters`,\n )\n return description\n}\n\nfunction describeSection(\n section: Record<string, StandardSchemaV1> | undefined,\n scope: ManifestEnvVar['scope'],\n meta: Record<string, EnvVarMeta> | undefined,\n): ManifestEnvVar[] {\n return Object.entries(section ?? {}).map(([key, schema]) => {\n let required = false\n try {\n validateStandardSchema(schema, undefined, 'env')\n } catch (error) {\n if (!(error instanceof StandardSchemaValidationError)) throw error\n required = true\n }\n const entry = meta?.[key]\n const sensitive = entry?.sensitive ?? scope === 'server'\n if (scope === 'client' && sensitive) {\n throw new Error(\n `[bunderstack] env.meta.${key}.sensitive must be false: client values ship to the browser`,\n )\n }\n return {\n key,\n required,\n scope,\n sensitive,\n ...(entry?.description === undefined\n ? {}\n : { description: envDescription(key, entry.description) }),\n }\n })\n}\n\nfunction systemTables() {\n return [\n {\n exportName: '_system.emailEvents',\n physicalName: getTableName(bunderstackEmailEvents),\n system: true,\n },\n {\n exportName: '_system.emails',\n physicalName: getTableName(bunderstackEmails),\n system: true,\n },\n {\n exportName: '_system.files',\n physicalName: getTableName(bunderstackFiles),\n system: true,\n },\n {\n exportName: '_system.idempotency',\n physicalName: getTableName(bunderstackIdempotency),\n system: true,\n },\n {\n exportName: '_system.jobs',\n physicalName: getTableName(bunderstackJobs),\n system: true,\n },\n ]\n}\n\nexport function parseManifest(value: unknown): BunderstackManifest {\n const manifest = validateStandardSchema(\n manifestSchema,\n value,\n 'manifest',\n ) as BunderstackManifest\n rejectDuplicates(\n 'database physical table',\n manifest.database.tables.map((entry) => entry.physicalName),\n )\n rejectDuplicates(\n 'database export table',\n manifest.database.tables.map((entry) => entry.exportName),\n )\n rejectDuplicates(\n 'storage bucket',\n manifest.storage.buckets.map((entry) => entry.name),\n )\n rejectDuplicates(\n 'environment key',\n manifest.environment.map((entry) => entry.key),\n )\n rejectDuplicates(\n 'api operation',\n manifest.api.operations.map((entry) => entry.handle),\n )\n rejectDuplicates(\n 'background job',\n manifest.background.jobs.map((entry) => entry.name),\n )\n rejectDuplicates(\n 'background cron',\n manifest.background.cron.map((entry) => entry.name),\n )\n rejectDuplicates(\n 'background maintenance',\n manifest.background.maintenance.map((entry) => entry.name),\n )\n return manifest\n}\n\nexport function buildManifest(args: {\n schema: Record<string, unknown>\n dialect: Dialect\n migrationsDirectory: string\n storage: ResolvedStorageBuckets\n envConfig: EnvConfigInput | undefined\n emailProvider: string | undefined\n realtime: boolean\n jobs: JobsDefs | undefined\n api: ApiOperation[]\n}): BunderstackManifest {\n const declaredKeys = new Set([\n ...Object.keys(args.envConfig?.server ?? {}),\n ...Object.keys(args.envConfig?.client ?? {}),\n ])\n for (const key of Object.keys(args.envConfig?.meta ?? {})) {\n if (!declaredKeys.has(key))\n throw new Error(\n `[bunderstack] env.meta references undeclared key \"${key}\"`,\n )\n }\n const environment = [\n ...describeSection(args.envConfig?.server, 'server', args.envConfig?.meta),\n ...describeSection(args.envConfig?.client, 'client', args.envConfig?.meta),\n ...(args.emailProvider === 'resend'\n ? [\n {\n key: 'RESEND_API_KEY',\n required: true,\n scope: 'server' as const,\n sensitive: true,\n description: 'Resend API key used to send transactional email',\n },\n ]\n : []),\n ...(args.emailProvider === 'smtp'\n ? [\n {\n key: 'SMTP_URL',\n required: true,\n scope: 'server' as const,\n sensitive: true,\n description: 'SMTP connection URL used to send transactional email',\n },\n ]\n : []),\n ]\n rejectDuplicates(\n 'environment key',\n environment.map((entry) => entry.key),\n )\n\n return parseManifest({\n version: 3,\n database: {\n dialect: args.dialect,\n migrationsDirectory: args.migrationsDirectory,\n tables: sortBy(\n [...systemTables(), ...describeTables(args.schema)],\n (entry) => entry.physicalName,\n ),\n },\n storage: {\n defaultBucket: args.storage.defaultBucket,\n buckets: sortBy(\n [...args.storage.buckets.values()].map((bucket) => ({\n name: bucket.name,\n visibility: bucket.visibility,\n })),\n (bucket) => bucket.name,\n ),\n },\n realtime: { required: args.realtime },\n environment: sortBy(environment, (entry) => entry.key),\n api: {\n operations: [...args.api].sort((left, right) =>\n left.handle.localeCompare(right.handle),\n ),\n },\n background: {\n jobs: sortBy(\n Object.entries(args.jobs ?? {})\n .filter(([, def]) => def.kind === 'job')\n .map(([name]) => ({ name })),\n (entry) => entry.name,\n ),\n cron: sortBy(\n Object.entries(args.jobs ?? {})\n .filter(([, def]) => def.kind === 'cron')\n .map(([name, def]) => ({\n name,\n schedule: def.kind === 'cron' ? def.schedule : '',\n timezone: 'UTC' as const,\n })),\n (entry) => entry.name,\n ),\n maintenance: [\n {\n name: 'storage-sweep',\n schedule: '0 4 * * *',\n timezone: 'UTC' as const,\n },\n ],\n },\n })\n}\n"]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { AnyDb } from './dialect.js';
|
|
2
|
+
import type { ReadinessProbes } from './readiness.js';
|
|
3
|
+
export declare function createReadinessProbes(db: AnyDb, now?: () => number): Pick<ReadinessProbes, 'probeDatabase' | 'countOverdueJobs'>;
|
|
4
|
+
//# sourceMappingURL=readiness-probes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readiness-probes.d.ts","sourceRoot":"","sources":["../src/readiness-probes.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,WAAW,CAAA;AACtC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAOlD,wBAAgB,qBAAqB,CACnC,EAAE,EAAE,KAAK,EACT,GAAG,GAAE,MAAM,MAAiB,GAC3B,IAAI,CAAC,eAAe,EAAE,eAAe,GAAG,kBAAkB,CAAC,CAe7D"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// src/readiness-probes.ts — the internal jobs table doubles as the liveness
|
|
2
|
+
// query: reaching it proves the connection, and a missing-relation error proves
|
|
3
|
+
// the schema was never provisioned.
|
|
4
|
+
import { and, count, eq, lt } from 'drizzle-orm';
|
|
5
|
+
import { jobsTableFor } from './internal-tables.js';
|
|
6
|
+
/** A pending job this far past its `runAt` means nothing is draining the queue. */
|
|
7
|
+
const OVERDUE_MS = 60_000;
|
|
8
|
+
export function createReadinessProbes(db, now = Date.now) {
|
|
9
|
+
const t = jobsTableFor(db);
|
|
10
|
+
return {
|
|
11
|
+
probeDatabase: async () => {
|
|
12
|
+
await db.select({ id: t.id }).from(t).limit(1);
|
|
13
|
+
},
|
|
14
|
+
countOverdueJobs: async () => {
|
|
15
|
+
const rows = await db
|
|
16
|
+
.select({ overdue: count() })
|
|
17
|
+
.from(t)
|
|
18
|
+
.where(and(eq(t.status, 'pending'), lt(t.runAt, now() - OVERDUE_MS)));
|
|
19
|
+
return Number(rows[0]?.overdue ?? 0);
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=readiness-probes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readiness-probes.js","sourceRoot":"","sources":["../src/readiness-probes.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,gFAAgF;AAChF,oCAAoC;AACpC,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,aAAa,CAAA;AAKhD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAEhD,mFAAmF;AACnF,MAAM,UAAU,GAAG,MAAM,CAAA;AAEzB,MAAM,UAAU,qBAAqB,CACnC,EAAS,EACT,MAAoB,IAAI,CAAC,GAAG;IAE5B,MAAM,CAAC,GAAG,YAAY,CAAC,EAAE,CAAC,CAAA;IAE1B,OAAO;QACL,aAAa,EAAE,KAAK,IAAI,EAAE;YACxB,MAAM,EAAE,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;QAChD,CAAC;QACD,gBAAgB,EAAE,KAAK,IAAI,EAAE;YAC3B,MAAM,IAAI,GAAG,MAAM,EAAE;iBAClB,MAAM,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC;iBAC5B,IAAI,CAAC,CAAC,CAAC;iBACP,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,UAAU,CAAC,CAAC,CAAC,CAAA;YACvE,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,IAAI,CAAC,CAAC,CAAA;QACtC,CAAC;KACF,CAAA;AACH,CAAC","sourcesContent":["// src/readiness-probes.ts — the internal jobs table doubles as the liveness\n// query: reaching it proves the connection, and a missing-relation error proves\n// the schema was never provisioned.\nimport { and, count, eq, lt } from 'drizzle-orm'\n\nimport type { AnyDb } from './dialect'\nimport type { ReadinessProbes } from './readiness'\n\nimport { jobsTableFor } from './internal-tables'\n\n/** A pending job this far past its `runAt` means nothing is draining the queue. */\nconst OVERDUE_MS = 60_000\n\nexport function createReadinessProbes(\n db: AnyDb,\n now: () => number = Date.now,\n): Pick<ReadinessProbes, 'probeDatabase' | 'countOverdueJobs'> {\n const t = jobsTableFor(db)\n\n return {\n probeDatabase: async () => {\n await db.select({ id: t.id }).from(t).limit(1)\n },\n countOverdueJobs: async () => {\n const rows = await db\n .select({ overdue: count() })\n .from(t)\n .where(and(eq(t.status, 'pending'), lt(t.runAt, now() - OVERDUE_MS)))\n return Number(rows[0]?.overdue ?? 0)\n },\n }\n}\n"]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import * as v from 'valibot';
|
|
2
|
+
export type ReadinessStatus = 'ok' | 'degraded' | 'error' | 'skipped';
|
|
3
|
+
export type ReadinessCode = 'unreachable' | 'not_provisioned' | 'backlog';
|
|
4
|
+
export type ReadinessCheckName = 'database' | 'schema' | 'background';
|
|
5
|
+
export type ReadinessCheck = {
|
|
6
|
+
name: ReadinessCheckName;
|
|
7
|
+
status: ReadinessStatus;
|
|
8
|
+
code?: ReadinessCode;
|
|
9
|
+
overdue?: number;
|
|
10
|
+
};
|
|
11
|
+
export type ReadinessReport = {
|
|
12
|
+
status: 'ok' | 'degraded' | 'error';
|
|
13
|
+
revision?: string;
|
|
14
|
+
checks: ReadinessCheck[];
|
|
15
|
+
};
|
|
16
|
+
export type ReadinessProbes = {
|
|
17
|
+
/** Deployed commit, when the platform injects `BUNDERSTACK_REVISION`. */
|
|
18
|
+
revision?: string;
|
|
19
|
+
queueJobsDeclared: boolean;
|
|
20
|
+
probeDatabase: () => Promise<void>;
|
|
21
|
+
countOverdueJobs: () => Promise<number>;
|
|
22
|
+
};
|
|
23
|
+
export declare const readinessReportSchema: v.StrictObjectSchema<{
|
|
24
|
+
readonly status: v.PicklistSchema<["ok", "degraded", "error"], undefined>;
|
|
25
|
+
readonly revision: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
26
|
+
readonly checks: v.ArraySchema<v.StrictObjectSchema<{
|
|
27
|
+
readonly name: v.PicklistSchema<["database", "schema", "background"], undefined>;
|
|
28
|
+
readonly status: v.PicklistSchema<["ok", "degraded", "error", "skipped"], undefined>;
|
|
29
|
+
readonly code: v.OptionalSchema<v.PicklistSchema<["unreachable", "not_provisioned", "backlog"], undefined>, undefined>;
|
|
30
|
+
readonly overdue: v.OptionalSchema<v.NumberSchema<undefined>, undefined>;
|
|
31
|
+
}, undefined>, undefined>;
|
|
32
|
+
}, undefined>;
|
|
33
|
+
/**
|
|
34
|
+
* A query that fails this way reached the database and found no schema.
|
|
35
|
+
* Drizzle wraps the driver error, so the relation name lives on `cause`.
|
|
36
|
+
*/
|
|
37
|
+
export declare function isMissingRelationError(error: unknown): boolean;
|
|
38
|
+
export declare function buildReadinessReport(probes: ReadinessProbes): Promise<ReadinessReport>;
|
|
39
|
+
//# sourceMappingURL=readiness.d.ts.map
|