bunderstack 0.23.3 → 0.24.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 +68 -0
- package/README.md +6 -4
- package/dist/api/builder.d.ts +13 -11
- package/dist/api/builder.d.ts.map +1 -1
- package/dist/api/builder.js.map +1 -1
- package/dist/api/context.d.ts +6 -6
- package/dist/api/context.d.ts.map +1 -1
- package/dist/api/context.js +1 -1
- package/dist/api/context.js.map +1 -1
- package/dist/api/crud-router.d.ts +6 -6
- package/dist/api/realtime-router.d.ts +1 -1
- package/dist/api/storage-router.d.ts +5 -5
- package/dist/backend-internals.d.ts +5 -13
- package/dist/backend-internals.d.ts.map +1 -1
- package/dist/backend-internals.js.map +1 -1
- package/dist/backend.d.ts +39 -5
- package/dist/backend.d.ts.map +1 -1
- package/dist/backend.js +37 -46
- package/dist/backend.js.map +1 -1
- package/dist/blueprint-generator.d.ts +1 -0
- package/dist/blueprint-generator.d.ts.map +1 -1
- package/dist/blueprint-generator.js +29 -1
- package/dist/blueprint-generator.js.map +1 -1
- package/dist/blueprint.d.ts +2 -1
- package/dist/blueprint.d.ts.map +1 -1
- package/dist/blueprint.js +9 -2
- package/dist/blueprint.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +14 -2
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +13 -19
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -1
- package/dist/config.js.map +1 -1
- package/dist/email/smtp.d.ts +7 -1
- package/dist/email/smtp.d.ts.map +1 -1
- package/dist/email/smtp.js +5 -1
- package/dist/email/smtp.js.map +1 -1
- package/dist/email.d.ts +3 -29
- package/dist/email.d.ts.map +1 -1
- package/dist/email.js +1 -194
- package/dist/email.js.map +1 -1
- package/dist/env-probe.d.ts +9 -0
- package/dist/env-probe.d.ts.map +1 -0
- package/dist/env-probe.js +73 -0
- package/dist/env-probe.js.map +1 -0
- package/dist/env.d.ts +2 -5
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +2 -9
- package/dist/env.js.map +1 -1
- package/dist/hosted-contract.d.ts +11 -0
- package/dist/hosted-contract.d.ts.map +1 -0
- package/dist/hosted-contract.js +46 -0
- package/dist/hosted-contract.js.map +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.ts +15 -0
- package/dist/inspect.d.ts.map +1 -0
- package/dist/inspect.js +43 -0
- package/dist/inspect.js.map +1 -0
- package/dist/internal-tables-pg.d.ts +43 -94
- package/dist/internal-tables-pg.d.ts.map +1 -1
- package/dist/internal-tables-pg.js +15 -17
- package/dist/internal-tables-pg.js.map +1 -1
- package/dist/internal-tables.d.ts +213 -488
- package/dist/internal-tables.d.ts.map +1 -1
- package/dist/internal-tables.js +34 -33
- package/dist/internal-tables.js.map +1 -1
- package/dist/jobs/define.d.ts +20 -20
- package/dist/jobs/define.d.ts.map +1 -1
- package/dist/jobs/define.js.map +1 -1
- package/dist/jobs/worker.d.ts.map +1 -1
- package/dist/jobs/worker.js +34 -22
- package/dist/jobs/worker.js.map +1 -1
- package/dist/manifest-diff.d.ts +6 -0
- package/dist/manifest-diff.d.ts.map +1 -0
- package/dist/manifest-diff.js +42 -0
- package/dist/manifest-diff.js.map +1 -0
- package/dist/manifest.d.ts +10 -2
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +27 -29
- package/dist/manifest.js.map +1 -1
- package/dist/messaging/email.d.ts +16 -0
- package/dist/messaging/email.d.ts.map +1 -0
- package/dist/messaging/email.js +8 -0
- package/dist/messaging/email.js.map +1 -0
- package/dist/messaging/index.d.ts +8 -0
- package/dist/messaging/index.d.ts.map +1 -0
- package/dist/messaging/index.js +4 -0
- package/dist/messaging/index.js.map +1 -0
- package/dist/messaging/journal.d.ts +4 -0
- package/dist/messaging/journal.d.ts.map +1 -0
- package/dist/messaging/journal.js +19 -0
- package/dist/messaging/journal.js.map +1 -0
- package/dist/messaging/runtime.d.ts +16 -0
- package/dist/messaging/runtime.d.ts.map +1 -0
- package/dist/messaging/runtime.js +213 -0
- package/dist/messaging/runtime.js.map +1 -0
- package/dist/messaging/standalone.d.ts +27 -0
- package/dist/messaging/standalone.d.ts.map +1 -0
- package/dist/messaging/standalone.js +18 -0
- package/dist/messaging/standalone.js.map +1 -0
- package/dist/messaging/telegram.d.ts +16 -0
- package/dist/messaging/telegram.d.ts.map +1 -0
- package/dist/messaging/telegram.js +5 -0
- package/dist/messaging/telegram.js.map +1 -0
- package/dist/messaging/types.d.ts +22 -0
- package/dist/messaging/types.d.ts.map +1 -0
- package/dist/messaging/types.js +17 -0
- package/dist/messaging/types.js.map +1 -0
- package/dist/runtime.d.ts +11 -10
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +19 -31
- package/dist/runtime.js.map +1 -1
- package/dist/schema-export-pg.d.ts +1 -1
- package/dist/schema-export-pg.d.ts.map +1 -1
- package/dist/schema-export-pg.js +1 -1
- package/dist/schema-export-pg.js.map +1 -1
- package/dist/schema-export.d.ts +1 -1
- package/dist/schema-export.d.ts.map +1 -1
- package/dist/schema-export.js +1 -1
- package/dist/schema-export.js.map +1 -1
- package/dist/testing/fixture.d.ts +2 -2
- package/dist/testing/fixture.d.ts.map +1 -1
- package/dist/testing/fixture.js +11 -9
- package/dist/testing/fixture.js.map +1 -1
- package/dist/testing/messaging.d.ts +21 -0
- package/dist/testing/messaging.d.ts.map +1 -0
- package/dist/testing/messaging.js +34 -0
- package/dist/testing/messaging.js.map +1 -0
- package/dist/testing.d.ts +1 -0
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js.map +1 -1
- package/package.json +6 -2
- package/skills/creating-bunderstack-apps/references/application-structure.md +16 -7
- package/skills/migrating-to-bunderstack/SKILL.md +72 -50
- package/skills/migrating-to-bunderstack/references/audit-checklist.md +2 -2
- package/skills/migrating-to-bunderstack/references/runtime-replacements.md +16 -12
package/dist/backend.js
CHANGED
|
@@ -1,51 +1,37 @@
|
|
|
1
|
-
import { createApiBuilder } from './api/builder.js';
|
|
2
|
-
import { describeApiOperations } from './api/catalog.js';
|
|
3
1
|
import { BACKEND_INTERNALS } from './backend-internals.js';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { buildManifest } from './manifest.js';
|
|
2
|
+
import { validateEnv } from './env.js';
|
|
3
|
+
import { assertHostedBlueprintFile } from './hosted-contract.js';
|
|
4
|
+
import { inspectConfig } from './inspect.js';
|
|
8
5
|
import { materializeBunderstack } from './runtime.js';
|
|
9
|
-
import { STORAGE_SWEEP_JOB_NAME, STORAGE_SWEEP_SCHEDULE, } from './storage/background.js';
|
|
10
|
-
import { resolveBuckets } from './storage/buckets.js';
|
|
11
6
|
export function isBunderstackBackend(value) {
|
|
12
7
|
return (typeof value === 'object' && value !== null && BACKEND_INTERNALS in value);
|
|
13
8
|
}
|
|
14
|
-
export function bunderstack(
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
[STORAGE_SWEEP_JOB_NAME]: {
|
|
41
|
-
kind: 'cron',
|
|
42
|
-
schedule: STORAGE_SWEEP_SCHEDULE,
|
|
43
|
-
handler: () => { },
|
|
44
|
-
},
|
|
45
|
-
}
|
|
46
|
-
: jobsDefs,
|
|
47
|
-
});
|
|
48
|
-
const start = (source, overrides = {}) => materializeBunderstack({ ...config, jobs: jobsDefs, api: customApiRouter }, source, overrides);
|
|
9
|
+
export function bunderstack(declaration) {
|
|
10
|
+
const { env: envSchema, ...slots } = declaration;
|
|
11
|
+
const inspect = (source) => {
|
|
12
|
+
const env = validateEnv(envSchema, { source });
|
|
13
|
+
const resolve = (value) => typeof value === 'function'
|
|
14
|
+
? value(env)
|
|
15
|
+
: value;
|
|
16
|
+
const config = {
|
|
17
|
+
...slots,
|
|
18
|
+
database: resolve(slots.database),
|
|
19
|
+
storage: resolve(slots.storage),
|
|
20
|
+
messaging: resolve(slots.messaging),
|
|
21
|
+
realtime: resolve(slots.realtime),
|
|
22
|
+
};
|
|
23
|
+
return inspectConfig(config, envSchema, env);
|
|
24
|
+
};
|
|
25
|
+
const start = async (source, overrides = {}, inspected = inspect(source)) => {
|
|
26
|
+
if (source.BUNDERSTACK_BLUEPRINT_PATH) {
|
|
27
|
+
await assertHostedBlueprintFile(inspected.manifest, source.BUNDERSTACK_BLUEPRINT_PATH);
|
|
28
|
+
}
|
|
29
|
+
return materializeBunderstack({
|
|
30
|
+
...inspected.config,
|
|
31
|
+
jobs: inspected.jobsDefs,
|
|
32
|
+
api: inspected.customApiRouter,
|
|
33
|
+
}, source, overrides, inspected.env);
|
|
34
|
+
};
|
|
49
35
|
let backend;
|
|
50
36
|
const test = (async (options) => {
|
|
51
37
|
const testing = await import('./testing.js');
|
|
@@ -56,10 +42,15 @@ export function bunderstack(config) {
|
|
|
56
42
|
return testing.configureTestApp(backend, options)(overrides);
|
|
57
43
|
});
|
|
58
44
|
backend = {
|
|
59
|
-
|
|
60
|
-
|
|
45
|
+
inspect: ({ env } = {}) => inspect(env ?? process.env)
|
|
46
|
+
.manifest,
|
|
47
|
+
start: async ({ env } = {}) => await start(env ?? process.env),
|
|
61
48
|
test,
|
|
62
|
-
[BACKEND_INTERNALS]: {
|
|
49
|
+
[BACKEND_INTERNALS]: {
|
|
50
|
+
envSchema,
|
|
51
|
+
inspect,
|
|
52
|
+
start,
|
|
53
|
+
},
|
|
63
54
|
};
|
|
64
55
|
return backend;
|
|
65
56
|
}
|
package/dist/backend.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backend.js","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AnyRouter as AnyORPCRouter } from '@orpc/server'\n\nimport type { TableAccessInput } from './access'\nimport type { BunderstackApiBuilder } from './api/builder'\nimport type { BunderstackConfig } from './config'\nimport type { EnvConfigInput, ValidatedEnv } from './env'\nimport type { BunderstackJobsBuilder, JobsDefs } from './jobs'\nimport type { BunderstackManifest } from './manifest'\nimport type { BunderstackApp, BucketNamesOf, RuntimeOverrides } from './runtime'\nimport type { StorageConfigInput } from './storage/buckets'\nimport type { TestMethod, TestOptions } from './testing/fixture'\n\nimport { createApiBuilder } from './api/builder'\nimport { describeApiOperations } from './api/catalog'\nimport { BACKEND_INTERNALS, type BackendInternals } from './backend-internals'\nimport { detectDialect } from './dialect'\nimport { emailProviderTag } from './email'\nimport { createJobsBuilder, validateJobsDefs } from './jobs'\nimport { buildManifest } from './manifest'\nimport { materializeBunderstack } from './runtime'\nimport {\n STORAGE_SWEEP_JOB_NAME,\n STORAGE_SWEEP_SCHEDULE,\n} from './storage/background'\nimport { resolveBuckets } from './storage/buckets'\n\nexport type StartOptions = {\n env?: Record<string, string | undefined>\n}\n\nexport type BunderstackBackend<TApp> = {\n readonly manifest: BunderstackManifest\n start(options?: StartOptions): Promise<TApp>\n test: TestMethod<TApp>\n readonly [BACKEND_INTERNALS]: BackendInternals<TApp>\n}\n\nexport type BunderstackDefinitionConfig<\n TSchema extends Record<string, unknown>,\n TAccess extends Record<string, TableAccessInput> | undefined = undefined,\n TStorage extends StorageConfigInput | undefined = undefined,\n TEnv extends EnvConfigInput | undefined = undefined,\n TJobsDefs extends JobsDefs | undefined = undefined,\n TCustomApiRouter extends AnyORPCRouter | undefined = undefined,\n TRealtime = undefined,\n> = Omit<\n BunderstackConfig<TSchema, TAccess, TStorage, TEnv, TCustomApiRouter>,\n 'realtime'\n> & {\n realtime?: TRealtime\n jobs?:\n | TJobsDefs\n | ((\n builder: BunderstackJobsBuilder<TSchema, ValidatedEnv<TEnv>>,\n ) => TJobsDefs)\n}\n\nexport function isBunderstackBackend(\n value: unknown,\n): value is BunderstackBackend<unknown> {\n return (\n typeof value === 'object' && value !== null && BACKEND_INTERNALS in value\n )\n}\n\nexport function bunderstack<\n TSchema extends Record<string, unknown>,\n const TAccess extends Record<string, TableAccessInput> | undefined =\n undefined,\n const TStorage extends StorageConfigInput | undefined = undefined,\n const TEnv extends EnvConfigInput | undefined = undefined,\n const TJobsDefs extends JobsDefs | undefined = undefined,\n TCustomApiRouter extends AnyORPCRouter | undefined = undefined,\n const TRealtime extends BunderstackConfig<\n TSchema,\n TAccess,\n TStorage,\n TEnv,\n TCustomApiRouter\n >['realtime'] = undefined,\n>(\n config: BunderstackDefinitionConfig<\n TSchema,\n TAccess,\n TStorage,\n TEnv,\n TJobsDefs,\n TCustomApiRouter,\n TRealtime\n >,\n): BunderstackBackend<\n BunderstackApp<\n TSchema,\n TAccess,\n BucketNamesOf<TStorage>,\n TEnv,\n TJobsDefs,\n TCustomApiRouter,\n TRealtime\n >\n> {\n const dialect = detectDialect(config.schema)\n const jobsDefs = config.jobs\n ? typeof config.jobs === 'function'\n ? config.jobs(createJobsBuilder<TSchema, ValidatedEnv<TEnv>>())\n : config.jobs\n : undefined\n if (jobsDefs) validateJobsDefs(jobsDefs)\n\n // Resolved here rather than at start: the manifest has to describe the same\n // router the runtime will serve, and `bunderstack blueprint` never starts.\n const customApiRouter =\n typeof config.api === 'function'\n ? (\n config.api as (\n builder: BunderstackApiBuilder<TSchema, ValidatedEnv<TEnv>>,\n ) => TCustomApiRouter\n )(createApiBuilder<TSchema, ValidatedEnv<TEnv>>())\n : config.api\n\n const manifest = buildManifest({\n schema: config.schema,\n dialect,\n migrationsDirectory: config.database.migrations ?? './migrations',\n storage: resolveBuckets(config.storage, {}),\n envConfig: config.env,\n emailProvider: emailProviderTag(config.email),\n realtime: Boolean(config.realtime),\n api: describeApiOperations(customApiRouter),\n jobs: config.storage\n ? {\n ...jobsDefs,\n [STORAGE_SWEEP_JOB_NAME]: {\n kind: 'cron',\n schedule: STORAGE_SWEEP_SCHEDULE,\n handler: () => {},\n },\n }\n : jobsDefs,\n })\n\n type App = BunderstackApp<\n TSchema,\n TAccess,\n BucketNamesOf<TStorage>,\n TEnv,\n TJobsDefs,\n TCustomApiRouter,\n TRealtime\n >\n\n const start = (\n source: Record<string, string | undefined>,\n overrides: RuntimeOverrides = {},\n ): Promise<App> =>\n materializeBunderstack(\n { ...config, jobs: jobsDefs, api: customApiRouter } as never,\n source,\n overrides,\n ) as Promise<App>\n\n let backend: BunderstackBackend<App>\n const test = (async (options) => {\n const testing = await import('./testing')\n return testing.createTestApp(backend, options)\n }) as TestMethod<App>\n test.configure = (options) =>\n (async (overrides: TestOptions = {}) => {\n const testing = await import('./testing')\n return testing.configureTestApp(backend, options)(overrides)\n }) as never\n backend = {\n manifest,\n start: ({ env } = {}) =>\n start(env ?? (process.env as Record<string, string | undefined>)),\n test,\n [BACKEND_INTERNALS]: { declaration: { config, jobsDefs }, start },\n }\n return backend\n}\n"]}
|
|
1
|
+
{"version":3,"file":"backend.js","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AnyRouter as AnyORPCRouter } from '@orpc/server'\n\nimport type { TableAccessInput } from './access'\nimport type { BunderstackConfig, RealtimeConfigInput } from './config'\nimport type { EnvConfigInput, ValidatedEnv } from './env'\nimport type { BunderstackJobsBuilder, JobsDefs } from './jobs'\nimport type { BunderstackManifest } from './manifest'\nimport type { MessagingConfig } from './messaging'\nimport type { BunderstackApp, BucketNamesOf, RuntimeOverrides } from './runtime'\nimport type { StorageConfigInput } from './storage/buckets'\nimport type { TestMethod, TestOptions } from './testing/fixture'\n\nimport { BACKEND_INTERNALS, type BackendInternals } from './backend-internals'\nimport { validateEnv } from './env'\nimport { assertHostedBlueprintFile } from './hosted-contract'\nimport { inspectConfig, type InspectedDefinition } from './inspect'\nimport { materializeBunderstack } from './runtime'\n\nexport type StartOptions = {\n env?: Record<string, string | undefined>\n}\n\nexport type BunderstackBackend<TApp> = {\n inspect(options?: StartOptions): BunderstackManifest\n start(options?: StartOptions): Promise<TApp>\n test: TestMethod<TApp>\n readonly [BACKEND_INTERNALS]: BackendInternals<TApp>\n}\n\nexport type BunderstackDefinitionConfig<\n TSchema extends Record<string, unknown>,\n TAccess extends Record<string, TableAccessInput> | undefined = undefined,\n TStorage extends StorageConfigInput | undefined = undefined,\n TEnv extends EnvConfigInput | undefined = undefined,\n TJobsDefs extends JobsDefs | undefined = undefined,\n TCustomApiRouter extends AnyORPCRouter | undefined = undefined,\n TRealtime = undefined,\n TMessaging extends MessagingConfig | undefined = undefined,\n> = Omit<\n BunderstackConfig<TSchema, TAccess, TStorage, TEnv, TCustomApiRouter>,\n 'realtime' | 'messaging' | 'api'\n> & {\n /**\n * An inline `api` or `jobs` builder receives the open `MessagingConfig`, not\n * `TMessaging`. Naming `TMessaging` in a callback parameter makes TypeScript\n * fix it before it reads the sibling `messaging` property, and the whole\n * declaration then loses its inferred channels. Declare the builder in its\n * own module with an annotated parameter to type `ctx.messaging` exactly —\n * `defineApi({ schema, env, messaging })` and `BunderstackJobsBuilder` both\n * take the channel record.\n */\n realtime?: TRealtime\n messaging?: TMessaging\n api?:\n | TCustomApiRouter\n | ((\n builder: import('./api/builder').BunderstackApiBuilder<\n TSchema,\n ValidatedEnv<TEnv>,\n MessagingConfig\n >,\n ) => TCustomApiRouter)\n jobs?:\n | TJobsDefs\n | ((\n builder: BunderstackJobsBuilder<\n TSchema,\n ValidatedEnv<TEnv>,\n MessagingConfig\n >,\n ) => TJobsDefs)\n}\n\nexport function isBunderstackBackend(\n value: unknown,\n): value is BunderstackBackend<unknown> {\n return (\n typeof value === 'object' && value !== null && BACKEND_INTERNALS in value\n )\n}\n\n/**\n * A configuration slot that may read validated environment values. The shape a\n * slot produces reaches the manifest, so a callback belongs here to supply a\n * key, a URL, or a sender — never to change which channels, buckets, or tables\n * exist. Blueprint generation resolves the declaration against two accepted\n * environments and rejects a shape that differs between them.\n */\nexport type EnvAware<TEnv extends EnvConfigInput | undefined, T> =\n | T\n | ((env: ValidatedEnv<NoInfer<TEnv>>) => T)\n\nexport type DatabaseSlot = BunderstackConfig<\n Record<string, unknown>,\n undefined,\n undefined,\n undefined,\n undefined\n>['database']\n\n/**\n * The whole application in one object. `schema`, `access`, and the routers are\n * plain data; the slots that hold credentials also accept a function of the\n * validated environment.\n */\nexport type BunderstackDeclaration<\n TSchema extends Record<string, unknown>,\n TEnv extends EnvConfigInput | undefined = undefined,\n TAccess extends Record<string, TableAccessInput> | undefined = undefined,\n TStorage extends StorageConfigInput | undefined = undefined,\n TJobsDefs extends JobsDefs | undefined = undefined,\n TCustomApiRouter extends AnyORPCRouter | undefined = undefined,\n TRealtime = undefined,\n TMessaging extends MessagingConfig | undefined = undefined,\n> = Omit<\n BunderstackDefinitionConfig<\n TSchema,\n TAccess,\n TStorage,\n TEnv,\n TJobsDefs,\n TCustomApiRouter,\n TRealtime,\n TMessaging\n >,\n 'database' | 'storage' | 'messaging' | 'realtime'\n> & {\n /** Declared environment. Its names reach the blueprint; its values never do. */\n env?: TEnv\n database: EnvAware<TEnv, DatabaseSlot>\n storage?: EnvAware<TEnv, TStorage>\n messaging?: EnvAware<TEnv, TMessaging>\n realtime?: EnvAware<TEnv, TRealtime>\n}\n\nexport function bunderstack<\n TSchema extends Record<string, unknown>,\n const TEnv extends EnvConfigInput | undefined = undefined,\n const TAccess extends Record<string, TableAccessInput> | undefined =\n undefined,\n const TStorage extends StorageConfigInput | undefined = undefined,\n const TJobsDefs extends JobsDefs | undefined = undefined,\n TCustomApiRouter extends AnyORPCRouter | undefined = undefined,\n const TRealtime extends RealtimeConfigInput | undefined = undefined,\n const TMessaging extends MessagingConfig | undefined = undefined,\n>(\n declaration: BunderstackDeclaration<\n TSchema,\n TEnv,\n TAccess,\n TStorage,\n TJobsDefs,\n TCustomApiRouter,\n TRealtime,\n TMessaging\n >,\n): BunderstackBackend<\n BunderstackApp<\n TSchema,\n TAccess,\n BucketNamesOf<TStorage>,\n TEnv,\n TJobsDefs,\n TCustomApiRouter,\n TRealtime,\n TMessaging\n >\n>\nexport function bunderstack(\n declaration: BunderstackDeclaration<Record<string, unknown>, any>,\n): BunderstackBackend<any> {\n const { env: envSchema, ...slots } = declaration as Record<\n string,\n unknown\n > & {\n env?: EnvConfigInput\n }\n const inspect = (source: Record<string, string | undefined>) => {\n const env = validateEnv(envSchema, { source })\n const resolve = (value: unknown) =>\n typeof value === 'function'\n ? (value as (given: typeof env) => unknown)(env)\n : value\n const config = {\n ...slots,\n database: resolve(slots.database),\n storage: resolve(slots.storage),\n messaging: resolve(slots.messaging),\n realtime: resolve(slots.realtime),\n }\n return inspectConfig(config as never, envSchema, env)\n }\n\n type App = BunderstackApp<any, any, any, any, any, any, any, any>\n\n const start = async (\n source: Record<string, string | undefined>,\n overrides: RuntimeOverrides = {},\n inspected: InspectedDefinition = inspect(source),\n ): Promise<App> => {\n if (source.BUNDERSTACK_BLUEPRINT_PATH) {\n await assertHostedBlueprintFile(\n inspected.manifest,\n source.BUNDERSTACK_BLUEPRINT_PATH,\n )\n }\n return materializeBunderstack(\n {\n ...inspected.config,\n jobs: inspected.jobsDefs,\n api: inspected.customApiRouter,\n } as never,\n source,\n overrides,\n inspected.env,\n ) as Promise<App>\n }\n\n let backend: BunderstackBackend<App>\n const test = (async (options) => {\n const testing = await import('./testing')\n return testing.createTestApp(backend, options)\n }) as TestMethod<App>\n test.configure = (options) =>\n (async (overrides: TestOptions = {}) => {\n const testing = await import('./testing')\n return testing.configureTestApp(backend, options)(overrides)\n }) as never\n backend = {\n inspect: ({ env } = {}) =>\n inspect(env ?? (process.env as Record<string, string | undefined>))\n .manifest,\n start: async ({ env } = {}) =>\n await start(env ?? (process.env as Record<string, string | undefined>)),\n test,\n [BACKEND_INTERNALS]: {\n envSchema,\n inspect,\n start,\n },\n }\n return backend\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"blueprint-generator.d.ts","sourceRoot":"","sources":["../src/blueprint-generator.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"blueprint-generator.d.ts","sourceRoot":"","sources":["../src/blueprint-generator.ts"],"names":[],"mappings":"AAaA,OAAO,EAGL,KAAK,oBAAoB,EAC1B,MAAM,aAAa,CAAA;AAOpB,MAAM,MAAM,wBAAwB,GAAG;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB,CAAA;AAED,MAAM,MAAM,uBAAuB,GAAG;IACpC,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,oBAAoB,CAAA;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,OAAO,CAAA;CACjB,CAAA;AAED,qBAAa,mBAAoB,SAAQ,KAAK;;CAO7C;AAqED,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,uBAAuB,CAAC,CAuHlC"}
|
|
@@ -2,8 +2,13 @@ import { mkdir, readFile, realpath, rename, rm, writeFile, } from 'node:fs/promi
|
|
|
2
2
|
import { dirname, isAbsolute, join, relative, resolve } from 'node:path';
|
|
3
3
|
import { pathToFileURL } from 'node:url';
|
|
4
4
|
import { isBunderstackBackend } from './backend.js';
|
|
5
|
+
import { BACKEND_INTERNALS } from './backend-internals.js';
|
|
5
6
|
import { blueprintFromManifest, serializeBlueprint, } from './blueprint.js';
|
|
7
|
+
import { parseBlueprintYaml } from './blueprint.js';
|
|
8
|
+
import { createEnvProbeSources } from './env-probe.js';
|
|
9
|
+
import { assertManifestMatchesBlueprint } from './hosted-contract.js';
|
|
6
10
|
import { parseManifest } from './manifest.js';
|
|
11
|
+
import { diffManifests } from './manifest-diff.js';
|
|
7
12
|
export class BlueprintCheckError extends Error {
|
|
8
13
|
constructor() {
|
|
9
14
|
super('bunderstack.blueprint.yaml is missing or stale; run `bunderstack blueprint`');
|
|
@@ -78,7 +83,30 @@ export async function generateBlueprint(options) {
|
|
|
78
83
|
if (!isBunderstackBackend(backend)) {
|
|
79
84
|
throw new Error(`[bunderstack] ${entry} must export backend`);
|
|
80
85
|
}
|
|
81
|
-
|
|
86
|
+
if (options.hostedCheck) {
|
|
87
|
+
const file = Bun.file(outputPath);
|
|
88
|
+
if (!(await file.exists())) {
|
|
89
|
+
throw new Error(`[bunderstack] hosted blueprint does not exist: ${output}`);
|
|
90
|
+
}
|
|
91
|
+
const source = await file.text();
|
|
92
|
+
const manifest = parseManifest(backend.inspect({ env: process.env }));
|
|
93
|
+
assertManifestMatchesBlueprint(manifest, source);
|
|
94
|
+
return {
|
|
95
|
+
path: outputPath,
|
|
96
|
+
blueprint: parseBlueprintYaml(source),
|
|
97
|
+
source,
|
|
98
|
+
changed: false,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
const probeSources = createEnvProbeSources(backend[BACKEND_INTERNALS].envSchema, process.env);
|
|
102
|
+
const firstManifest = parseManifest(backend.inspect({ env: probeSources[0] }));
|
|
103
|
+
const secondManifest = parseManifest(backend.inspect({ env: probeSources[1] }));
|
|
104
|
+
const differences = diffManifests(firstManifest, secondManifest);
|
|
105
|
+
if (differences.length > 0) {
|
|
106
|
+
throw new Error('[bunderstack] environment-dependent blueprint shape:\n' +
|
|
107
|
+
differences.map(({ kind, path }) => ` - ${kind}: ${path}`).join('\n'));
|
|
108
|
+
}
|
|
109
|
+
const manifest = firstManifest;
|
|
82
110
|
const workerRequired = manifest.background.jobs.length > 0;
|
|
83
111
|
requireScript(pkg, 'worker', workerRequired);
|
|
84
112
|
const migrationsDirectory = normalizeProjectPath(directory, manifest.database.migrationsDirectory, 'migrationsDirectory');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"blueprint-generator.js","sourceRoot":"","sources":["../src/blueprint-generator.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n mkdir,\n readFile,\n realpath,\n rename,\n rm,\n writeFile,\n} from 'node:fs/promises'\nimport { dirname, isAbsolute, join, relative, resolve } from 'node:path'\nimport { pathToFileURL } from 'node:url'\n\nimport { isBunderstackBackend } from './backend'\nimport {\n blueprintFromManifest,\n serializeBlueprint,\n type BunderstackBlueprint,\n} from './blueprint'\nimport { parseManifest } from './manifest'\n\nexport type GenerateBlueprintOptions = {\n directory: string\n entry?: string\n output?: string\n check?: boolean\n}\n\nexport type GenerateBlueprintResult = {\n path: string\n blueprint: BunderstackBlueprint\n source: string\n changed: boolean\n}\n\nexport class BlueprintCheckError extends Error {\n constructor() {\n super(\n 'bunderstack.blueprint.yaml is missing or stale; run `bunderstack blueprint`',\n )\n this.name = 'BlueprintCheckError'\n }\n}\n\ntype AppPackage = {\n scripts?: Record<string, unknown>\n dependencies?: Record<string, unknown>\n devDependencies?: Record<string, unknown>\n bunderstack?: { entry?: unknown }\n}\n\nfunction requireRelativePath(value: string, label: string): string {\n const normalized = value.replaceAll('\\\\', '/')\n if (\n !normalized ||\n isAbsolute(normalized) ||\n normalized.split('/').some((part) => !part || part === '..')\n ) {\n throw new Error(\n `[bunderstack] ${label} must be a relative path without traversal`,\n )\n }\n return normalized\n}\n\nfunction resolveWithin(root: string, value: string, label: string): string {\n const path = resolve(root, requireRelativePath(value, label))\n const pathFromRoot = relative(root, path)\n if (\n pathFromRoot === '..' ||\n pathFromRoot.startsWith('../') ||\n isAbsolute(pathFromRoot)\n ) {\n throw new Error(\n `[bunderstack] ${label} must stay within the application directory`,\n )\n }\n return path\n}\n\nfunction normalizeProjectPath(\n root: string,\n value: string,\n label: string,\n): string {\n if (!isAbsolute(value)) return requireRelativePath(value, label)\n const pathFromRoot = relative(root, resolve(value)) || '.'\n return requireRelativePath(pathFromRoot, label)\n}\n\nfunction requireScript(\n pkg: AppPackage,\n name: 'build' | 'start' | 'worker',\n required: boolean,\n): boolean {\n const value = pkg.scripts?.[name]\n if (typeof value === 'string' && value.trim()) return true\n if (required)\n throw new Error(\n `[bunderstack] package.json requires a non-empty \"${name}\" script`,\n )\n return false\n}\n\nasync function packageVersion(): Promise<string> {\n const pkg = (await Bun.file(\n new URL('../package.json', import.meta.url),\n ).json()) as { version: string }\n return pkg.version\n}\n\nexport async function generateBlueprint(\n options: GenerateBlueprintOptions,\n): Promise<GenerateBlueprintResult> {\n const directory = await realpath(resolve(options.directory))\n const packagePath = join(directory, 'package.json')\n const pkg = JSON.parse(await readFile(packagePath, 'utf8')) as AppPackage\n const allDependencies = { ...pkg.dependencies, ...pkg.devDependencies }\n let framework: 'tanstack-start' | 'solid' | 'bun-ssr' = 'bun-ssr'\n if (typeof allDependencies['@tanstack/react-start'] === 'string') {\n framework = 'tanstack-start'\n } else if (\n typeof allDependencies['solid-js'] === 'string' ||\n typeof allDependencies['@solidjs/web'] === 'string'\n ) {\n framework = 'solid'\n }\n requireScript(pkg, 'build', true)\n requireScript(pkg, 'start', true)\n\n const configuredEntry = pkg.bunderstack?.entry\n const entry = requireRelativePath(\n options.entry ??\n (typeof configuredEntry === 'string'\n ? configuredEntry\n : 'src/bunderstack.ts'),\n 'entry',\n )\n const entryPath = resolveWithin(directory, entry, 'entry')\n if (!(await Bun.file(entryPath).exists())) {\n throw new Error(`[bunderstack] entry does not exist: ${entry}`)\n }\n const output = requireRelativePath(\n options.output ?? 'bunderstack.blueprint.yaml',\n 'output',\n )\n const outputPath = resolveWithin(directory, output, 'output')\n\n const module = (await import(\n `${pathToFileURL(entryPath).href}?blueprint=${Date.now()}`\n )) as { backend?: unknown }\n const backend = module.backend\n if (!isBunderstackBackend(backend)) {\n throw new Error(`[bunderstack] ${entry} must export backend`)\n }\n const manifest = parseManifest(backend.manifest)\n const workerRequired = manifest.background.jobs.length > 0\n requireScript(pkg, 'worker', workerRequired)\n const migrationsDirectory = normalizeProjectPath(\n directory,\n manifest.database.migrationsDirectory,\n 'migrationsDirectory',\n )\n const migrationJournal = join(\n resolveWithin(directory, migrationsDirectory, 'migrationsDirectory'),\n 'meta',\n '_journal.json',\n )\n const migrationMode = (await Bun.file(migrationJournal).exists())\n ? 'migrations'\n : 'push'\n const blueprint = blueprintFromManifest({\n manifest: {\n ...manifest,\n database: { ...manifest.database, migrationsDirectory },\n },\n generatorVersion: await packageVersion(),\n entry,\n migrationMode,\n framework,\n })\n const source = serializeBlueprint(blueprint)\n const existing = (await Bun.file(outputPath).exists())\n ? await Bun.file(outputPath).text()\n : undefined\n if (options.check) {\n if (existing !== source) throw new BlueprintCheckError()\n return { path: outputPath, blueprint, source, changed: false }\n }\n if (existing === source)\n return { path: outputPath, blueprint, source, changed: false }\n await mkdir(dirname(outputPath), { recursive: true })\n const temporary = `${outputPath}.${process.pid}.${Date.now()}.tmp`\n try {\n await writeFile(temporary, source, { mode: 0o600 })\n await rename(temporary, outputPath)\n } finally {\n await rm(temporary, { force: true })\n }\n return { path: outputPath, blueprint, source, changed: true }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"blueprint-generator.js","sourceRoot":"","sources":["../src/blueprint-generator.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n mkdir,\n readFile,\n realpath,\n rename,\n rm,\n writeFile,\n} from 'node:fs/promises'\nimport { dirname, isAbsolute, join, relative, resolve } from 'node:path'\nimport { pathToFileURL } from 'node:url'\n\nimport { isBunderstackBackend } from './backend'\nimport { BACKEND_INTERNALS } from './backend-internals'\nimport {\n blueprintFromManifest,\n serializeBlueprint,\n type BunderstackBlueprint,\n} from './blueprint'\nimport { parseBlueprintYaml } from './blueprint'\nimport { createEnvProbeSources } from './env-probe'\nimport { assertManifestMatchesBlueprint } from './hosted-contract'\nimport { parseManifest } from './manifest'\nimport { diffManifests } from './manifest-diff'\n\nexport type GenerateBlueprintOptions = {\n directory: string\n entry?: string\n output?: string\n check?: boolean\n hostedCheck?: boolean\n}\n\nexport type GenerateBlueprintResult = {\n path: string\n blueprint: BunderstackBlueprint\n source: string\n changed: boolean\n}\n\nexport class BlueprintCheckError extends Error {\n constructor() {\n super(\n 'bunderstack.blueprint.yaml is missing or stale; run `bunderstack blueprint`',\n )\n this.name = 'BlueprintCheckError'\n }\n}\n\ntype AppPackage = {\n scripts?: Record<string, unknown>\n dependencies?: Record<string, unknown>\n devDependencies?: Record<string, unknown>\n bunderstack?: { entry?: unknown }\n}\n\nfunction requireRelativePath(value: string, label: string): string {\n const normalized = value.replaceAll('\\\\', '/')\n if (\n !normalized ||\n isAbsolute(normalized) ||\n normalized.split('/').some((part) => !part || part === '..')\n ) {\n throw new Error(\n `[bunderstack] ${label} must be a relative path without traversal`,\n )\n }\n return normalized\n}\n\nfunction resolveWithin(root: string, value: string, label: string): string {\n const path = resolve(root, requireRelativePath(value, label))\n const pathFromRoot = relative(root, path)\n if (\n pathFromRoot === '..' ||\n pathFromRoot.startsWith('../') ||\n isAbsolute(pathFromRoot)\n ) {\n throw new Error(\n `[bunderstack] ${label} must stay within the application directory`,\n )\n }\n return path\n}\n\nfunction normalizeProjectPath(\n root: string,\n value: string,\n label: string,\n): string {\n if (!isAbsolute(value)) return requireRelativePath(value, label)\n const pathFromRoot = relative(root, resolve(value)) || '.'\n return requireRelativePath(pathFromRoot, label)\n}\n\nfunction requireScript(\n pkg: AppPackage,\n name: 'build' | 'start' | 'worker',\n required: boolean,\n): boolean {\n const value = pkg.scripts?.[name]\n if (typeof value === 'string' && value.trim()) return true\n if (required)\n throw new Error(\n `[bunderstack] package.json requires a non-empty \"${name}\" script`,\n )\n return false\n}\n\nasync function packageVersion(): Promise<string> {\n const pkg = (await Bun.file(\n new URL('../package.json', import.meta.url),\n ).json()) as { version: string }\n return pkg.version\n}\n\nexport async function generateBlueprint(\n options: GenerateBlueprintOptions,\n): Promise<GenerateBlueprintResult> {\n const directory = await realpath(resolve(options.directory))\n const packagePath = join(directory, 'package.json')\n const pkg = JSON.parse(await readFile(packagePath, 'utf8')) as AppPackage\n const allDependencies = { ...pkg.dependencies, ...pkg.devDependencies }\n let framework: 'tanstack-start' | 'solid' | 'bun-ssr' = 'bun-ssr'\n if (typeof allDependencies['@tanstack/react-start'] === 'string') {\n framework = 'tanstack-start'\n } else if (\n typeof allDependencies['solid-js'] === 'string' ||\n typeof allDependencies['@solidjs/web'] === 'string'\n ) {\n framework = 'solid'\n }\n requireScript(pkg, 'build', true)\n requireScript(pkg, 'start', true)\n\n const configuredEntry = pkg.bunderstack?.entry\n const entry = requireRelativePath(\n options.entry ??\n (typeof configuredEntry === 'string'\n ? configuredEntry\n : 'src/bunderstack.ts'),\n 'entry',\n )\n const entryPath = resolveWithin(directory, entry, 'entry')\n if (!(await Bun.file(entryPath).exists())) {\n throw new Error(`[bunderstack] entry does not exist: ${entry}`)\n }\n const output = requireRelativePath(\n options.output ?? 'bunderstack.blueprint.yaml',\n 'output',\n )\n const outputPath = resolveWithin(directory, output, 'output')\n\n const module = (await import(\n `${pathToFileURL(entryPath).href}?blueprint=${Date.now()}`\n )) as { backend?: unknown }\n const backend = module.backend\n if (!isBunderstackBackend(backend)) {\n throw new Error(`[bunderstack] ${entry} must export backend`)\n }\n if (options.hostedCheck) {\n const file = Bun.file(outputPath)\n if (!(await file.exists())) {\n throw new Error(\n `[bunderstack] hosted blueprint does not exist: ${output}`,\n )\n }\n const source = await file.text()\n const manifest = parseManifest(backend.inspect({ env: process.env }))\n assertManifestMatchesBlueprint(manifest, source)\n return {\n path: outputPath,\n blueprint: parseBlueprintYaml(source),\n source,\n changed: false,\n }\n }\n const probeSources = createEnvProbeSources(\n backend[BACKEND_INTERNALS].envSchema,\n process.env as Record<string, string | undefined>,\n )\n const firstManifest = parseManifest(backend.inspect({ env: probeSources[0] }))\n const secondManifest = parseManifest(\n backend.inspect({ env: probeSources[1] }),\n )\n const differences = diffManifests(firstManifest, secondManifest)\n if (differences.length > 0) {\n throw new Error(\n '[bunderstack] environment-dependent blueprint shape:\\n' +\n differences.map(({ kind, path }) => ` - ${kind}: ${path}`).join('\\n'),\n )\n }\n const manifest = firstManifest\n const workerRequired = manifest.background.jobs.length > 0\n requireScript(pkg, 'worker', workerRequired)\n const migrationsDirectory = normalizeProjectPath(\n directory,\n manifest.database.migrationsDirectory,\n 'migrationsDirectory',\n )\n const migrationJournal = join(\n resolveWithin(directory, migrationsDirectory, 'migrationsDirectory'),\n 'meta',\n '_journal.json',\n )\n const migrationMode = (await Bun.file(migrationJournal).exists())\n ? 'migrations'\n : 'push'\n const blueprint = blueprintFromManifest({\n manifest: {\n ...manifest,\n database: { ...manifest.database, migrationsDirectory },\n },\n generatorVersion: await packageVersion(),\n entry,\n migrationMode,\n framework,\n })\n const source = serializeBlueprint(blueprint)\n const existing = (await Bun.file(outputPath).exists())\n ? await Bun.file(outputPath).text()\n : undefined\n if (options.check) {\n if (existing !== source) throw new BlueprintCheckError()\n return { path: outputPath, blueprint, source, changed: false }\n }\n if (existing === source)\n return { path: outputPath, blueprint, source, changed: false }\n await mkdir(dirname(outputPath), { recursive: true })\n const temporary = `${outputPath}.${process.pid}.${Date.now()}.tmp`\n try {\n await writeFile(temporary, source, { mode: 0o600 })\n await rename(temporary, outputPath)\n } finally {\n await rm(temporary, { force: true })\n }\n return { path: outputPath, blueprint, source, changed: true }\n}\n"]}
|
package/dist/blueprint.d.ts
CHANGED
|
@@ -28,7 +28,7 @@ export type BunderstackBlueprint = {
|
|
|
28
28
|
};
|
|
29
29
|
bunderstack: {
|
|
30
30
|
entry: string;
|
|
31
|
-
manifestVersion:
|
|
31
|
+
manifestVersion: 4;
|
|
32
32
|
};
|
|
33
33
|
resources: {
|
|
34
34
|
database: BunderstackManifest['database'] & {
|
|
@@ -38,6 +38,7 @@ export type BunderstackBlueprint = {
|
|
|
38
38
|
realtime?: {
|
|
39
39
|
required: true;
|
|
40
40
|
};
|
|
41
|
+
messaging: BunderstackManifest['messaging'];
|
|
41
42
|
};
|
|
42
43
|
environment: BlueprintEnvVar[];
|
|
43
44
|
/** Application-declared procedures. Absent in blueprints written before 0.23.0. */
|
package/dist/blueprint.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"blueprint.d.ts","sourceRoot":"","sources":["../src/blueprint.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAKrD,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,MAAM,CAAA;AACjD,MAAM,MAAM,oBAAoB,GAC5B,gBAAgB,GAChB,OAAO,GACP,SAAS,GACT,QAAQ,CAAA;AAEZ;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,OAAO,CAAA;IACjB,KAAK,EAAE,QAAQ,GAAG,QAAQ,CAAA;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,OAAO,EAAE,CAAC,CAAA;IACV,SAAS,EAAE;QAAE,IAAI,EAAE,aAAa,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;IACnD,WAAW,EAAE;QACX,SAAS,EAAE,oBAAoB,CAAA;QAC/B,OAAO,EAAE;YAAE,KAAK,EAAE,OAAO,CAAC;YAAC,KAAK,EAAE,OAAO,CAAC;YAAC,MAAM,CAAC,EAAE,QAAQ,CAAA;SAAE,CAAA;KAC/D,CAAA;IACD,WAAW,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,CAAC,CAAA;KAAE,CAAA;IAClD,SAAS,EAAE;QACT,QAAQ,EAAE,mBAAmB,CAAC,UAAU,CAAC,GAAG;YAAE,aAAa,EAAE,aAAa,CAAA;SAAE,CAAA;QAC5E,OAAO,EAAE,mBAAmB,CAAC,SAAS,CAAC,CAAA;QACvC,QAAQ,CAAC,EAAE;YAAE,QAAQ,EAAE,IAAI,CAAA;SAAE,CAAA;
|
|
1
|
+
{"version":3,"file":"blueprint.d.ts","sourceRoot":"","sources":["../src/blueprint.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAKrD,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,MAAM,CAAA;AACjD,MAAM,MAAM,oBAAoB,GAC5B,gBAAgB,GAChB,OAAO,GACP,SAAS,GACT,QAAQ,CAAA;AAEZ;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,OAAO,CAAA;IACjB,KAAK,EAAE,QAAQ,GAAG,QAAQ,CAAA;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,OAAO,EAAE,CAAC,CAAA;IACV,SAAS,EAAE;QAAE,IAAI,EAAE,aAAa,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;IACnD,WAAW,EAAE;QACX,SAAS,EAAE,oBAAoB,CAAA;QAC/B,OAAO,EAAE;YAAE,KAAK,EAAE,OAAO,CAAC;YAAC,KAAK,EAAE,OAAO,CAAC;YAAC,MAAM,CAAC,EAAE,QAAQ,CAAA;SAAE,CAAA;KAC/D,CAAA;IACD,WAAW,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,CAAC,CAAA;KAAE,CAAA;IAClD,SAAS,EAAE;QACT,QAAQ,EAAE,mBAAmB,CAAC,UAAU,CAAC,GAAG;YAAE,aAAa,EAAE,aAAa,CAAA;SAAE,CAAA;QAC5E,OAAO,EAAE,mBAAmB,CAAC,SAAS,CAAC,CAAA;QACvC,QAAQ,CAAC,EAAE;YAAE,QAAQ,EAAE,IAAI,CAAA;SAAE,CAAA;QAC7B,SAAS,EAAE,mBAAmB,CAAC,WAAW,CAAC,CAAA;KAC5C,CAAA;IACD,WAAW,EAAE,eAAe,EAAE,CAAA;IAC9B,mFAAmF;IACnF,GAAG,CAAC,EAAE;QAAE,UAAU,EAAE,mBAAmB,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAA;KAAE,CAAA;IAC9D,UAAU,EAAE,mBAAmB,CAAC,YAAY,CAAC,GAAG;QAC9C,MAAM,EAAE;YAAE,QAAQ,EAAE,OAAO,CAAA;SAAE,CAAA;KAC9B,CAAA;CACF,CAAA;AAuID,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,oBAAoB,CAgEnE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAEjE;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE;IAC1C,QAAQ,EAAE,mBAAmB,CAAA;IAC7B,gBAAgB,EAAE,MAAM,CAAA;IACxB,KAAK,EAAE,MAAM,CAAA;IACb,aAAa,EAAE,aAAa,CAAA;IAC5B,SAAS,CAAC,EAAE,oBAAoB,CAAA;CACjC,GAAG,oBAAoB,CAiDvB;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,oBAAoB,CAEvE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,oBAAoB,GAAG,MAAM,CAmBtE"}
|
package/dist/blueprint.js
CHANGED
|
@@ -37,7 +37,7 @@ const blueprintSchema = open({
|
|
|
37
37
|
}),
|
|
38
38
|
bunderstack: open({
|
|
39
39
|
entry: relativePath,
|
|
40
|
-
manifestVersion: v.literal(
|
|
40
|
+
manifestVersion: v.literal(4),
|
|
41
41
|
}),
|
|
42
42
|
resources: open({
|
|
43
43
|
database: open({
|
|
@@ -58,6 +58,9 @@ const blueprintSchema = open({
|
|
|
58
58
|
})),
|
|
59
59
|
}),
|
|
60
60
|
realtime: v.optional(open({ required: v.literal(true) })),
|
|
61
|
+
messaging: open({
|
|
62
|
+
channels: v.array(open({ name: nonEmpty, kind: nonEmpty, provider: nonEmpty })),
|
|
63
|
+
}),
|
|
61
64
|
}),
|
|
62
65
|
environment: v.array(open({
|
|
63
66
|
key: nonEmpty,
|
|
@@ -108,6 +111,7 @@ export function parseBlueprint(value) {
|
|
|
108
111
|
rejectDuplicates('database export table', blueprint.resources.database.tables.map((entry) => entry.exportName));
|
|
109
112
|
rejectDuplicates('storage bucket', blueprint.resources.storage.buckets.map((entry) => entry.name));
|
|
110
113
|
rejectDuplicates('environment key', blueprint.environment.map((entry) => entry.key));
|
|
114
|
+
rejectDuplicates('messaging channel', blueprint.resources.messaging.channels.map((entry) => entry.name));
|
|
111
115
|
if (blueprint.api)
|
|
112
116
|
rejectDuplicates('api operation', blueprint.api.operations.map((entry) => entry.handle));
|
|
113
117
|
rejectDuplicates('background job', blueprint.background.jobs.map((entry) => entry.name));
|
|
@@ -145,7 +149,7 @@ export function blueprintFromManifest(args) {
|
|
|
145
149
|
...(workerRequired ? { worker: 'worker' } : {}),
|
|
146
150
|
},
|
|
147
151
|
},
|
|
148
|
-
bunderstack: { entry: args.entry, manifestVersion:
|
|
152
|
+
bunderstack: { entry: args.entry, manifestVersion: 4 },
|
|
149
153
|
resources: {
|
|
150
154
|
database: {
|
|
151
155
|
...args.manifest.database,
|
|
@@ -159,6 +163,9 @@ export function blueprintFromManifest(args) {
|
|
|
159
163
|
...(args.manifest.realtime.required
|
|
160
164
|
? { realtime: { required: true } }
|
|
161
165
|
: {}),
|
|
166
|
+
messaging: {
|
|
167
|
+
channels: sortBy(args.manifest.messaging.channels, (entry) => entry.name),
|
|
168
|
+
},
|
|
162
169
|
},
|
|
163
170
|
environment: sortBy(args.manifest.environment, (entry) => entry.key),
|
|
164
171
|
api: { operations: args.manifest.api.operations },
|
package/dist/blueprint.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"blueprint.js","sourceRoot":"","sources":["../src/blueprint.ts"],"names":[],"mappings":"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","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"]}
|
|
1
|
+
{"version":3,"file":"blueprint.js","sourceRoot":"","sources":["../src/blueprint.ts"],"names":[],"mappings":"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","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: 4 }\n resources: {\n database: BunderstackManifest['database'] & { migrationMode: MigrationMode }\n storage: BunderstackManifest['storage']\n realtime?: { required: true }\n messaging: BunderstackManifest['messaging']\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(4),\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 messaging: open({\n channels: v.array(\n open({ name: nonEmpty, kind: nonEmpty, provider: nonEmpty }),\n ),\n }),\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 rejectDuplicates(\n 'messaging channel',\n blueprint.resources.messaging.channels.map((entry) => entry.name),\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: 4 },\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 messaging: {\n channels: sortBy(\n args.manifest.messaging.channels,\n (entry) => entry.name,\n ),\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/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAEL,iBAAiB,EAElB,MAAM,uBAAuB,CAAA;AAG9B,MAAM,MAAM,KAAK,GAAG;IAClB,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B,CAAA;AAcD,wBAAsB,MAAM,CAC1B,IAAI,EAAE,MAAM,EAAE,EACd,EAAE,EAAE,KAAK,EACT,QAAQ,GAAE,OAAO,iBAAqC,GACrD,OAAO,CAAC,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAEL,iBAAiB,EAElB,MAAM,uBAAuB,CAAA;AAG9B,MAAM,MAAM,KAAK,GAAG;IAClB,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B,CAAA;AAcD,wBAAsB,MAAM,CAC1B,IAAI,EAAE,MAAM,EAAE,EACd,EAAE,EAAE,KAAK,EACT,QAAQ,GAAE,OAAO,iBAAqC,GACrD,OAAO,CAAC,MAAM,CAAC,CAmGjB"}
|
package/dist/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { BlueprintCheckError, generateBlueprint, } from './blueprint-generator.js';
|
|
3
3
|
import { installSkills } from './cli-skills.js';
|
|
4
4
|
const help = `Usage:
|
|
5
|
-
bunderstack blueprint [directory] [--entry <path>] [--output <path>] [--check]
|
|
5
|
+
bunderstack blueprint [directory] [--entry <path>] [--output <path>] [--check|--hosted-check]
|
|
6
6
|
bunderstack skills [--dir <path>] [--check]
|
|
7
7
|
|
|
8
8
|
blueprint Generate a committed deployment declaration for a TanStack Start
|
|
@@ -53,9 +53,21 @@ export async function runCli(args, io, generate = generateBlueprint) {
|
|
|
53
53
|
for (let index = 1; index < args.length; index++) {
|
|
54
54
|
const argument = args[index];
|
|
55
55
|
if (argument === '--check') {
|
|
56
|
+
if (options.hostedCheck) {
|
|
57
|
+
io.stderr('[bunderstack] --check and --hosted-check are mutually exclusive');
|
|
58
|
+
return 2;
|
|
59
|
+
}
|
|
56
60
|
options.check = true;
|
|
57
61
|
continue;
|
|
58
62
|
}
|
|
63
|
+
if (argument === '--hosted-check') {
|
|
64
|
+
if (options.check) {
|
|
65
|
+
io.stderr('[bunderstack] --check and --hosted-check are mutually exclusive');
|
|
66
|
+
return 2;
|
|
67
|
+
}
|
|
68
|
+
options.hostedCheck = true;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
59
71
|
if (argument === '--entry' || argument === '--output') {
|
|
60
72
|
const value = args[++index];
|
|
61
73
|
if (!value || value.startsWith('--')) {
|
|
@@ -80,7 +92,7 @@ export async function runCli(args, io, generate = generateBlueprint) {
|
|
|
80
92
|
}
|
|
81
93
|
try {
|
|
82
94
|
const result = await generate(options);
|
|
83
|
-
io.stdout(options.check || !result.changed
|
|
95
|
+
io.stdout(options.check || options.hostedCheck || !result.changed
|
|
84
96
|
? 'bunderstack.blueprint.yaml is current'
|
|
85
97
|
: 'Generated bunderstack.blueprint.yaml');
|
|
86
98
|
return 0;
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env bun\nimport {\n BlueprintCheckError,\n generateBlueprint,\n type GenerateBlueprintOptions,\n} from './blueprint-generator'\nimport { installSkills } from './cli-skills'\n\nexport type CliIo = {\n stdout(message: string): void\n stderr(message: string): void\n}\n\nconst help = `Usage:\n bunderstack blueprint [directory] [--entry <path>] [--output <path>] [--check|--hosted-check]\n bunderstack skills [--dir <path>] [--check]\n\nblueprint Generate a committed deployment declaration for a TanStack Start\n application. Entry precedence: --entry,\n package.json#bunderstack.entry, src/bunderstack.ts.\n\nskills Install the Bunderstack agent skills that match this version into\n .agents/skills, and point AGENTS.md at them so an agent loads them\n before touching the API. --check reports drift without writing.`\n\nexport async function runCli(\n args: string[],\n io: CliIo,\n generate: typeof generateBlueprint = generateBlueprint,\n): Promise<number> {\n if (args[0] === '--help' || args[0] === '-h') {\n io.stdout(help)\n return 0\n }\n if (args[0] === '--version') {\n io.stdout(\n (\n (await Bun.file(\n new URL('../package.json', import.meta.url),\n ).json()) as { version: string }\n ).version,\n )\n return 0\n }\n if (args[0] === 'skills') {\n const options: { cwd: string; directory?: string; check?: boolean } = {\n cwd: process.cwd(),\n }\n for (let index = 1; index < args.length; index++) {\n const argument = args[index]!\n if (argument === '--check') {\n options.check = true\n continue\n }\n if (argument === '--dir') {\n const value = args[++index]\n if (!value || value.startsWith('--')) {\n io.stderr('[bunderstack] missing value for --dir')\n return 2\n }\n options.directory = value\n continue\n }\n io.stderr(`[bunderstack] unknown option: ${argument}`)\n return 2\n }\n return installSkills(options, io)\n }\n\n if (args[0] !== 'blueprint') {\n io.stderr(help)\n return 2\n }\n const options: GenerateBlueprintOptions = { directory: process.cwd() }\n for (let index = 1; index < args.length; index++) {\n const argument = args[index]!\n if (argument === '--check') {\n if (options.hostedCheck) {\n io.stderr(\n '[bunderstack] --check and --hosted-check are mutually exclusive',\n )\n return 2\n }\n options.check = true\n continue\n }\n if (argument === '--hosted-check') {\n if (options.check) {\n io.stderr(\n '[bunderstack] --check and --hosted-check are mutually exclusive',\n )\n return 2\n }\n options.hostedCheck = true\n continue\n }\n if (argument === '--entry' || argument === '--output') {\n const value = args[++index]\n if (!value || value.startsWith('--')) {\n io.stderr(`[bunderstack] missing value for ${argument}`)\n return 2\n }\n if (argument === '--entry') options.entry = value\n else options.output = value\n continue\n }\n if (argument.startsWith('-')) {\n io.stderr(`[bunderstack] unknown option: ${argument}`)\n return 2\n }\n if (options.directory !== process.cwd()) {\n io.stderr('[bunderstack] only one application directory is allowed')\n return 2\n }\n options.directory = argument\n }\n try {\n const result = await generate(options)\n io.stdout(\n options.check || options.hostedCheck || !result.changed\n ? 'bunderstack.blueprint.yaml is current'\n : 'Generated bunderstack.blueprint.yaml',\n )\n return 0\n } catch (error) {\n io.stderr(error instanceof Error ? error.message : String(error))\n return error instanceof BlueprintCheckError ? 1 : 1\n }\n}\n\nif (import.meta.main) {\n const exitCode = await runCli(process.argv.slice(2), {\n stdout: (message) => console.log(message),\n stderr: (message) => console.error(message),\n })\n process.exit(exitCode)\n}\n"]}
|
package/dist/config.d.ts
CHANGED
|
@@ -5,8 +5,8 @@ import type { BunderstackApiBuilder } from './api/builder.js';
|
|
|
5
5
|
import type { DatabaseAdapter } from './database/adapter.js';
|
|
6
6
|
import type { DbFor } from './db.js';
|
|
7
7
|
import type { AnyDb } from './dialect.js';
|
|
8
|
-
import type { EmailConfigInput } from './email.js';
|
|
9
8
|
import type { IdempotencyConfig } from './idempotency.js';
|
|
9
|
+
import type { MessagingConfig } from './messaging/index.js';
|
|
10
10
|
import type { RateLimitConfig } from './rate-limit.js';
|
|
11
11
|
import { type BaseEnv, type EnvConfigInput, type ValidatedEnv } from './env.js';
|
|
12
12
|
import { type ResolvedStorageBuckets, type StorageConfigInput } from './storage/buckets.js';
|
|
@@ -53,6 +53,15 @@ export type AuthConfigFactory = (ctx: {
|
|
|
53
53
|
db: AnyDb;
|
|
54
54
|
env: BaseEnv;
|
|
55
55
|
}) => BetterAuthConfig;
|
|
56
|
+
/** Realtime declaration. Independent of schema, storage, and env. */
|
|
57
|
+
export type RealtimeConfigInput = boolean | {
|
|
58
|
+
bufferSize?: number;
|
|
59
|
+
resumeSeconds?: number;
|
|
60
|
+
redis?: string | {
|
|
61
|
+
url: string;
|
|
62
|
+
token?: string;
|
|
63
|
+
};
|
|
64
|
+
};
|
|
56
65
|
export type BunderstackConfig<TSchema extends Record<string, unknown>, TAccess extends Record<string, TableAccessInput> | undefined = Record<string, TableAccessInput> | undefined, TStorage extends StorageConfigInput | undefined = StorageConfigInput | undefined, TEnv extends EnvConfigInput | undefined = EnvConfigInput | undefined, TCustomApiRouter extends AnyRouter | undefined = AnyRouter | undefined> = {
|
|
57
66
|
schema: TSchema;
|
|
58
67
|
access?: TAccess;
|
|
@@ -75,11 +84,10 @@ export type BunderstackConfig<TSchema extends Record<string, unknown>, TAccess e
|
|
|
75
84
|
*/
|
|
76
85
|
authResolver?: AuthSessionResolver;
|
|
77
86
|
storage?: TStorage;
|
|
78
|
-
env?: TEnv;
|
|
79
87
|
background?: {
|
|
80
88
|
autoStart?: boolean;
|
|
81
89
|
};
|
|
82
|
-
|
|
90
|
+
messaging?: MessagingConfig;
|
|
83
91
|
/**
|
|
84
92
|
* The application's oRPC router. Pass the finished router object, or a
|
|
85
93
|
* callback when the router needs the framework builder at configuration
|
|
@@ -100,14 +108,7 @@ export type BunderstackConfig<TSchema extends Record<string, unknown>, TAccess e
|
|
|
100
108
|
idempotency?: boolean | IdempotencyConfig;
|
|
101
109
|
/** Generate and serve `/api/openapi.json`. Disabled by default. */
|
|
102
110
|
openapi?: boolean;
|
|
103
|
-
realtime?:
|
|
104
|
-
bufferSize?: number;
|
|
105
|
-
resumeSeconds?: number;
|
|
106
|
-
redis?: string | {
|
|
107
|
-
url: string;
|
|
108
|
-
token?: string;
|
|
109
|
-
};
|
|
110
|
-
};
|
|
111
|
+
realtime?: RealtimeConfigInput;
|
|
111
112
|
};
|
|
112
113
|
export type ResolvedConfig = {
|
|
113
114
|
database: {
|
|
@@ -118,14 +119,7 @@ export type ResolvedConfig = {
|
|
|
118
119
|
};
|
|
119
120
|
auth: BetterAuthConfig | AuthConfigFactory;
|
|
120
121
|
storage: ResolvedStorageBuckets;
|
|
121
|
-
realtime?:
|
|
122
|
-
bufferSize?: number;
|
|
123
|
-
resumeSeconds?: number;
|
|
124
|
-
redis?: string | {
|
|
125
|
-
url: string;
|
|
126
|
-
token?: string;
|
|
127
|
-
};
|
|
128
|
-
};
|
|
122
|
+
realtime?: RealtimeConfigInput;
|
|
129
123
|
};
|
|
130
124
|
export declare function resolveConfig<TSchema extends Record<string, unknown>, TAccess extends Record<string, TableAccessInput> | undefined = undefined, TStorage extends StorageConfigInput | undefined = undefined, TEnv extends EnvConfigInput | undefined = undefined, TCustomApiRouter extends AnyRouter | undefined = undefined>(options: BunderstackConfig<TSchema, TAccess, TStorage, TEnv, TCustomApiRouter>, env?: BaseEnv, platformSource?: Record<string, string | undefined>): ResolvedConfig;
|
|
131
125
|
/** Collapse the object/builder union once the db is up. */
|