bunderstack 0.23.4 → 0.24.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +67 -0
- package/README.md +7 -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/auth.d.ts +20 -9
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +3 -8
- package/dist/auth.js.map +1 -1
- 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 +23 -25
- 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 +14 -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 +33 -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/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/provision-internals.d.ts +1 -1
- package/dist/provision-internals.js +1 -1
- package/dist/provision-internals.js.map +1 -1
- package/dist/provision-runtime.d.ts +7 -0
- package/dist/provision-runtime.d.ts.map +1 -0
- package/dist/provision-runtime.js +50 -0
- package/dist/provision-runtime.js.map +1 -0
- package/dist/provision-schema.d.ts +16 -0
- package/dist/provision-schema.d.ts.map +1 -0
- package/dist/provision-schema.js +63 -0
- package/dist/provision-schema.js.map +1 -0
- package/dist/provision.d.ts +4 -21
- package/dist/provision.d.ts.map +1 -1
- package/dist/provision.js +10 -111
- package/dist/provision.js.map +1 -1
- package/dist/runtime.d.ts +17 -14
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +20 -33
- 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 +3 -3
- package/dist/testing/fixture.d.ts.map +1 -1
- package/dist/testing/fixture.js +12 -10
- 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/llms-full.txt +416 -226
- package/llms.txt +4 -3
- package/package.json +10 -2
- package/skills/creating-bunderstack-apps/references/application-structure.md +16 -7
- package/skills/creating-bunderstack-apps/references/verification.md +7 -6
- package/skills/migrating-to-bunderstack/SKILL.md +75 -51
- package/skills/migrating-to-bunderstack/references/audit-checklist.md +2 -2
- package/skills/migrating-to-bunderstack/references/runtime-replacements.md +20 -15
package/dist/manifest.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { getTableName, isTable } from 'drizzle-orm';
|
|
2
2
|
import * as v from 'valibot';
|
|
3
|
-
import {
|
|
3
|
+
import { bunderstackMessageEvents, bunderstackMessages, bunderstackFiles, bunderstackIdempotency, bunderstackJobs, } from './internal-tables.js';
|
|
4
4
|
import { parseCron } from './jobs/cron.js';
|
|
5
|
+
import { isMessagingDescriptor } from './messaging/types.js';
|
|
5
6
|
import { StandardSchemaValidationError, validateStandardSchema, } from './standard-schema.js';
|
|
6
7
|
const MAX_ENV_DESCRIPTION = 200;
|
|
7
8
|
const nonEmpty = v.pipe(v.string(), v.minLength(1));
|
|
@@ -18,7 +19,7 @@ const cronSchedule = v.pipe(nonEmpty, v.check((value) => {
|
|
|
18
19
|
}
|
|
19
20
|
}, 'invalid cron schedule'));
|
|
20
21
|
const manifestSchema = v.strictObject({
|
|
21
|
-
version: v.literal(
|
|
22
|
+
version: v.literal(4),
|
|
22
23
|
database: v.strictObject({
|
|
23
24
|
dialect: v.picklist(['sqlite', 'pg']),
|
|
24
25
|
migrationsDirectory: migrationDirectory,
|
|
@@ -36,6 +37,9 @@ const manifestSchema = v.strictObject({
|
|
|
36
37
|
})),
|
|
37
38
|
}),
|
|
38
39
|
realtime: v.strictObject({ required: v.boolean() }),
|
|
40
|
+
messaging: v.strictObject({
|
|
41
|
+
channels: v.array(v.strictObject({ name: nonEmpty, kind: nonEmpty, provider: nonEmpty })),
|
|
42
|
+
}),
|
|
39
43
|
environment: v.array(v.strictObject({
|
|
40
44
|
key: nonEmpty,
|
|
41
45
|
required: v.boolean(),
|
|
@@ -122,13 +126,13 @@ function describeSection(section, scope, meta) {
|
|
|
122
126
|
function systemTables() {
|
|
123
127
|
return [
|
|
124
128
|
{
|
|
125
|
-
exportName: '_system.
|
|
126
|
-
physicalName: getTableName(
|
|
129
|
+
exportName: '_system.messageEvents',
|
|
130
|
+
physicalName: getTableName(bunderstackMessageEvents),
|
|
127
131
|
system: true,
|
|
128
132
|
},
|
|
129
133
|
{
|
|
130
|
-
exportName: '_system.
|
|
131
|
-
physicalName: getTableName(
|
|
134
|
+
exportName: '_system.messages',
|
|
135
|
+
physicalName: getTableName(bunderstackMessages),
|
|
132
136
|
system: true,
|
|
133
137
|
},
|
|
134
138
|
{
|
|
@@ -154,6 +158,7 @@ export function parseManifest(value) {
|
|
|
154
158
|
rejectDuplicates('database export table', manifest.database.tables.map((entry) => entry.exportName));
|
|
155
159
|
rejectDuplicates('storage bucket', manifest.storage.buckets.map((entry) => entry.name));
|
|
156
160
|
rejectDuplicates('environment key', manifest.environment.map((entry) => entry.key));
|
|
161
|
+
rejectDuplicates('messaging channel', manifest.messaging.channels.map((entry) => entry.name));
|
|
157
162
|
rejectDuplicates('api operation', manifest.api.operations.map((entry) => entry.handle));
|
|
158
163
|
rejectDuplicates('background job', manifest.background.jobs.map((entry) => entry.name));
|
|
159
164
|
rejectDuplicates('background cron', manifest.background.cron.map((entry) => entry.name));
|
|
@@ -161,6 +166,14 @@ export function parseManifest(value) {
|
|
|
161
166
|
return manifest;
|
|
162
167
|
}
|
|
163
168
|
export function buildManifest(args) {
|
|
169
|
+
for (const [name, descriptor] of Object.entries(args.messaging ?? {})) {
|
|
170
|
+
if (!name.trim()) {
|
|
171
|
+
throw new Error('[bunderstack] messaging channel names cannot be empty');
|
|
172
|
+
}
|
|
173
|
+
if (!isMessagingDescriptor(descriptor)) {
|
|
174
|
+
throw new Error(`[bunderstack] messaging.${name} is not a provider descriptor`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
164
177
|
const declaredKeys = new Set([
|
|
165
178
|
...Object.keys(args.envConfig?.server ?? {}),
|
|
166
179
|
...Object.keys(args.envConfig?.client ?? {}),
|
|
@@ -172,32 +185,10 @@ export function buildManifest(args) {
|
|
|
172
185
|
const environment = [
|
|
173
186
|
...describeSection(args.envConfig?.server, 'server', args.envConfig?.meta),
|
|
174
187
|
...describeSection(args.envConfig?.client, 'client', args.envConfig?.meta),
|
|
175
|
-
...(args.emailProvider === 'resend'
|
|
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
|
-
]
|
|
185
|
-
: []),
|
|
186
|
-
...(args.emailProvider === 'smtp'
|
|
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
|
-
]
|
|
196
|
-
: []),
|
|
197
188
|
];
|
|
198
189
|
rejectDuplicates('environment key', environment.map((entry) => entry.key));
|
|
199
190
|
return parseManifest({
|
|
200
|
-
version:
|
|
191
|
+
version: 4,
|
|
201
192
|
database: {
|
|
202
193
|
dialect: args.dialect,
|
|
203
194
|
migrationsDirectory: args.migrationsDirectory,
|
|
@@ -211,6 +202,13 @@ export function buildManifest(args) {
|
|
|
211
202
|
})), (bucket) => bucket.name),
|
|
212
203
|
},
|
|
213
204
|
realtime: { required: args.realtime },
|
|
205
|
+
messaging: {
|
|
206
|
+
channels: sortBy(Object.entries(args.messaging ?? {}).map(([name, descriptor]) => ({
|
|
207
|
+
name,
|
|
208
|
+
kind: descriptor.kind,
|
|
209
|
+
provider: descriptor.provider,
|
|
210
|
+
})), (entry) => entry.name),
|
|
211
|
+
},
|
|
214
212
|
environment: sortBy(environment, (entry) => entry.key),
|
|
215
213
|
api: {
|
|
216
214
|
operations: [...args.api].sort((left, right) => left.handle.localeCompare(right.handle)),
|
package/dist/manifest.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AACnD,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAO5B,OAAO,EACL,sBAAsB,EACtB,iBAAiB,EACjB,gBAAgB,EAChB,sBAAsB,EACtB,eAAe,GAChB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACvC,OAAO,EACL,6BAA6B,EAC7B,sBAAsB,GACvB,MAAM,mBAAmB,CAAA;AAmD1B,MAAM,mBAAmB,GAAG,GAAG,CAAA;AAE/B,MAAM,QAAQ,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAA;AACnD,MAAM,kBAAkB,GAAG,CAAC,CAAC,IAAI,CAC/B,QAAQ,EACR,CAAC,CAAC,KAAK,CACL,CAAC,KAAK,EAAE,EAAE,CACR,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC;IACrB,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;QACpB,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,EAAE,CAAC,CAAC,EACnE,mFAAmF,CACpF,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,cAAc,GAAG,CAAC,CAAC,YAAY,CAAC;IACpC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACrB,QAAQ,EAAE,CAAC,CAAC,YAAY,CAAC;QACvB,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QACrC,mBAAmB,EAAE,kBAAkB;QACvC,MAAM,EAAE,CAAC,CAAC,KAAK,CACb,CAAC,CAAC,YAAY,CAAC;YACb,UAAU,EAAE,QAAQ;YACpB,YAAY,EAAE,QAAQ;YACtB,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE;SACpB,CAAC,CACH;KACF,CAAC;IACF,OAAO,EAAE,CAAC,CAAC,YAAY,CAAC;QACtB,aAAa,EAAE,QAAQ;QACvB,OAAO,EAAE,CAAC,CAAC,KAAK,CACd,CAAC,CAAC,YAAY,CAAC;YACb,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;SAC9C,CAAC,CACH;KACF,CAAC;IACF,QAAQ,EAAE,CAAC,CAAC,YAAY,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;IACnD,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;QACvC,SAAS,EAAE,CAAC,CAAC,OAAO,EAAE;QACtB,WAAW,EAAE,CAAC,CAAC,QAAQ,CACrB,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC,CACnD;KACF,CAAC,CACH;IACD,GAAG,EAAE,CAAC,CAAC,YAAY,CAAC;QAClB,UAAU,EAAE,CAAC,CAAC,KAAK,CACjB,CAAC,CAAC,YAAY,CAAC;YACb,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;IACF,UAAU,EAAE,CAAC,CAAC,YAAY,CAAC;QACzB,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,SAAS,cAAc,CAAC,MAA+B;IACrD,MAAM,WAAW,GAAG,IAAI,GAAG,CACzB,YAAY,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CAClD,CAAA;IACD,OAAO,MAAM,CACX,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,EAAE,KAAK,CAAC,EAAE,EAAE,CACrD,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;QACrD,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;QACpE,CAAC,CAAC,EAAE,CACP,EACD,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,YAAY,CAC9B,CAAA;AACH,CAAC;AAED,SAAS,cAAc,CAAC,GAAW,EAAE,KAAa;IAChD,MAAM,WAAW,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACrD,IAAI,CAAC,WAAW;QACd,MAAM,IAAI,KAAK,CACb,0BAA0B,GAAG,gCAAgC,CAC9D,CAAA;IACH,IAAI,WAAW,CAAC,MAAM,GAAG,mBAAmB;QAC1C,MAAM,IAAI,KAAK,CACb,0BAA0B,GAAG,gCAAgC,mBAAmB,aAAa,CAC9F,CAAA;IACH,OAAO,WAAW,CAAA;AACpB,CAAC;AAED,SAAS,eAAe,CACtB,OAAqD,EACrD,KAA8B,EAC9B,IAA4C;IAE5C,OAAO,MAAM,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,EAAE;QACzD,IAAI,QAAQ,GAAG,KAAK,CAAA;QACpB,IAAI,CAAC;YACH,sBAAsB,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,CAAC,CAAA;QAClD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,CAAC,KAAK,YAAY,6BAA6B,CAAC;gBAAE,MAAM,KAAK,CAAA;YAClE,QAAQ,GAAG,IAAI,CAAA;QACjB,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,EAAE,CAAC,GAAG,CAAC,CAAA;QACzB,MAAM,SAAS,GAAG,KAAK,EAAE,SAAS,IAAI,KAAK,KAAK,QAAQ,CAAA;QACxD,IAAI,KAAK,KAAK,QAAQ,IAAI,SAAS,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CACb,0BAA0B,GAAG,6DAA6D,CAC3F,CAAA;QACH,CAAC;QACD,OAAO;YACL,GAAG;YACH,QAAQ;YACR,KAAK;YACL,SAAS;YACT,GAAG,CAAC,KAAK,EAAE,WAAW,KAAK,SAAS;gBAClC,CAAC,CAAC,EAAE;gBACJ,CAAC,CAAC,EAAE,WAAW,EAAE,cAAc,CAAC,GAAG,EAAE,KAAK,CAAC,WAAW,CAAC,EAAE,CAAC;SAC7D,CAAA;IACH,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,SAAS,YAAY;IACnB,OAAO;QACL;YACE,UAAU,EAAE,qBAAqB;YACjC,YAAY,EAAE,YAAY,CAAC,sBAAsB,CAAC;YAClD,MAAM,EAAE,IAAI;SACb;QACD;YACE,UAAU,EAAE,gBAAgB;YAC5B,YAAY,EAAE,YAAY,CAAC,iBAAiB,CAAC;YAC7C,MAAM,EAAE,IAAI;SACb;QACD;YACE,UAAU,EAAE,eAAe;YAC3B,YAAY,EAAE,YAAY,CAAC,gBAAgB,CAAC;YAC5C,MAAM,EAAE,IAAI;SACb;QACD;YACE,UAAU,EAAE,qBAAqB;YACjC,YAAY,EAAE,YAAY,CAAC,sBAAsB,CAAC;YAClD,MAAM,EAAE,IAAI;SACb;QACD;YACE,UAAU,EAAE,cAAc;YAC1B,YAAY,EAAE,YAAY,CAAC,eAAe,CAAC;YAC3C,MAAM,EAAE,IAAI;SACb;KACF,CAAA;AACH,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,MAAM,QAAQ,GAAG,sBAAsB,CACrC,cAAc,EACd,KAAK,EACL,UAAU,CACY,CAAA;IACxB,gBAAgB,CACd,yBAAyB,EACzB,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CAC5D,CAAA;IACD,gBAAgB,CACd,uBAAuB,EACvB,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,CAC1D,CAAA;IACD,gBAAgB,CACd,gBAAgB,EAChB,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CACpD,CAAA;IACD,gBAAgB,CACd,iBAAiB,EACjB,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAC/C,CAAA;IACD,gBAAgB,CACd,eAAe,EACf,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CACrD,CAAA;IACD,gBAAgB,CACd,gBAAgB,EAChB,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CACpD,CAAA;IACD,gBAAgB,CACd,iBAAiB,EACjB,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CACpD,CAAA;IACD,gBAAgB,CACd,wBAAwB,EACxB,QAAQ,CAAC,UAAU,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAC3D,CAAA;IACD,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAU7B;IACC,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC;QAC3B,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,IAAI,EAAE,CAAC;QAC5C,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,IAAI,EAAE,CAAC;KAC7C,CAAC,CAAA;IACF,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,EAAE,CAAC;QAC1D,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC;YACxB,MAAM,IAAI,KAAK,CACb,qDAAqD,GAAG,GAAG,CAC5D,CAAA;IACL,CAAC;IACD,MAAM,WAAW,GAAG;QAClB,GAAG,eAAe,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC;QAC1E,GAAG,eAAe,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC;QAC1E,GAAG,CAAC,IAAI,CAAC,aAAa,KAAK,QAAQ;YACjC,CAAC,CAAC;gBACE;oBACE,GAAG,EAAE,gBAAgB;oBACrB,QAAQ,EAAE,IAAI;oBACd,KAAK,EAAE,QAAiB;oBACxB,SAAS,EAAE,IAAI;oBACf,WAAW,EAAE,iDAAiD;iBAC/D;aACF;YACH,CAAC,CAAC,EAAE,CAAC;QACP,GAAG,CAAC,IAAI,CAAC,aAAa,KAAK,MAAM;YAC/B,CAAC,CAAC;gBACE;oBACE,GAAG,EAAE,UAAU;oBACf,QAAQ,EAAE,IAAI;oBACd,KAAK,EAAE,QAAiB;oBACxB,SAAS,EAAE,IAAI;oBACf,WAAW,EAAE,sDAAsD;iBACpE;aACF;YACH,CAAC,CAAC,EAAE,CAAC;KACR,CAAA;IACD,gBAAgB,CACd,iBAAiB,EACjB,WAAW,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CACtC,CAAA;IAED,OAAO,aAAa,CAAC;QACnB,OAAO,EAAE,CAAC;QACV,QAAQ,EAAE;YACR,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,mBAAmB,EAAE,IAAI,CAAC,mBAAmB;YAC7C,MAAM,EAAE,MAAM,CACZ,CAAC,GAAG,YAAY,EAAE,EAAE,GAAG,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,EACnD,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,YAAY,CAC9B;SACF;QACD,OAAO,EAAE;YACP,aAAa,EAAE,IAAI,CAAC,OAAO,CAAC,aAAa;YACzC,OAAO,EAAE,MAAM,CACb,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;gBAClD,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,UAAU,EAAE,MAAM,CAAC,UAAU;aAC9B,CAAC,CAAC,EACH,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CACxB;SACF;QACD,QAAQ,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE;QACrC,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC;QACtD,GAAG,EAAE;YACH,UAAU,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAC7C,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC,CACxC;SACF;QACD,UAAU,EAAE;YACV,IAAI,EAAE,MAAM,CACV,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC;iBAC5B,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,KAAK,CAAC;iBACvC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,EAC9B,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CACtB;YACD,IAAI,EAAE,MAAM,CACV,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC;iBAC5B,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,MAAM,CAAC;iBACxC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;gBACrB,IAAI;gBACJ,QAAQ,EAAE,GAAG,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE;gBACjD,QAAQ,EAAE,KAAc;aACzB,CAAC,CAAC,EACL,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CACtB;YACD,WAAW,EAAE;gBACX;oBACE,IAAI,EAAE,eAAe;oBACrB,QAAQ,EAAE,WAAW;oBACrB,QAAQ,EAAE,KAAc;iBACzB;aACF;SACF;KACF,CAAC,CAAA;AACJ,CAAC","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"]}
|
|
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 { MessagingConfig } from './messaging'\nimport type { ResolvedBucket, ResolvedStorageBuckets } from './storage/buckets'\n\nimport {\n bunderstackMessageEvents,\n bunderstackMessages,\n bunderstackFiles,\n bunderstackIdempotency,\n bunderstackJobs,\n} from './internal-tables'\nimport { parseCron } from './jobs/cron'\nimport { isMessagingDescriptor } from './messaging/types'\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: 4\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 messaging: {\n channels: { name: string; kind: string; provider: string }[]\n }\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(4),\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 messaging: v.strictObject({\n channels: v.array(\n v.strictObject({ name: nonEmpty, kind: nonEmpty, provider: nonEmpty }),\n ),\n }),\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.messageEvents',\n physicalName: getTableName(bunderstackMessageEvents),\n system: true,\n },\n {\n exportName: '_system.messages',\n physicalName: getTableName(bunderstackMessages),\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 'messaging channel',\n manifest.messaging.channels.map((entry) => entry.name),\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 messaging: MessagingConfig | undefined\n realtime: boolean\n jobs: JobsDefs | undefined\n api: ApiOperation[]\n}): BunderstackManifest {\n for (const [name, descriptor] of Object.entries(args.messaging ?? {})) {\n if (!name.trim()) {\n throw new Error('[bunderstack] messaging channel names cannot be empty')\n }\n if (!isMessagingDescriptor(descriptor)) {\n throw new Error(\n `[bunderstack] messaging.${name} is not a provider descriptor`,\n )\n }\n }\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 ]\n rejectDuplicates(\n 'environment key',\n environment.map((entry) => entry.key),\n )\n\n return parseManifest({\n version: 4,\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 messaging: {\n channels: sortBy(\n Object.entries(args.messaging ?? {}).map(([name, descriptor]) => ({\n name,\n kind: descriptor.kind,\n provider: descriptor.provider,\n })),\n (entry) => entry.name,\n ),\n },\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,16 @@
|
|
|
1
|
+
import type { EmailAdapter, EmailMessage, SentEmail } from '../email.js';
|
|
2
|
+
import { type MessagingDescriptor } from './types.js';
|
|
3
|
+
export type ResendConfig = {
|
|
4
|
+
apiKey?: string;
|
|
5
|
+
from?: string;
|
|
6
|
+
};
|
|
7
|
+
export type ResendDescriptor = MessagingDescriptor<'email', 'resend', EmailMessage, SentEmail, ResendConfig>;
|
|
8
|
+
export declare function resend(config?: ResendConfig): ResendDescriptor;
|
|
9
|
+
export type CustomEmailConfig = {
|
|
10
|
+
adapter?: EmailAdapter | EmailAdapter['send'];
|
|
11
|
+
from?: string;
|
|
12
|
+
provider?: string;
|
|
13
|
+
};
|
|
14
|
+
export type CustomEmailDescriptor = MessagingDescriptor<'email', 'custom', EmailMessage, SentEmail, CustomEmailConfig>;
|
|
15
|
+
export declare function customEmail(config?: CustomEmailConfig): CustomEmailDescriptor;
|
|
16
|
+
//# sourceMappingURL=email.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../../src/messaging/email.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAErE,OAAO,EAAc,KAAK,mBAAmB,EAAE,MAAM,SAAS,CAAA;AAE9D,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG,mBAAmB,CAChD,OAAO,EACP,QAAQ,EACR,YAAY,EACZ,SAAS,EACT,YAAY,CACb,CAAA;AAED,wBAAgB,MAAM,CAAC,MAAM,GAAE,YAAiB,GAAG,gBAAgB,CAElE;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,CAAC,EAAE,YAAY,GAAG,YAAY,CAAC,MAAM,CAAC,CAAA;IAC7C,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG,mBAAmB,CACrD,OAAO,EACP,QAAQ,EACR,YAAY,EACZ,SAAS,EACT,iBAAiB,CAClB,CAAA;AAED,wBAAgB,WAAW,CACzB,MAAM,GAAE,iBAAsB,GAC7B,qBAAqB,CAEvB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email.js","sourceRoot":"","sources":["../../src/messaging/email.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAA4B,MAAM,SAAS,CAAA;AAe9D,MAAM,UAAU,MAAM,CAAC,SAAuB,EAAE;IAC9C,OAAO,UAAU,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAA;AAC9C,CAAC;AAgBD,MAAM,UAAU,WAAW,CACzB,SAA4B,EAAE;IAE9B,OAAO,UAAU,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAA;AAC9C,CAAC","sourcesContent":["import type { EmailAdapter, EmailMessage, SentEmail } from '../email'\n\nimport { descriptor, type MessagingDescriptor } from './types'\n\nexport type ResendConfig = {\n apiKey?: string\n from?: string\n}\n\nexport type ResendDescriptor = MessagingDescriptor<\n 'email',\n 'resend',\n EmailMessage,\n SentEmail,\n ResendConfig\n>\n\nexport function resend(config: ResendConfig = {}): ResendDescriptor {\n return descriptor('email', 'resend', config)\n}\n\nexport type CustomEmailConfig = {\n adapter?: EmailAdapter | EmailAdapter['send']\n from?: string\n provider?: string\n}\n\nexport type CustomEmailDescriptor = MessagingDescriptor<\n 'email',\n 'custom',\n EmailMessage,\n SentEmail,\n CustomEmailConfig\n>\n\nexport function customEmail(\n config: CustomEmailConfig = {},\n): CustomEmailDescriptor {\n return descriptor('email', 'custom', config)\n}\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { resend, customEmail } from './email.js';
|
|
2
|
+
export { createMessaging } from './standalone.js';
|
|
3
|
+
export type { MessagingContext } from './standalone.js';
|
|
4
|
+
export type { CustomEmailConfig, CustomEmailDescriptor, ResendConfig, ResendDescriptor, } from './email.js';
|
|
5
|
+
export { telegram } from './telegram.js';
|
|
6
|
+
export type { SentTelegramMessage, TelegramConfig, TelegramDescriptor, TelegramMessage, } from './telegram.js';
|
|
7
|
+
export type { AnyMessagingDescriptor, MessagingConfig, MessagingDescriptor, MessagingFacade, MessagingFacades, MessagingFacadesFor, } from './types.js';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/messaging/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAC9C,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AACpD,YAAY,EACV,iBAAiB,EACjB,qBAAqB,EACrB,YAAY,EACZ,gBAAgB,GACjB,MAAM,SAAS,CAAA;AAChB,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AACrC,YAAY,EACV,mBAAmB,EACnB,cAAc,EACd,kBAAkB,EAClB,eAAe,GAChB,MAAM,YAAY,CAAA;AACnB,YAAY,EACV,sBAAsB,EACtB,eAAe,EACf,mBAAmB,EACnB,eAAe,EACf,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,SAAS,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/messaging/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAQ9C,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA","sourcesContent":["export { resend, customEmail } from './email'\nexport { createMessaging } from './standalone'\nexport type { MessagingContext } from './standalone'\nexport type {\n CustomEmailConfig,\n CustomEmailDescriptor,\n ResendConfig,\n ResendDescriptor,\n} from './email'\nexport { telegram } from './telegram'\nexport type {\n SentTelegramMessage,\n TelegramConfig,\n TelegramDescriptor,\n TelegramMessage,\n} from './telegram'\nexport type {\n AnyMessagingDescriptor,\n MessagingConfig,\n MessagingDescriptor,\n MessagingFacade,\n MessagingFacades,\n MessagingFacadesFor,\n} from './types'\n"]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { AnyDb } from '../dialect.js';
|
|
2
|
+
export declare function insertMessage(db: AnyDb | undefined, value: Record<string, unknown>): Promise<void>;
|
|
3
|
+
export declare function updateMessage(db: AnyDb | undefined, id: string, value: Record<string, unknown>, expectedStatus?: string): Promise<void>;
|
|
4
|
+
//# sourceMappingURL=journal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"journal.d.ts","sourceRoot":"","sources":["../../src/messaging/journal.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAIvC,wBAAsB,aAAa,CACjC,EAAE,EAAE,KAAK,GAAG,SAAS,EACrB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,iBAI/B;AAED,wBAAsB,aAAa,CACjC,EAAE,EAAE,KAAK,GAAG,SAAS,EACrB,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,cAAc,CAAC,EAAE,MAAM,iBAYxB"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { and, eq } from 'drizzle-orm';
|
|
2
|
+
import { messagesTableFor } from '../internal-tables.js';
|
|
3
|
+
export async function insertMessage(db, value) {
|
|
4
|
+
if (!db)
|
|
5
|
+
return;
|
|
6
|
+
await db.insert(messagesTableFor(db)).values(value);
|
|
7
|
+
}
|
|
8
|
+
export async function updateMessage(db, id, value, expectedStatus) {
|
|
9
|
+
if (!db)
|
|
10
|
+
return;
|
|
11
|
+
const table = messagesTableFor(db);
|
|
12
|
+
await db
|
|
13
|
+
.update(table)
|
|
14
|
+
.set(value)
|
|
15
|
+
.where(expectedStatus
|
|
16
|
+
? and(eq(table.id, id), eq(table.status, expectedStatus))
|
|
17
|
+
: eq(table.id, id));
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=journal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"journal.js","sourceRoot":"","sources":["../../src/messaging/journal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,aAAa,CAAA;AAIrC,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAErD,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,EAAqB,EACrB,KAA8B;IAE9B,IAAI,CAAC,EAAE;QAAE,OAAM;IACf,MAAM,EAAE,CAAC,MAAM,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AACrD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,EAAqB,EACrB,EAAU,EACV,KAA8B,EAC9B,cAAuB;IAEvB,IAAI,CAAC,EAAE;QAAE,OAAM;IACf,MAAM,KAAK,GAAG,gBAAgB,CAAC,EAAE,CAAC,CAAA;IAClC,MAAM,EAAE;SACL,MAAM,CAAC,KAAK,CAAC;SACb,GAAG,CAAC,KAAK,CAAC;SACV,KAAK,CACJ,cAAc;QACZ,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;QACzD,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CACrB,CAAA;AACL,CAAC","sourcesContent":["import { and, eq } from 'drizzle-orm'\n\nimport type { AnyDb } from '../dialect'\n\nimport { messagesTableFor } from '../internal-tables'\n\nexport async function insertMessage(\n db: AnyDb | undefined,\n value: Record<string, unknown>,\n) {\n if (!db) return\n await db.insert(messagesTableFor(db)).values(value)\n}\n\nexport async function updateMessage(\n db: AnyDb | undefined,\n id: string,\n value: Record<string, unknown>,\n expectedStatus?: string,\n) {\n if (!db) return\n const table = messagesTableFor(db)\n await db\n .update(table)\n .set(value)\n .where(\n expectedStatus\n ? and(eq(table.id, id), eq(table.status, expectedStatus))\n : eq(table.id, id),\n )\n}\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { AnyDb } from '../dialect.js';
|
|
2
|
+
import type { BaseEnv } from '../env.js';
|
|
3
|
+
import type { MessagingConfig, MessagingFacades } from './types.js';
|
|
4
|
+
export type MessagingAdapter = {
|
|
5
|
+
send(input: any): Promise<{
|
|
6
|
+
id?: string;
|
|
7
|
+
}>;
|
|
8
|
+
};
|
|
9
|
+
export type CreateMessagingOptions = {
|
|
10
|
+
env: BaseEnv;
|
|
11
|
+
db?: AnyDb;
|
|
12
|
+
fetchFn?: typeof fetch;
|
|
13
|
+
adapterOverrides?: Record<string, MessagingAdapter>;
|
|
14
|
+
};
|
|
15
|
+
export declare function createMessaging<const TConfig extends MessagingConfig>(config: TConfig, options: CreateMessagingOptions): MessagingFacades<TConfig>;
|
|
16
|
+
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/messaging/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAEvC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AAErC,OAAO,KAAK,EAEV,eAAe,EACf,gBAAgB,EACjB,MAAM,SAAS,CAAA;AAKhB,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO,CAAC;QAAE,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAC3C,CAAA;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC,GAAG,EAAE,OAAO,CAAA;IACZ,EAAE,CAAC,EAAE,KAAK,CAAA;IACV,OAAO,CAAC,EAAE,OAAO,KAAK,CAAA;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;CACpD,CAAA;AA+ID,wBAAgB,eAAe,CAAC,KAAK,CAAC,OAAO,SAAS,eAAe,EACnE,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,sBAAsB,GAC9B,gBAAgB,CAAC,OAAO,CAAC,CA6H3B"}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { insertMessage, updateMessage } from './journal.js';
|
|
2
|
+
import { isMessagingDescriptor } from './types.js';
|
|
3
|
+
const nonEmpty = (value) => typeof value === 'string' && value.trim().length > 0;
|
|
4
|
+
const array = (value) => value === undefined ? [] : Array.isArray(value) ? value : [value];
|
|
5
|
+
function managedConfig(raw) {
|
|
6
|
+
if (!nonEmpty(raw))
|
|
7
|
+
return {};
|
|
8
|
+
let value;
|
|
9
|
+
try {
|
|
10
|
+
value = JSON.parse(raw);
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
throw new Error('[bunderstack] BUNDERSTACK_MESSAGING_CONFIG must be valid JSON');
|
|
14
|
+
}
|
|
15
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
16
|
+
throw new Error('[bunderstack] BUNDERSTACK_MESSAGING_CONFIG must be an object');
|
|
17
|
+
}
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
function consoleCapture(channel, kind, input) {
|
|
21
|
+
console.log(`\n${'─'.repeat(60)}\n` +
|
|
22
|
+
`💬 ${channel} (${kind}, captured — not sent)\n` +
|
|
23
|
+
`${'─'.repeat(60)}\n` +
|
|
24
|
+
`${JSON.stringify(input, null, 2)}\n` +
|
|
25
|
+
`${'─'.repeat(60)}`);
|
|
26
|
+
}
|
|
27
|
+
function emailContent(input, from) {
|
|
28
|
+
return {
|
|
29
|
+
recipients: {
|
|
30
|
+
to: array(input.to),
|
|
31
|
+
cc: array(input.cc),
|
|
32
|
+
bcc: array(input.bcc),
|
|
33
|
+
},
|
|
34
|
+
content: {
|
|
35
|
+
subject: input.subject,
|
|
36
|
+
html: input.html,
|
|
37
|
+
text: input.text,
|
|
38
|
+
from,
|
|
39
|
+
replyTo: input.replyTo,
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function telegramContent(input) {
|
|
44
|
+
return {
|
|
45
|
+
recipients: { to: [String(input.to)] },
|
|
46
|
+
content: { text: input.text, parseMode: input.parseMode },
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function createResendAdapter(apiKey, fetchFn) {
|
|
50
|
+
return {
|
|
51
|
+
async send(input) {
|
|
52
|
+
const response = await fetchFn('https://api.resend.com/emails', {
|
|
53
|
+
method: 'POST',
|
|
54
|
+
headers: {
|
|
55
|
+
Authorization: `Bearer ${apiKey}`,
|
|
56
|
+
'Content-Type': 'application/json',
|
|
57
|
+
},
|
|
58
|
+
body: JSON.stringify({
|
|
59
|
+
from: input.from,
|
|
60
|
+
to: array(input.to),
|
|
61
|
+
subject: input.subject,
|
|
62
|
+
html: input.html,
|
|
63
|
+
text: input.text,
|
|
64
|
+
reply_to: input.replyTo,
|
|
65
|
+
cc: array(input.cc),
|
|
66
|
+
bcc: array(input.bcc),
|
|
67
|
+
tags: input.tags,
|
|
68
|
+
}),
|
|
69
|
+
});
|
|
70
|
+
if (!response.ok) {
|
|
71
|
+
throw new Error(`resend API error (${response.status}): ${await response.text()}`);
|
|
72
|
+
}
|
|
73
|
+
return (await response.json());
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function createTelegramAdapter(token, fetchFn) {
|
|
78
|
+
return {
|
|
79
|
+
async send(input) {
|
|
80
|
+
const response = await fetchFn(`https://api.telegram.org/bot${token}/sendMessage`, {
|
|
81
|
+
method: 'POST',
|
|
82
|
+
headers: { 'Content-Type': 'application/json' },
|
|
83
|
+
body: JSON.stringify({
|
|
84
|
+
chat_id: input.to,
|
|
85
|
+
text: input.text,
|
|
86
|
+
parse_mode: input.parseMode,
|
|
87
|
+
}),
|
|
88
|
+
});
|
|
89
|
+
if (!response.ok) {
|
|
90
|
+
throw new Error(`telegram API error (${response.status}): ${await response.text()}`);
|
|
91
|
+
}
|
|
92
|
+
const body = (await response.json());
|
|
93
|
+
return { id: body.result?.message_id?.toString() };
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function providerConfig(descriptor, managed) {
|
|
98
|
+
const merged = { ...managed[descriptor.provider] };
|
|
99
|
+
for (const [key, value] of Object.entries(descriptor.config)) {
|
|
100
|
+
if (typeof value === 'string' ? nonEmpty(value) : value !== undefined) {
|
|
101
|
+
merged[key] = value;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return merged;
|
|
105
|
+
}
|
|
106
|
+
export function createMessaging(config, options) {
|
|
107
|
+
const managed = managedConfig(options.env.BUNDERSTACK_MESSAGING_CONFIG);
|
|
108
|
+
const entries = Object.entries(config).map(([channel, descriptor]) => {
|
|
109
|
+
if (!channel.trim())
|
|
110
|
+
throw new Error('[bunderstack] messaging channel names cannot be empty');
|
|
111
|
+
if (!isMessagingDescriptor(descriptor)) {
|
|
112
|
+
throw new Error(`[bunderstack] messaging.${channel} is not a provider descriptor`);
|
|
113
|
+
}
|
|
114
|
+
const merged = providerConfig(descriptor, managed);
|
|
115
|
+
const override = options.adapterOverrides?.[channel];
|
|
116
|
+
let adapter = override;
|
|
117
|
+
let source = override ? 'capture' : 'explicit';
|
|
118
|
+
let from;
|
|
119
|
+
if (!adapter &&
|
|
120
|
+
descriptor.kind === 'email' &&
|
|
121
|
+
descriptor.provider === 'resend') {
|
|
122
|
+
const own = descriptor.config;
|
|
123
|
+
const apiKey = merged.apiKey;
|
|
124
|
+
from = nonEmpty(merged.from) ? merged.from : undefined;
|
|
125
|
+
if (nonEmpty(apiKey) && from) {
|
|
126
|
+
source = nonEmpty(own.apiKey) ? 'explicit' : 'managed';
|
|
127
|
+
adapter = createResendAdapter(apiKey, options.fetchFn ?? fetch);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
else if (!adapter && descriptor.kind === 'email') {
|
|
131
|
+
const own = descriptor.config;
|
|
132
|
+
from = nonEmpty(merged.from) ? merged.from : undefined;
|
|
133
|
+
if (own.adapter && from) {
|
|
134
|
+
adapter =
|
|
135
|
+
typeof own.adapter === 'function'
|
|
136
|
+
? { send: own.adapter }
|
|
137
|
+
: own.adapter;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
else if (!adapter && descriptor.kind === 'telegram') {
|
|
141
|
+
const own = descriptor.config;
|
|
142
|
+
const token = merged.botToken;
|
|
143
|
+
if (nonEmpty(token)) {
|
|
144
|
+
source = nonEmpty(own.botToken) ? 'explicit' : 'managed';
|
|
145
|
+
adapter = createTelegramAdapter(token, options.fetchFn ?? fetch);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const capture = !adapter || Boolean(override);
|
|
149
|
+
if (capture)
|
|
150
|
+
source = 'capture';
|
|
151
|
+
return [
|
|
152
|
+
channel,
|
|
153
|
+
{
|
|
154
|
+
async send(input) {
|
|
155
|
+
if (descriptor.kind === 'email' &&
|
|
156
|
+
!input.html &&
|
|
157
|
+
!input.text) {
|
|
158
|
+
throw new Error('email message needs html or text content');
|
|
159
|
+
}
|
|
160
|
+
const resolvedFrom = descriptor.kind === 'email'
|
|
161
|
+
? (input.from ?? from ?? 'unknown@localhost')
|
|
162
|
+
: undefined;
|
|
163
|
+
const normalized = descriptor.kind === 'email'
|
|
164
|
+
? emailContent(input, resolvedFrom)
|
|
165
|
+
: telegramContent(input);
|
|
166
|
+
const id = `message_${crypto.randomUUID()}`;
|
|
167
|
+
const now = Date.now();
|
|
168
|
+
await insertMessage(options.db, {
|
|
169
|
+
id,
|
|
170
|
+
channel,
|
|
171
|
+
kind: descriptor.kind,
|
|
172
|
+
provider: descriptor.provider,
|
|
173
|
+
credentialSource: source,
|
|
174
|
+
status: capture ? 'captured' : 'sending',
|
|
175
|
+
recipientsJson: JSON.stringify(normalized.recipients),
|
|
176
|
+
contentJson: JSON.stringify(normalized.content),
|
|
177
|
+
createdAt: now,
|
|
178
|
+
updatedAt: now,
|
|
179
|
+
});
|
|
180
|
+
if (capture) {
|
|
181
|
+
await adapter?.send(input);
|
|
182
|
+
if (!override && !options.env.BUNDERHOST_ENVIRONMENT_ID) {
|
|
183
|
+
consoleCapture(channel, descriptor.kind, input);
|
|
184
|
+
}
|
|
185
|
+
return { id };
|
|
186
|
+
}
|
|
187
|
+
try {
|
|
188
|
+
const outgoing = descriptor.kind === 'email'
|
|
189
|
+
? { ...input, from: resolvedFrom }
|
|
190
|
+
: input;
|
|
191
|
+
const sent = await adapter.send(outgoing);
|
|
192
|
+
await updateMessage(options.db, id, {
|
|
193
|
+
providerId: sent.id || undefined,
|
|
194
|
+
status: 'sent',
|
|
195
|
+
updatedAt: Date.now(),
|
|
196
|
+
}, 'sending');
|
|
197
|
+
return { id, providerId: sent.id || undefined };
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
await updateMessage(options.db, id, {
|
|
201
|
+
status: 'failed',
|
|
202
|
+
error: error instanceof Error ? error.message : String(error),
|
|
203
|
+
updatedAt: Date.now(),
|
|
204
|
+
}, 'sending');
|
|
205
|
+
throw error;
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
];
|
|
210
|
+
});
|
|
211
|
+
return Object.fromEntries(entries);
|
|
212
|
+
}
|
|
213
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/messaging/runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AnyDb } from '../dialect'\nimport type { EmailAdapter, EmailMessage } from '../email'\nimport type { BaseEnv } from '../env'\nimport type { TelegramMessage } from './telegram'\nimport type {\n AnyMessagingDescriptor,\n MessagingConfig,\n MessagingFacades,\n} from './types'\n\nimport { insertMessage, updateMessage } from './journal'\nimport { isMessagingDescriptor } from './types'\n\nexport type MessagingAdapter = {\n send(input: any): Promise<{ id?: string }>\n}\n\nexport type CreateMessagingOptions = {\n env: BaseEnv\n db?: AnyDb\n fetchFn?: typeof fetch\n adapterOverrides?: Record<string, MessagingAdapter>\n}\n\ntype ManagedConfig = Record<string, Record<string, unknown>>\ntype CredentialSource = 'explicit' | 'managed' | 'capture'\n\nconst nonEmpty = (value: unknown): value is string =>\n typeof value === 'string' && value.trim().length > 0\n\nconst array = (value: string | string[] | undefined) =>\n value === undefined ? [] : Array.isArray(value) ? value : [value]\n\nfunction managedConfig(raw: string | undefined): ManagedConfig {\n if (!nonEmpty(raw)) return {}\n let value: unknown\n try {\n value = JSON.parse(raw)\n } catch {\n throw new Error(\n '[bunderstack] BUNDERSTACK_MESSAGING_CONFIG must be valid JSON',\n )\n }\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n throw new Error(\n '[bunderstack] BUNDERSTACK_MESSAGING_CONFIG must be an object',\n )\n }\n return value as ManagedConfig\n}\n\nfunction consoleCapture(channel: string, kind: string, input: unknown) {\n console.log(\n `\\n${'─'.repeat(60)}\\n` +\n `💬 ${channel} (${kind}, captured — not sent)\\n` +\n `${'─'.repeat(60)}\\n` +\n `${JSON.stringify(input, null, 2)}\\n` +\n `${'─'.repeat(60)}`,\n )\n}\n\nfunction emailContent(input: EmailMessage, from: string) {\n return {\n recipients: {\n to: array(input.to),\n cc: array(input.cc),\n bcc: array(input.bcc),\n },\n content: {\n subject: input.subject,\n html: input.html,\n text: input.text,\n from,\n replyTo: input.replyTo,\n },\n }\n}\n\nfunction telegramContent(input: TelegramMessage) {\n return {\n recipients: { to: [String(input.to)] },\n content: { text: input.text, parseMode: input.parseMode },\n }\n}\n\nfunction createResendAdapter(\n apiKey: string,\n fetchFn: typeof fetch,\n): MessagingAdapter {\n return {\n async send(input: EmailMessage & { from: string; tags?: unknown }) {\n const response = await fetchFn('https://api.resend.com/emails', {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n from: input.from,\n to: array(input.to),\n subject: input.subject,\n html: input.html,\n text: input.text,\n reply_to: input.replyTo,\n cc: array(input.cc),\n bcc: array(input.bcc),\n tags: input.tags,\n }),\n })\n if (!response.ok) {\n throw new Error(\n `resend API error (${response.status}): ${await response.text()}`,\n )\n }\n return (await response.json()) as { id?: string }\n },\n }\n}\n\nfunction createTelegramAdapter(\n token: string,\n fetchFn: typeof fetch,\n): MessagingAdapter {\n return {\n async send(input: TelegramMessage) {\n const response = await fetchFn(\n `https://api.telegram.org/bot${token}/sendMessage`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n chat_id: input.to,\n text: input.text,\n parse_mode: input.parseMode,\n }),\n },\n )\n if (!response.ok) {\n throw new Error(\n `telegram API error (${response.status}): ${await response.text()}`,\n )\n }\n const body = (await response.json()) as {\n result?: { message_id?: number }\n }\n return { id: body.result?.message_id?.toString() }\n },\n }\n}\n\nfunction providerConfig(\n descriptor: AnyMessagingDescriptor,\n managed: ManagedConfig,\n): Record<string, unknown> {\n const merged = { ...managed[descriptor.provider] }\n for (const [key, value] of Object.entries(\n descriptor.config as Record<string, unknown>,\n )) {\n if (typeof value === 'string' ? nonEmpty(value) : value !== undefined) {\n merged[key] = value\n }\n }\n return merged\n}\n\nexport function createMessaging<const TConfig extends MessagingConfig>(\n config: TConfig,\n options: CreateMessagingOptions,\n): MessagingFacades<TConfig> {\n const managed = managedConfig(options.env.BUNDERSTACK_MESSAGING_CONFIG)\n const entries = Object.entries(config).map(([channel, descriptor]) => {\n if (!channel.trim())\n throw new Error('[bunderstack] messaging channel names cannot be empty')\n if (!isMessagingDescriptor(descriptor)) {\n throw new Error(\n `[bunderstack] messaging.${channel} is not a provider descriptor`,\n )\n }\n const merged = providerConfig(descriptor, managed)\n const override = options.adapterOverrides?.[channel]\n let adapter: MessagingAdapter | undefined = override\n let source: CredentialSource = override ? 'capture' : 'explicit'\n let from: string | undefined\n if (\n !adapter &&\n descriptor.kind === 'email' &&\n descriptor.provider === 'resend'\n ) {\n const own = descriptor.config as { apiKey?: string; from?: string }\n const apiKey = merged.apiKey\n from = nonEmpty(merged.from) ? merged.from : undefined\n if (nonEmpty(apiKey) && from) {\n source = nonEmpty(own.apiKey) ? 'explicit' : 'managed'\n adapter = createResendAdapter(apiKey, options.fetchFn ?? fetch)\n }\n } else if (!adapter && descriptor.kind === 'email') {\n const own = descriptor.config as {\n adapter?: EmailAdapter | EmailAdapter['send']\n from?: string\n }\n from = nonEmpty(merged.from) ? merged.from : undefined\n if (own.adapter && from) {\n adapter =\n typeof own.adapter === 'function'\n ? { send: own.adapter }\n : own.adapter\n }\n } else if (!adapter && descriptor.kind === 'telegram') {\n const own = descriptor.config as { botToken?: string }\n const token = merged.botToken\n if (nonEmpty(token)) {\n source = nonEmpty(own.botToken) ? 'explicit' : 'managed'\n adapter = createTelegramAdapter(token, options.fetchFn ?? fetch)\n }\n }\n const capture = !adapter || Boolean(override)\n if (capture) source = 'capture'\n\n return [\n channel,\n {\n async send(input: EmailMessage | TelegramMessage) {\n if (\n descriptor.kind === 'email' &&\n !(input as EmailMessage).html &&\n !(input as EmailMessage).text\n ) {\n throw new Error('email message needs html or text content')\n }\n const resolvedFrom =\n descriptor.kind === 'email'\n ? ((input as EmailMessage).from ?? from ?? 'unknown@localhost')\n : undefined\n const normalized =\n descriptor.kind === 'email'\n ? emailContent(input as EmailMessage, resolvedFrom!)\n : telegramContent(input as TelegramMessage)\n const id = `message_${crypto.randomUUID()}`\n const now = Date.now()\n await insertMessage(options.db, {\n id,\n channel,\n kind: descriptor.kind,\n provider: descriptor.provider,\n credentialSource: source,\n status: capture ? 'captured' : 'sending',\n recipientsJson: JSON.stringify(normalized.recipients),\n contentJson: JSON.stringify(normalized.content),\n createdAt: now,\n updatedAt: now,\n })\n if (capture) {\n await adapter?.send(input)\n if (!override && !options.env.BUNDERHOST_ENVIRONMENT_ID) {\n consoleCapture(channel, descriptor.kind, input)\n }\n return { id }\n }\n try {\n const outgoing =\n descriptor.kind === 'email'\n ? { ...input, from: resolvedFrom }\n : input\n const sent = await adapter!.send(outgoing)\n await updateMessage(\n options.db,\n id,\n {\n providerId: sent.id || undefined,\n status: 'sent',\n updatedAt: Date.now(),\n },\n 'sending',\n )\n return { id, providerId: sent.id || undefined }\n } catch (error) {\n await updateMessage(\n options.db,\n id,\n {\n status: 'failed',\n error: error instanceof Error ? error.message : String(error),\n updatedAt: Date.now(),\n },\n 'sending',\n )\n throw error\n }\n },\n },\n ]\n })\n return Object.fromEntries(entries) as MessagingFacades<TConfig>\n}\n"]}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { AnyDb } from '../dialect.js';
|
|
2
|
+
import type { BaseEnv } from '../env.js';
|
|
3
|
+
import type { MessagingConfig, MessagingFacades } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* What a standalone messaging facade needs. `db` is the application's own
|
|
6
|
+
* connection: pass it so captured and sent messages reach the same journal the
|
|
7
|
+
* app writes, and omit it only when there is no database to write to.
|
|
8
|
+
*/
|
|
9
|
+
export type MessagingContext = {
|
|
10
|
+
env: BaseEnv;
|
|
11
|
+
db?: AnyDb;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Builds channel facades outside a started app, for code that runs before or
|
|
15
|
+
* beside it — a Better Auth builder sending an invitation, a script, a
|
|
16
|
+
* one-off worker. Channels behave exactly as they do on `app.messaging`,
|
|
17
|
+
* capture included.
|
|
18
|
+
*
|
|
19
|
+
* ```ts
|
|
20
|
+
* export const auth = defineAuth(schema, ({ db, env }) => {
|
|
21
|
+
* const messaging = createMessaging({ email: resend({ from: env.EMAIL_FROM }) }, { env, db })
|
|
22
|
+
* return { …, sendInvitation: (to) => messaging.email.send({ to, … }) }
|
|
23
|
+
* })
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare function createMessaging<const TConfig extends MessagingConfig>(channels: TConfig, context: MessagingContext): MessagingFacades<TConfig>;
|
|
27
|
+
//# sourceMappingURL=standalone.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"standalone.d.ts","sourceRoot":"","sources":["../../src/messaging/standalone.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAIhE;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,GAAG,EAAE,OAAO,CAAA;IACZ,EAAE,CAAC,EAAE,KAAK,CAAA;CACX,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAC,KAAK,CAAC,OAAO,SAAS,eAAe,EACnE,QAAQ,EAAE,OAAO,EACjB,OAAO,EAAE,gBAAgB,GACxB,gBAAgB,CAAC,OAAO,CAAC,CAE3B"}
|