bunderstack 0.24.6 → 0.25.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 CHANGED
@@ -2,6 +2,40 @@
2
2
 
3
3
  All notable changes to `bunderstack` will be documented in this file.
4
4
 
5
+ ## [0.25.1] — 2026-09-25
6
+
7
+ ### Fixed
8
+
9
+ - Apps whose schema declares no Better Auth `user` and `session` tables no
10
+ longer build or run a Better Auth instance. With better-auth 1.7.6 or later,
11
+ such apps failed every request, or failed to start with `openapi: true`,
12
+ with `BetterAuthError: Drizzle schema mismatch`. They now serve no
13
+ `/api/auth/*` routes and no auth OpenAPI paths. `app.auth` and `ctx.auth`
14
+ still build the instance on first use.
15
+
16
+ ### Changed
17
+
18
+ - The repository tests against better-auth 1.7.6.
19
+
20
+ ## [0.25.0] — 2026-09-25
21
+
22
+ ### Added
23
+
24
+ - `jobs.enqueue(name, input, { tx })` inserts the job row through the caller's
25
+ Drizzle transaction on `app.jobs`, `context.jobs`, job handler `ctx.jobs`,
26
+ and test fixtures. The job becomes visible to workers only when the
27
+ transaction commits, and a rollback removes it, so applications no longer
28
+ need an outbox table to schedule work atomically with their writes. `app.jobs`
29
+ types `tx` as the app's `BunderstackTx`; a transaction of the other database
30
+ dialect throws. Delivery stays at-least-once.
31
+ - `dedupeUntil: 'start' | 'finish'` on `j.job()` declarations. The default
32
+ `'finish'` keeps today's behavior: a `dedupeKey` collapses enqueues until the
33
+ job reaches a terminal state. `'start'` releases the key when a worker claims
34
+ the job, so an enqueue during the run schedules one more run that reads the
35
+ newer state. Cron declarations reject `dedupeUntil`.
36
+ - Export the `DedupeUntil`, `EnqueueTransaction`, and `TypedEnqueueOptions`
37
+ types.
38
+
5
39
  ## [0.24.6] — 2026-09-21
6
40
 
7
41
  ### Fixed
package/dist/auth.d.ts CHANGED
@@ -30,6 +30,15 @@ export declare function createAuth<TConfig extends BetterAuthConfig = BetterAuth
30
30
  * minted by another local app arrives here too — would break every request.
31
31
  */
32
32
  export declare function missingAuthModels(schema: Record<string, unknown>, cfg: BetterAuthConfig): string[];
33
+ /**
34
+ * Defer building better-auth until someone touches the instance. From 1.7.6 on,
35
+ * constructing it starts a schema check that logs, and every `auth.api` call
36
+ * throws, when the auth tables are missing. An app without auth models never
37
+ * builds one, yet `app.auth` / `ctx.auth` still work for code that reaches for
38
+ * it on purpose. A proxy, not a getter: oRPC middleware and access scopes
39
+ * spread the context, which would read a getter on every request.
40
+ */
41
+ export declare function lazyAuth<TAuth extends object>(create: () => TAuth): TAuth;
33
42
  /**
34
43
  * Adapt the raw better-auth instance to our internal {@link AuthSessionResolver}
35
44
  * contract. better-auth's `getSession` has a union return (a bare session, or a
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AACA,OAAO,EAAc,KAAK,IAAI,EAAE,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAE1E,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAE7C,OAAO,KAAK,EACV,cAAc,EACd,mBAAmB,EACnB,iBAAiB,EAElB,MAAM,UAAU,CAAA;AACjB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAChD,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAC/C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAE1C,KAAK,qBAAqB,CAAC,OAAO,SAAS,gBAAgB,IAAI,OAAO,SAAS;IAC7E,OAAO,EAAE,MAAM,QAAQ,SAAS,SAAS,gBAAgB,EAAE,CAAA;CAC5D,GACG,QAAQ,GACR,EAAE,CAAA;AAEN,KAAK,2BAA2B,CAAC,OAAO,SAAS,gBAAgB,IAAI,IAAI,CACvE,gBAAgB,EAChB,SAAS,CACV,GACC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,GAAG;IACzB,OAAO,EAAE,CAAC,GAAG,qBAAqB,CAAC,OAAO,CAAC,EAAE,UAAU,CAAC,OAAO,OAAO,CAAC,CAAC,CAAA;CACzE,CAAA;AAEH;;;;GAIG;AACH,MAAM,MAAM,eAAe,CACzB,OAAO,SAAS,gBAAgB,GAAG,gBAAgB,IACjD,IAAI,CAAC,2BAA2B,CAAC,OAAO,CAAC,CAAC,CAAA;AAE9C;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,OAAO,SAAS,gBAAgB,GAAG,gBAAgB,EAC5E,EAAE,EAAE,KAAK,EACT,GAAG,EAAE,gBAAgB,EACrB,OAAO,EAAE,OAAO,EAChB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC,eAAe,CAAC,OAAO,CAAC,CAY1B;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,GAAG,EAAE,gBAAgB,GACpB,MAAM,EAAE,CAMV;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAE7D,IAAI,EAAE,IAAI,EACV,WAAW,CAAC,EAAE,iBAAiB,CAAC,MAAM,CAAC,GACtC,mBAAmB,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,cAAc,CAAC,CAAC,CA4CzD;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,gBAAgB,EACrB,KAAK,EAAE,WAAW,EAClB,eAAe,EAAE,OAAO,GACvB,gBAAgB,CAkClB"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AACA,OAAO,EAAc,KAAK,IAAI,EAAE,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAE1E,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAE7C,OAAO,KAAK,EACV,cAAc,EACd,mBAAmB,EACnB,iBAAiB,EAElB,MAAM,UAAU,CAAA;AACjB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAChD,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAC/C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAE1C,KAAK,qBAAqB,CAAC,OAAO,SAAS,gBAAgB,IAAI,OAAO,SAAS;IAC7E,OAAO,EAAE,MAAM,QAAQ,SAAS,SAAS,gBAAgB,EAAE,CAAA;CAC5D,GACG,QAAQ,GACR,EAAE,CAAA;AAEN,KAAK,2BAA2B,CAAC,OAAO,SAAS,gBAAgB,IAAI,IAAI,CACvE,gBAAgB,EAChB,SAAS,CACV,GACC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,GAAG;IACzB,OAAO,EAAE,CAAC,GAAG,qBAAqB,CAAC,OAAO,CAAC,EAAE,UAAU,CAAC,OAAO,OAAO,CAAC,CAAC,CAAA;CACzE,CAAA;AAEH;;;;GAIG;AACH,MAAM,MAAM,eAAe,CACzB,OAAO,SAAS,gBAAgB,GAAG,gBAAgB,IACjD,IAAI,CAAC,2BAA2B,CAAC,OAAO,CAAC,CAAC,CAAA;AAE9C;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,OAAO,SAAS,gBAAgB,GAAG,gBAAgB,EAC5E,EAAE,EAAE,KAAK,EACT,GAAG,EAAE,gBAAgB,EACrB,OAAO,EAAE,OAAO,EAChB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC,eAAe,CAAC,OAAO,CAAC,CAY1B;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,GAAG,EAAE,gBAAgB,GACpB,MAAM,EAAE,CAMV;AAED;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,KAAK,SAAS,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,GAAG,KAAK,CAezE;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAE7D,IAAI,EAAE,IAAI,EACV,WAAW,CAAC,EAAE,iBAAiB,CAAC,MAAM,CAAC,GACtC,mBAAmB,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,cAAc,CAAC,CAAC,CA4CzD;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,gBAAgB,EACrB,KAAK,EAAE,WAAW,EAClB,eAAe,EAAE,OAAO,GACvB,gBAAgB,CAkClB"}
package/dist/auth.js CHANGED
@@ -33,6 +33,30 @@ export function missingAuthModels(schema, cfg) {
33
33
  ];
34
34
  return models.filter((model) => !schema[model]);
35
35
  }
36
+ /**
37
+ * Defer building better-auth until someone touches the instance. From 1.7.6 on,
38
+ * constructing it starts a schema check that logs, and every `auth.api` call
39
+ * throws, when the auth tables are missing. An app without auth models never
40
+ * builds one, yet `app.auth` / `ctx.auth` still work for code that reaches for
41
+ * it on purpose. A proxy, not a getter: oRPC middleware and access scopes
42
+ * spread the context, which would read a getter on every request.
43
+ */
44
+ export function lazyAuth(create) {
45
+ let instance;
46
+ const resolve = () => {
47
+ instance ??= create();
48
+ return instance;
49
+ };
50
+ return new Proxy({}, {
51
+ get: (_, key) => Reflect.get(resolve(), key),
52
+ has: (_, key) => Reflect.has(resolve(), key),
53
+ ownKeys: () => Reflect.ownKeys(resolve()),
54
+ getOwnPropertyDescriptor: (_, key) => {
55
+ const descriptor = Reflect.getOwnPropertyDescriptor(resolve(), key);
56
+ return descriptor ? { ...descriptor, configurable: true } : undefined;
57
+ },
58
+ });
59
+ }
36
60
  /**
37
61
  * Adapt the raw better-auth instance to our internal {@link AuthSessionResolver}
38
62
  * contract. better-auth's `getSession` has a union return (a bare session, or a
@@ -58,7 +82,7 @@ export function toAuthSessionResolver(auth, sessionUser) {
58
82
  : undefined;
59
83
  const emailVerified = 'emailVerified' in result.user && result.user.emailVerified === true;
60
84
  const mapped = sessionUser?.mapUser(result.user);
61
- const extras = { ...(mapped ?? {}) };
85
+ const extras = { ...mapped };
62
86
  delete extras.id;
63
87
  delete extras.email;
64
88
  delete extras.emailVerified;
package/dist/auth.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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","sourcesContent":["// src/auth.ts\nimport { betterAuth, type Auth, type BetterAuthPlugin } from 'better-auth'\nimport { drizzleAdapter } from 'better-auth/adapters/drizzle'\nimport { openAPI } from 'better-auth/plugins'\n\nimport type {\n AccessUserBase,\n AuthSessionResolver,\n SessionUserConfig,\n SessionUserSource,\n} from './access'\nimport type { BetterAuthConfig } from './config'\nimport type { AnyDb, Dialect } from './dialect'\nimport type { EmailFacade } from './email'\n\ntype ConfiguredAuthPlugins<TConfig extends BetterAuthConfig> = TConfig extends {\n plugins: infer TPlugins extends readonly BetterAuthPlugin[]\n}\n ? TPlugins\n : []\n\ntype WithBunderstackAuthDefaults<TConfig extends BetterAuthConfig> = Omit<\n BetterAuthConfig,\n 'plugins'\n> &\n Omit<TConfig, 'plugins'> & {\n plugins: [...ConfiguredAuthPlugins<TConfig>, ReturnType<typeof openAPI>]\n }\n\n/**\n * The public Better Auth instance retains every endpoint and model field from\n * the declared config. Bunderstack always installs OpenAPI at runtime, so its\n * endpoints are part of the public type even when the app omitted the plugin.\n */\nexport type BunderstackAuth<\n TConfig extends BetterAuthConfig = BetterAuthConfig,\n> = Auth<WithBunderstackAuthDefaults<TConfig>>\n\n/**\n * Build Better Auth while exposing its named plugin-aware public type. The\n * implementation accepts the resolved runtime config, while `TConfig` carries\n * the declaration that produced it; their relationship is asserted once here.\n */\nexport function createAuth<TConfig extends BetterAuthConfig = BetterAuthConfig>(\n db: AnyDb,\n cfg: BetterAuthConfig,\n dialect: Dialect,\n userSchema?: Record<string, unknown>,\n): BunderstackAuth<TConfig> {\n const hasOpenApi = cfg.plugins?.some((p: any) => p.id === 'open-api')\n const plugins = hasOpenApi ? cfg.plugins : [...(cfg.plugins || []), openAPI()]\n\n return betterAuth({\n ...cfg,\n plugins,\n database: drizzleAdapter(db as Parameters<typeof drizzleAdapter>[0], {\n provider: dialect === 'pg' ? 'pg' : 'sqlite',\n ...(userSchema ? { schema: userSchema } : {}),\n }),\n }) as unknown as BunderstackAuth<TConfig>\n}\n\n/**\n * better-auth resolves its models out of the app's own schema, so an app that\n * declares none has no auth at all. Session resolution must then short-circuit\n * to null: the drizzle adapter throws on a missing model, and any session\n * cookie for the host — a browser keeps those per host, not per port, so one\n * minted by another local app arrives here too — would break every request.\n */\nexport function missingAuthModels(\n schema: Record<string, unknown>,\n cfg: BetterAuthConfig,\n): string[] {\n const models = [\n cfg.user?.modelName ?? 'user',\n cfg.session?.modelName ?? 'session',\n ]\n return models.filter((model) => !schema[model])\n}\n\n/**\n * Adapt the raw better-auth instance to our internal {@link AuthSessionResolver}\n * contract. better-auth's `getSession` has a union return (a bare session, or a\n * `{ headers, response }` wrapper when `returnHeaders` is set); we only ever\n * call the bare form, so we narrow on `'user' in result` and map to our shape.\n * Keeping this adapter here means internal modules never depend on better-auth's\n * evolving types.\n */\nexport function toAuthSessionResolver<\n TExtra extends Record<string, unknown> = Record<never, never>,\n>(\n auth: Auth,\n sessionUser?: SessionUserConfig<TExtra>,\n): AuthSessionResolver<Omit<TExtra, keyof AccessUserBase>> {\n return {\n api: {\n async getSession({ headers }) {\n const result = await auth.api.getSession({ headers })\n if (result && 'user' in result && result.user) {\n const session = 'session' in result ? result.session : null\n const activeOrganizationId =\n session &&\n 'activeOrganizationId' in session &&\n typeof session.activeOrganizationId === 'string'\n ? session.activeOrganizationId\n : null\n const role =\n 'role' in result.user && typeof result.user.role === 'string'\n ? result.user.role\n : undefined\n const emailVerified =\n 'emailVerified' in result.user && result.user.emailVerified === true\n const mapped = sessionUser?.mapUser(\n result.user as SessionUserSource,\n ) as Record<string, unknown> | undefined\n const extras = { ...(mapped ?? {}) }\n delete extras.id\n delete extras.email\n delete extras.emailVerified\n delete extras.name\n delete extras.role\n return {\n user: {\n ...extras,\n id: result.user.id,\n email: result.user.email,\n emailVerified,\n name: result.user.name,\n ...(role ? { role } : {}),\n },\n session: session ? { activeOrganizationId } : null,\n }\n }\n return null\n },\n },\n } as AuthSessionResolver<Omit<TExtra, keyof AccessUserBase>>\n}\n\n/**\n * Fill better-auth's email hooks from the bunderstack email facade. Only fills\n * gaps: user-supplied handlers always win, and nothing is injected when email\n * isn't configured. emailAndPassword is only touched when the user enabled it\n * (injecting it unasked would enable the feature).\n */\nexport function withEmailAuthDefaults(\n cfg: BetterAuthConfig,\n email: EmailFacade,\n emailConfigured: boolean,\n): BetterAuthConfig {\n if (!emailConfigured) return cfg\n const out: BetterAuthConfig = { ...cfg }\n\n if (\n cfg.emailAndPassword?.enabled &&\n !cfg.emailAndPassword.sendResetPassword\n ) {\n out.emailAndPassword = {\n ...cfg.emailAndPassword,\n sendResetPassword: async ({ user, url }) => {\n await email.send({\n to: user.email,\n subject: 'Reset your password',\n text: `Click the link to reset your password:\\n\\n${url}\\n\\nIf you didn't request this, you can ignore this email.`,\n })\n },\n }\n }\n\n if (!cfg.emailVerification?.sendVerificationEmail) {\n out.emailVerification = {\n ...cfg.emailVerification,\n sendVerificationEmail: async ({ user, url }) => {\n await email.send({\n to: user.email,\n subject: 'Verify your email',\n text: `Click the link to verify your email address:\\n\\n${url}`,\n })\n },\n }\n }\n\n return out\n}\n"]}
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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","sourcesContent":["// src/auth.ts\nimport { betterAuth, type Auth, type BetterAuthPlugin } from 'better-auth'\nimport { drizzleAdapter } from 'better-auth/adapters/drizzle'\nimport { openAPI } from 'better-auth/plugins'\n\nimport type {\n AccessUserBase,\n AuthSessionResolver,\n SessionUserConfig,\n SessionUserSource,\n} from './access'\nimport type { BetterAuthConfig } from './config'\nimport type { AnyDb, Dialect } from './dialect'\nimport type { EmailFacade } from './email'\n\ntype ConfiguredAuthPlugins<TConfig extends BetterAuthConfig> = TConfig extends {\n plugins: infer TPlugins extends readonly BetterAuthPlugin[]\n}\n ? TPlugins\n : []\n\ntype WithBunderstackAuthDefaults<TConfig extends BetterAuthConfig> = Omit<\n BetterAuthConfig,\n 'plugins'\n> &\n Omit<TConfig, 'plugins'> & {\n plugins: [...ConfiguredAuthPlugins<TConfig>, ReturnType<typeof openAPI>]\n }\n\n/**\n * The public Better Auth instance retains every endpoint and model field from\n * the declared config. Bunderstack always installs OpenAPI at runtime, so its\n * endpoints are part of the public type even when the app omitted the plugin.\n */\nexport type BunderstackAuth<\n TConfig extends BetterAuthConfig = BetterAuthConfig,\n> = Auth<WithBunderstackAuthDefaults<TConfig>>\n\n/**\n * Build Better Auth while exposing its named plugin-aware public type. The\n * implementation accepts the resolved runtime config, while `TConfig` carries\n * the declaration that produced it; their relationship is asserted once here.\n */\nexport function createAuth<TConfig extends BetterAuthConfig = BetterAuthConfig>(\n db: AnyDb,\n cfg: BetterAuthConfig,\n dialect: Dialect,\n userSchema?: Record<string, unknown>,\n): BunderstackAuth<TConfig> {\n const hasOpenApi = cfg.plugins?.some((p: any) => p.id === 'open-api')\n const plugins = hasOpenApi ? cfg.plugins : [...(cfg.plugins || []), openAPI()]\n\n return betterAuth({\n ...cfg,\n plugins,\n database: drizzleAdapter(db as Parameters<typeof drizzleAdapter>[0], {\n provider: dialect === 'pg' ? 'pg' : 'sqlite',\n ...(userSchema ? { schema: userSchema } : {}),\n }),\n }) as unknown as BunderstackAuth<TConfig>\n}\n\n/**\n * better-auth resolves its models out of the app's own schema, so an app that\n * declares none has no auth at all. Session resolution must then short-circuit\n * to null: the drizzle adapter throws on a missing model, and any session\n * cookie for the host — a browser keeps those per host, not per port, so one\n * minted by another local app arrives here too — would break every request.\n */\nexport function missingAuthModels(\n schema: Record<string, unknown>,\n cfg: BetterAuthConfig,\n): string[] {\n const models = [\n cfg.user?.modelName ?? 'user',\n cfg.session?.modelName ?? 'session',\n ]\n return models.filter((model) => !schema[model])\n}\n\n/**\n * Defer building better-auth until someone touches the instance. From 1.7.6 on,\n * constructing it starts a schema check that logs, and every `auth.api` call\n * throws, when the auth tables are missing. An app without auth models never\n * builds one, yet `app.auth` / `ctx.auth` still work for code that reaches for\n * it on purpose. A proxy, not a getter: oRPC middleware and access scopes\n * spread the context, which would read a getter on every request.\n */\nexport function lazyAuth<TAuth extends object>(create: () => TAuth): TAuth {\n let instance: TAuth | undefined\n const resolve = () => {\n instance ??= create()\n return instance\n }\n return new Proxy({} as TAuth, {\n get: (_, key) => Reflect.get(resolve(), key),\n has: (_, key) => Reflect.has(resolve(), key),\n ownKeys: () => Reflect.ownKeys(resolve()),\n getOwnPropertyDescriptor: (_, key) => {\n const descriptor = Reflect.getOwnPropertyDescriptor(resolve(), key)\n return descriptor ? { ...descriptor, configurable: true } : undefined\n },\n })\n}\n\n/**\n * Adapt the raw better-auth instance to our internal {@link AuthSessionResolver}\n * contract. better-auth's `getSession` has a union return (a bare session, or a\n * `{ headers, response }` wrapper when `returnHeaders` is set); we only ever\n * call the bare form, so we narrow on `'user' in result` and map to our shape.\n * Keeping this adapter here means internal modules never depend on better-auth's\n * evolving types.\n */\nexport function toAuthSessionResolver<\n TExtra extends Record<string, unknown> = Record<never, never>,\n>(\n auth: Auth,\n sessionUser?: SessionUserConfig<TExtra>,\n): AuthSessionResolver<Omit<TExtra, keyof AccessUserBase>> {\n return {\n api: {\n async getSession({ headers }) {\n const result = await auth.api.getSession({ headers })\n if (result && 'user' in result && result.user) {\n const session = 'session' in result ? result.session : null\n const activeOrganizationId =\n session &&\n 'activeOrganizationId' in session &&\n typeof session.activeOrganizationId === 'string'\n ? session.activeOrganizationId\n : null\n const role =\n 'role' in result.user && typeof result.user.role === 'string'\n ? result.user.role\n : undefined\n const emailVerified =\n 'emailVerified' in result.user && result.user.emailVerified === true\n const mapped = sessionUser?.mapUser(\n result.user as SessionUserSource,\n ) as Record<string, unknown> | undefined\n const extras = { ...mapped }\n delete extras.id\n delete extras.email\n delete extras.emailVerified\n delete extras.name\n delete extras.role\n return {\n user: {\n ...extras,\n id: result.user.id,\n email: result.user.email,\n emailVerified,\n name: result.user.name,\n ...(role ? { role } : {}),\n },\n session: session ? { activeOrganizationId } : null,\n }\n }\n return null\n },\n },\n } as AuthSessionResolver<Omit<TExtra, keyof AccessUserBase>>\n}\n\n/**\n * Fill better-auth's email hooks from the bunderstack email facade. Only fills\n * gaps: user-supplied handlers always win, and nothing is injected when email\n * isn't configured. emailAndPassword is only touched when the user enabled it\n * (injecting it unasked would enable the feature).\n */\nexport function withEmailAuthDefaults(\n cfg: BetterAuthConfig,\n email: EmailFacade,\n emailConfigured: boolean,\n): BetterAuthConfig {\n if (!emailConfigured) return cfg\n const out: BetterAuthConfig = { ...cfg }\n\n if (\n cfg.emailAndPassword?.enabled &&\n !cfg.emailAndPassword.sendResetPassword\n ) {\n out.emailAndPassword = {\n ...cfg.emailAndPassword,\n sendResetPassword: async ({ user, url }) => {\n await email.send({\n to: user.email,\n subject: 'Reset your password',\n text: `Click the link to reset your password:\\n\\n${url}\\n\\nIf you didn't request this, you can ignore this email.`,\n })\n },\n }\n }\n\n if (!cfg.emailVerification?.sendVerificationEmail) {\n out.emailVerification = {\n ...cfg.emailVerification,\n sendVerificationEmail: async ({ user, url }) => {\n await email.send({\n to: user.email,\n subject: 'Verify your email',\n text: `Click the link to verify your email address:\\n\\n${url}`,\n })\n },\n }\n }\n\n return out\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -18,7 +18,7 @@ export { buildReadinessReport, readinessReportSchema } from './readiness.js';
18
18
  export type { ReadinessCheck, ReadinessCheckName, ReadinessCode, ReadinessReport, ReadinessStatus, } from './readiness.js';
19
19
  export type { EmailMessage, EmailAdapter } from './email.js';
20
20
  export { createJobsBuilder } from './jobs/index.js';
21
- export type { BunderstackJobContext, BunderstackJobsBuilder, BackgroundDefinition, BackgroundDefs, CronDefinition, CronInvocation, EnqueueOptions, JobContext, JobDefinition, JobsDefs, JobsFacade, JobsRuntimeFacade, QueueJobDefinition, QueueJobKeys, RunWorkerOptions, StartWorkerOptions, WorkerHandle, } from './jobs/index.js';
21
+ export type { BunderstackJobContext, BunderstackJobsBuilder, BackgroundDefinition, BackgroundDefs, CronDefinition, CronInvocation, DedupeUntil, EnqueueOptions, EnqueueTransaction, JobContext, JobDefinition, JobsDefs, JobsFacade, JobsRuntimeFacade, QueueJobDefinition, QueueJobKeys, TypedEnqueueOptions, RunWorkerOptions, StartWorkerOptions, WorkerHandle, } from './jobs/index.js';
22
22
  export { defineSessionUser, defineAccess, validateAndResolveAccess, checkAccess, AUTH_TABLE_NAMES, } from './access.js';
23
23
  export type { TableAccessInput, OperationRule, AccessContext, AccessUser, AccessUserBase, SessionUserConfig, SessionUserExtraOf, SessionUserSource, } from './access.js';
24
24
  export { typeid, generate as generateTypeId, parse as parseTypeId, asTypeId, } from './typeid.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAA;AACzB,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAgB5E,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAG1C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAE3C,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AASvE,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,OAAO,CAAA;AAEzE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAOzD,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AASzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAoBhD,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,wBAAwB,EACxB,WAAW,EACX,gBAAgB,GACjB,MAAM,UAAU,CAAA;AAWjB,OAAO,EACL,MAAM,EACN,QAAQ,IAAI,cAAc,EAC1B,KAAK,IAAI,WAAW,EACpB,QAAQ,GACT,MAAM,UAAU,CAAA;AAejB,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAMxD,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAU3D,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EACrB,gBAAgB,EAChB,2BAA2B,GAC5B,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA","sourcesContent":["export * from './backend'\nexport { createMessaging, resend, customEmail, telegram } from './messaging'\nexport type {\n MessagingConfig,\n MessagingDescriptor,\n MessagingFacade,\n MessagingFacades,\n TelegramMessage,\n} from './messaging'\nexport type {\n AppRunWorkerOptions,\n AppStartWorkerOptions,\n AuthInstance,\n BucketNamesOf,\n BunderstackApp,\n StorageFacade,\n} from './runtime'\nexport { listSpec } from './api/list-spec'\nexport type { ListSpecOptions } from './api/list-spec'\nexport type { BunderstackDb, BunderstackTx } from './db'\nexport { MAX_LIST_LIMIT } from './list-query'\nexport { BunderstackError } from './errors'\nexport type { BunderstackErrorCode } from './errors'\nexport { resolveConfig, resolveAuthConfig, defineAuth } from './config'\nexport type {\n AuthConfigContext,\n AuthConfigFactory,\n AuthConfigInput,\n BetterAuthConfig,\n BunderstackConfig,\n ResolvedConfig,\n} from './config'\nexport { validateEnv, createClientEnv, BunderstackEnvError } from './env'\nexport type { EnvConfigInput, BaseEnv, ValidatedEnv } from './env'\nexport { buildManifest, parseManifest } from './manifest'\nexport type {\n ApiOperation,\n ApiOperationEffect,\n BunderstackManifest,\n ManifestEnvVar,\n} from './manifest'\nexport { buildReadinessReport, readinessReportSchema } from './readiness'\nexport type {\n ReadinessCheck,\n ReadinessCheckName,\n ReadinessCode,\n ReadinessReport,\n ReadinessStatus,\n} from './readiness'\nexport type { EmailMessage, EmailAdapter } from './email'\nexport { createJobsBuilder } from './jobs/index'\nexport type {\n BunderstackJobContext,\n BunderstackJobsBuilder,\n BackgroundDefinition,\n BackgroundDefs,\n CronDefinition,\n CronInvocation,\n EnqueueOptions,\n JobContext,\n JobDefinition,\n JobsDefs,\n JobsFacade,\n JobsRuntimeFacade,\n QueueJobDefinition,\n QueueJobKeys,\n RunWorkerOptions,\n StartWorkerOptions,\n WorkerHandle,\n} from './jobs/index'\nexport {\n defineSessionUser,\n defineAccess,\n validateAndResolveAccess,\n checkAccess,\n AUTH_TABLE_NAMES,\n} from './access'\nexport type {\n TableAccessInput,\n OperationRule,\n AccessContext,\n AccessUser,\n AccessUserBase,\n SessionUserConfig,\n SessionUserExtraOf,\n SessionUserSource,\n} from './access'\nexport {\n typeid,\n generate as generateTypeId,\n parse as parseTypeId,\n asTypeId,\n} from './typeid'\nexport type { TypeId } from './typeid'\nexport type {\n DatabaseAdapter,\n DatabaseConnection,\n DatabaseConnectionResult,\n} from './database/adapter'\nexport type { StorageAdapter } from './storage/index'\nexport type {\n StorageConfigInput,\n BucketConfigInput,\n ResolvedBucket,\n} from './storage/buckets'\nexport type { TransformSpec } from './storage/thumbnails'\nexport type { RealtimeAction } from './realtime/publisher'\nexport { createRealtimeFacade } from './realtime/facade'\nexport type {\n RealtimeFacade,\n RealtimeTransport,\n SchemaTable,\n} from './realtime/facade'\nexport { createApiBuilder, defineApi } from './api/builder'\nexport type { BunderstackApiBuilder, ApiFactory } from './api/builder'\nexport type { ApiContext } from './api/context'\nexport type {\n CrudApiRouterFor,\n ExposedApiTables,\n MergeApiRouterTypes,\n UnifiedApiRouter,\n} from './api/types'\nexport type { TableCrudProcedures } from './api/crud-router'\nexport {\n buildApiRegistry,\n mergeApiRoutersStrict,\n normalizeApiPath,\n normalizeForeignOpenAPISpec,\n} from './api/registry'\nexport { mergeOpenAPISpecs } from './api/openapi'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAA;AACzB,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAgB5E,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAG1C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAE3C,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AASvE,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,OAAO,CAAA;AAEzE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAOzD,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AASzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAuBhD,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,wBAAwB,EACxB,WAAW,EACX,gBAAgB,GACjB,MAAM,UAAU,CAAA;AAWjB,OAAO,EACL,MAAM,EACN,QAAQ,IAAI,cAAc,EAC1B,KAAK,IAAI,WAAW,EACpB,QAAQ,GACT,MAAM,UAAU,CAAA;AAejB,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAMxD,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAU3D,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EACrB,gBAAgB,EAChB,2BAA2B,GAC5B,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA","sourcesContent":["export * from './backend'\nexport { createMessaging, resend, customEmail, telegram } from './messaging'\nexport type {\n MessagingConfig,\n MessagingDescriptor,\n MessagingFacade,\n MessagingFacades,\n TelegramMessage,\n} from './messaging'\nexport type {\n AppRunWorkerOptions,\n AppStartWorkerOptions,\n AuthInstance,\n BucketNamesOf,\n BunderstackApp,\n StorageFacade,\n} from './runtime'\nexport { listSpec } from './api/list-spec'\nexport type { ListSpecOptions } from './api/list-spec'\nexport type { BunderstackDb, BunderstackTx } from './db'\nexport { MAX_LIST_LIMIT } from './list-query'\nexport { BunderstackError } from './errors'\nexport type { BunderstackErrorCode } from './errors'\nexport { resolveConfig, resolveAuthConfig, defineAuth } from './config'\nexport type {\n AuthConfigContext,\n AuthConfigFactory,\n AuthConfigInput,\n BetterAuthConfig,\n BunderstackConfig,\n ResolvedConfig,\n} from './config'\nexport { validateEnv, createClientEnv, BunderstackEnvError } from './env'\nexport type { EnvConfigInput, BaseEnv, ValidatedEnv } from './env'\nexport { buildManifest, parseManifest } from './manifest'\nexport type {\n ApiOperation,\n ApiOperationEffect,\n BunderstackManifest,\n ManifestEnvVar,\n} from './manifest'\nexport { buildReadinessReport, readinessReportSchema } from './readiness'\nexport type {\n ReadinessCheck,\n ReadinessCheckName,\n ReadinessCode,\n ReadinessReport,\n ReadinessStatus,\n} from './readiness'\nexport type { EmailMessage, EmailAdapter } from './email'\nexport { createJobsBuilder } from './jobs/index'\nexport type {\n BunderstackJobContext,\n BunderstackJobsBuilder,\n BackgroundDefinition,\n BackgroundDefs,\n CronDefinition,\n CronInvocation,\n DedupeUntil,\n EnqueueOptions,\n EnqueueTransaction,\n JobContext,\n JobDefinition,\n JobsDefs,\n JobsFacade,\n JobsRuntimeFacade,\n QueueJobDefinition,\n QueueJobKeys,\n TypedEnqueueOptions,\n RunWorkerOptions,\n StartWorkerOptions,\n WorkerHandle,\n} from './jobs/index'\nexport {\n defineSessionUser,\n defineAccess,\n validateAndResolveAccess,\n checkAccess,\n AUTH_TABLE_NAMES,\n} from './access'\nexport type {\n TableAccessInput,\n OperationRule,\n AccessContext,\n AccessUser,\n AccessUserBase,\n SessionUserConfig,\n SessionUserExtraOf,\n SessionUserSource,\n} from './access'\nexport {\n typeid,\n generate as generateTypeId,\n parse as parseTypeId,\n asTypeId,\n} from './typeid'\nexport type { TypeId } from './typeid'\nexport type {\n DatabaseAdapter,\n DatabaseConnection,\n DatabaseConnectionResult,\n} from './database/adapter'\nexport type { StorageAdapter } from './storage/index'\nexport type {\n StorageConfigInput,\n BucketConfigInput,\n ResolvedBucket,\n} from './storage/buckets'\nexport type { TransformSpec } from './storage/thumbnails'\nexport type { RealtimeAction } from './realtime/publisher'\nexport { createRealtimeFacade } from './realtime/facade'\nexport type {\n RealtimeFacade,\n RealtimeTransport,\n SchemaTable,\n} from './realtime/facade'\nexport { createApiBuilder, defineApi } from './api/builder'\nexport type { BunderstackApiBuilder, ApiFactory } from './api/builder'\nexport type { ApiContext } from './api/context'\nexport type {\n CrudApiRouterFor,\n ExposedApiTables,\n MergeApiRouterTypes,\n UnifiedApiRouter,\n} from './api/types'\nexport type { TableCrudProcedures } from './api/crud-router'\nexport {\n buildApiRegistry,\n mergeApiRoutersStrict,\n normalizeApiPath,\n normalizeForeignOpenAPISpec,\n} from './api/registry'\nexport { mergeOpenAPISpecs } from './api/openapi'\n"]}
@@ -1,5 +1,6 @@
1
1
  import type { StandardSchemaV1 } from '@standard-schema/spec';
2
- import type { DbFor } from '../db.js';
2
+ import type { BunderstackTx, DbFor } from '../db.js';
3
+ import type { AnyDb } from '../dialect.js';
3
4
  import type { MessagingConfig, MessagingFacadesFor } from '../messaging/index.js';
4
5
  import type { StorageFacade } from '../runtime.js';
5
6
  import { type CatchUp } from './slots.js';
@@ -16,14 +17,45 @@ export type BackgroundTiming = {
16
17
  timeout?: number;
17
18
  };
18
19
  export declare function leaseDurationFor<T extends BackgroundTiming>(def: T): number;
20
+ /**
21
+ * Structural view of a Drizzle database or transaction handle that `enqueue`
22
+ * can insert through. The loose runtime facade accepts any handle of the app's
23
+ * dialect; `app.jobs` narrows it to the app's `BunderstackTx`.
24
+ */
25
+ export type EnqueueTransaction = AnyDb;
19
26
  export type EnqueueOptions = {
20
- /** Collapse duplicate enqueues while the queue row is non-terminal. */
27
+ /**
28
+ * Collapse duplicate enqueues while the queue row holds its key: until a
29
+ * terminal state by default, or until it is claimed with
30
+ * `dedupeUntil: 'start'`.
31
+ */
21
32
  dedupeKey?: string;
22
33
  /** Milliseconds from now until the job becomes claimable. */
23
34
  delay?: number;
24
35
  /** Absolute time the job becomes claimable; wins over `delay`. */
25
36
  runAt?: Date | number;
37
+ /**
38
+ * Insert the job row through this transaction. The job becomes visible to
39
+ * workers only when the transaction commits; a rollback removes it. Must be
40
+ * a transaction on the app's own database.
41
+ */
42
+ tx?: EnqueueTransaction;
43
+ };
44
+ /** `EnqueueOptions` with `tx` narrowed to the app schema's transaction type. */
45
+ export type TypedEnqueueOptions<TSchema extends Record<string, unknown>> = Omit<EnqueueOptions, 'tx'> & {
46
+ /**
47
+ * Insert the job row through this transaction. The job becomes visible to
48
+ * workers only when the transaction commits; a rollback removes it.
49
+ */
50
+ tx?: BunderstackTx<TSchema>;
26
51
  };
52
+ /**
53
+ * How long a queue job's `dedupeKey` collapses new enqueues.
54
+ * - `'finish'` (default): until the job reaches a terminal state.
55
+ * - `'start'`: until a worker claims the job; later enqueues create a new row
56
+ * that runs after it and reads the newer state.
57
+ */
58
+ export type DedupeUntil = 'start' | 'finish';
27
59
  export type TickResult = {
28
60
  /** Rows moved from pending to running this tick. */
29
61
  claimed: number;
@@ -68,6 +100,12 @@ export type QueueJobDefinition<TInput, TSchema extends Record<string, unknown> =
68
100
  };
69
101
  /** Max simultaneous `running` rows of this type, enforced per worker. */
70
102
  concurrency?: number;
103
+ /**
104
+ * When a `dedupeKey` stops collapsing new enqueues. Default `'finish'`:
105
+ * held until a terminal state. `'start'`: released when a worker claims the
106
+ * job, so an enqueue during the run schedules one more run.
107
+ */
108
+ dedupeUntil?: DedupeUntil;
71
109
  handler: (input: TInput, ctx: JobContext<TSchema, TEnvResult, TMessaging>) => Promise<void> | void;
72
110
  /** Fires once, after the final attempt fails. Errors here are logged, never retried. */
73
111
  onFailed?: (input: TInput, error: Error, ctx: JobContext<TSchema, TEnvResult, TMessaging>) => Promise<void> | void;
@@ -133,10 +171,11 @@ type JobInputOf<TDef> = TDef extends QueueJobDefinition<infer TInput, any, any,
133
171
  * methods instead would make TS treat them as overloaded, so the loose
134
172
  * `(name: string, ...)` signature would still accept any name.
135
173
  */
136
- export type JobsFacade<TDefs extends JobsDefs> = Omit<JobsRuntimeFacade, 'enqueue'> & {
137
- enqueue<K extends QueueJobKeys<TDefs>>(name: K, ...rest: JobInputOf<TDefs[K]> extends undefined ? [input?: undefined, opts?: EnqueueOptions] : [input: JobInputOf<TDefs[K]>, opts?: EnqueueOptions]): Promise<{
174
+ export type JobsFacade<TDefs extends JobsDefs, TSchema extends Record<string, unknown> | undefined = undefined> = Omit<JobsRuntimeFacade, 'enqueue'> & {
175
+ enqueue<K extends QueueJobKeys<TDefs>>(name: K, ...rest: JobInputOf<TDefs[K]> extends undefined ? [input?: undefined, opts?: JobsFacadeEnqueueOptions<TSchema>] : [input: JobInputOf<TDefs[K]>, opts?: JobsFacadeEnqueueOptions<TSchema>]): Promise<{
138
176
  id: string;
139
177
  }>;
140
178
  };
179
+ type JobsFacadeEnqueueOptions<TSchema extends Record<string, unknown> | undefined> = [TSchema] extends [Record<string, unknown>] ? TypedEnqueueOptions<TSchema> : EnqueueOptions;
141
180
  export {};
142
181
  //# sourceMappingURL=define.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"define.d.ts","sourceRoot":"","sources":["../../src/jobs/define.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"define.d.ts","sourceRoot":"","sources":["../../src/jobs/define.ts"],"names":[],"mappings":"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"}
@@ -32,6 +32,7 @@ export function validateBackgroundDefs(defs) {
32
32
  }
33
33
  if (def.kind === 'cron') {
34
34
  parseCron(def.schedule);
35
+ assertCronHasNoDedupeUntil(def, `cron "${name}"`);
35
36
  if (def.concurrency !== undefined) {
36
37
  throw new Error(`[bunderstack] cron "${name}": concurrency is not supported for cron tasks — slots are already unique`);
37
38
  }
@@ -44,6 +45,17 @@ export function validateBackgroundDefs(defs) {
44
45
  (def.concurrency < 1 || !Number.isInteger(def.concurrency))) {
45
46
  throw new Error(`[bunderstack] job "${name}": concurrency must be a positive integer`);
46
47
  }
48
+ if (def.dedupeUntil !== undefined &&
49
+ def.dedupeUntil !== 'start' &&
50
+ def.dedupeUntil !== 'finish') {
51
+ throw new Error(`[bunderstack] job "${name}": dedupeUntil must be 'start' or 'finish'`);
52
+ }
53
+ }
54
+ }
55
+ /** Cron slot ownership depends on the retained dedupe key. */
56
+ function assertCronHasNoDedupeUntil(def, label) {
57
+ if (def.dedupeUntil !== undefined) {
58
+ throw new Error(`[bunderstack] ${label}: dedupeUntil is not supported for cron tasks — cron tasks retain their slot key`);
47
59
  }
48
60
  }
49
61
  /** @deprecated Use validateBackgroundDefs. */
@@ -74,6 +86,7 @@ export function createJobsBuilder() {
74
86
  },
75
87
  cron(def) {
76
88
  parseCron(def.schedule);
89
+ assertCronHasNoDedupeUntil(def, 'cron');
77
90
  return { kind: 'cron', ...def };
78
91
  },
79
92
  /** Identity with validation: returns the defs map, typed. */
@@ -1 +1 @@
1
- {"version":3,"file":"define.js","sourceRoot":"","sources":["../../src/jobs/define.ts"],"names":[],"mappings":"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","sourcesContent":["// src/jobs/define.ts — job definition types and the typed builder.\n// `createJobsBuilder` exists purely to carry\n// TSchema/TEnvResult typing into inline callbacks and extracted files.\nimport type { StandardSchemaV1 } from '@standard-schema/spec'\n\nimport type { DbFor } from '../db'\nimport type { MessagingConfig, MessagingFacadesFor } from '../messaging'\nimport type { StorageFacade } from '../runtime'\n\nimport { parseCron } from './cron'\nimport { CRON_PREFIX, type CatchUp } from './slots'\n\nexport const DEFAULT_RETRIES = 3\nexport const DEFAULT_LEASE_DURATION_MS = 60_000\n/** @deprecated Use DEFAULT_LEASE_DURATION_MS. */\nexport const DEFAULT_TIMEOUT_MS = DEFAULT_LEASE_DURATION_MS\n\nexport type BackgroundTiming = {\n /** Lease duration in ms; an expired lease sends the job back to pending. */\n leaseDuration?: number\n /** Optional cooperative execution deadline in ms. */\n maxRuntime?: number\n /** @deprecated Use leaseDuration. */\n timeout?: number\n}\n\nexport function leaseDurationFor<T extends BackgroundTiming>(def: T): number {\n return def.leaseDuration ?? def.timeout ?? DEFAULT_LEASE_DURATION_MS\n}\n\nexport type EnqueueOptions = {\n /** Collapse duplicate enqueues while the queue row is non-terminal. */\n dedupeKey?: string\n /** Milliseconds from now until the job becomes claimable. */\n delay?: number\n /** Absolute time the job becomes claimable; wins over `delay`. */\n runAt?: Date | number\n}\n\nexport type TickResult = {\n /** Rows moved from pending to running this tick. */\n claimed: number\n /** Handlers that completed successfully. */\n ran: number\n /** Handlers that threw, whether or not they will be retried. */\n failed: number\n}\n\n/**\n * The untyped runtime facade. Job handlers and API context expose this shape;\n * `app.jobs` narrows `enqueue` to the declared job names/payloads.\n */\nexport type JobsRuntimeFacade = {\n enqueue(\n name: string,\n input?: unknown,\n opts?: EnqueueOptions,\n ): Promise<{ id: string }>\n /** Run one poll cycle deterministically (tests). `now` defaults to Date.now(). */\n tick(now?: number): Promise<TickResult>\n}\n\nimport type { RealtimeFacade } from '../realtime/facade'\n\nexport type JobContext<\n TSchema extends Record<string, unknown> = Record<string, unknown>,\n TEnvResult = Record<string, unknown>,\n TMessaging extends MessagingConfig | undefined = MessagingConfig,\n> = {\n db: DbFor<TSchema>\n env: TEnvResult\n messaging: MessagingFacadesFor<TMessaging>\n storage: StorageFacade\n jobs: JobsRuntimeFacade\n realtime: RealtimeFacade<TSchema>\n /** Aborted when execution reaches its deadline or the job loses its lease. */\n signal: AbortSignal\n}\n\nexport type BunderstackJobContext<\n TSchema extends Record<string, unknown> = Record<string, unknown>,\n TEnvResult = Record<string, unknown>,\n TMessaging extends MessagingConfig | undefined = MessagingConfig,\n> = JobContext<TSchema, TEnvResult, TMessaging>\n\nexport type QueueJobDefinition<\n TInput,\n TSchema extends Record<string, unknown> = Record<string, unknown>,\n TEnvResult = Record<string, unknown>,\n TMessaging extends MessagingConfig | undefined = MessagingConfig,\n> = BackgroundTiming & {\n kind: 'job'\n /** Standard Schema payload; parsed at enqueue AND before the handler runs. */\n input?: StandardSchemaV1<unknown, TInput>\n /** Attempts after the first failure. Default 3 (so 4 total attempts). */\n retries?: number\n /** Delay before retry N (1-based). Default exponential: 1s, 2s, 4s, … */\n backoff?: ((attempt: number) => number) | { baseMs?: number; factor?: number }\n /** Max simultaneous `running` rows of this type, enforced per worker. */\n concurrency?: number\n handler: (\n input: TInput,\n ctx: JobContext<TSchema, TEnvResult, TMessaging>,\n ) => Promise<void> | void\n /** Fires once, after the final attempt fails. Errors here are logged, never retried. */\n onFailed?: (\n input: TInput,\n error: Error,\n ctx: JobContext<TSchema, TEnvResult, TMessaging>,\n ) => Promise<void> | void\n}\n\nexport type CronInvocation = { scheduledFor: Date }\n\nexport type CronDefinition<\n TSchema extends Record<string, unknown> = Record<string, unknown>,\n TEnvResult = Record<string, unknown>,\n TSchedule extends string = string,\n TMessaging extends MessagingConfig | undefined = MessagingConfig,\n> = BackgroundTiming & {\n kind: 'cron'\n schedule: TSchedule\n /** Attempts after the first failure. Default 3 (so 4 total attempts). */\n retries?: number\n /** Delay before retry N (1-based). Default exponential: 1s, 2s, 4s, … */\n backoff?: ((attempt: number) => number) | { baseMs?: number; factor?: number }\n /** How missed slots are handled on wake. Default 'latest'. */\n catchUp?: CatchUp\n /** How far back catch-up looks, in ms. Default 1 hour. */\n catchUpWindow?: number\n handler: (\n invocation: CronInvocation,\n ctx: JobContext<TSchema, TEnvResult, TMessaging>,\n ) => Promise<void> | void\n /** Fires once, after the final attempt fails. Errors here are logged, never retried. */\n onFailed?: (\n invocation: CronInvocation,\n error: Error,\n ctx: JobContext<TSchema, TEnvResult, TMessaging>,\n ) => Promise<void> | void\n}\n\nexport type BackgroundDefinition =\n | QueueJobDefinition<any, any, any, any>\n | CronDefinition<any, any, any, any>\nexport type BackgroundDefs = Record<string, BackgroundDefinition>\n\n/** @deprecated Use QueueJobDefinition. */\nexport type JobDefinition<\n TInput,\n TSchema extends Record<string, unknown> = Record<string, unknown>,\n TEnvResult = Record<string, unknown>,\n TMessaging extends MessagingConfig | undefined = MessagingConfig,\n> = QueueJobDefinition<TInput, TSchema, TEnvResult, TMessaging>\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type AnyJobDefinition = QueueJobDefinition<any, any, any, any>\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type AnyBackgroundDefinition =\n | QueueJobDefinition<any, any, any, any>\n | CronDefinition<any, any, any, any>\nexport type JobsDefs = BackgroundDefs\n\nexport type QueueJobKeys<TDefs extends BackgroundDefs> = {\n [K in keyof TDefs & string]: TDefs[K] extends QueueJobDefinition<\n any,\n any,\n any,\n any\n >\n ? K\n : never\n}[keyof TDefs & string]\n\n/** Throws when a definition is unusable. Safe to call more than once. */\nexport function validateBackgroundDefs(defs: BackgroundDefs): void {\n for (const [name, def] of Object.entries(defs)) {\n if (typeof def.handler !== 'function') {\n throw new Error(`[bunderstack] background task \"${name}\" has no handler`)\n }\n if (def.kind === 'job' && name.startsWith(CRON_PREFIX)) {\n throw new Error(\n `[bunderstack] job \"${name}\": the \"${CRON_PREFIX}\" prefix is reserved for cron tasks`,\n )\n }\n if (\n def.retries !== undefined &&\n (def.retries < 0 || !Number.isInteger(def.retries))\n ) {\n throw new Error(\n `[bunderstack] background task \"${name}\": retries must be a non-negative integer`,\n )\n }\n if (def.timeout !== undefined && def.leaseDuration !== undefined) {\n throw new Error(\n `[bunderstack] background task \"${name}\": timeout and leaseDuration cannot both be declared`,\n )\n }\n for (const field of ['timeout', 'leaseDuration', 'maxRuntime'] as const) {\n const value = def[field]\n if (\n value !== undefined &&\n (!Number.isFinite(value) || !Number.isInteger(value) || value <= 0)\n ) {\n throw new Error(\n `[bunderstack] background task \"${name}\": ${field} must be a positive finite integer`,\n )\n }\n }\n if (def.kind === 'cron') {\n parseCron(def.schedule)\n if ((def as { concurrency?: number }).concurrency !== undefined) {\n throw new Error(\n `[bunderstack] cron \"${name}\": concurrency is not supported for cron tasks — slots are already unique`,\n )\n }\n if (def.catchUpWindow !== undefined && def.catchUpWindow <= 0) {\n throw new Error(\n `[bunderstack] cron \"${name}\": catchUpWindow must be positive`,\n )\n }\n continue\n }\n if (\n def.concurrency !== undefined &&\n (def.concurrency < 1 || !Number.isInteger(def.concurrency))\n ) {\n throw new Error(\n `[bunderstack] job \"${name}\": concurrency must be a positive integer`,\n )\n }\n }\n}\n\n/** @deprecated Use validateBackgroundDefs. */\nexport const validateJobsDefs = validateBackgroundDefs\n\n/**\n * Delay in ms before retry `attempt` (1-based = the attempt that just failed).\n * Jittered by ±20% so a shared outage does not retry every job in lockstep.\n * A caller-supplied backoff function is returned verbatim — the caller owns it.\n */\nexport function backoffMs(\n def: AnyBackgroundDefinition,\n attempt: number,\n): number {\n const b = def.backoff\n if (typeof b === 'function') return b(attempt)\n const baseMs = b?.baseMs ?? 1000\n const factor = b?.factor ?? 2\n const flat = baseMs * factor ** (attempt - 1)\n return Math.round(flat * (0.8 + Math.random() * 0.4))\n}\n\n/**\n * Build the `j` instance bunderstack hands to the config's `jobs` builder\n * callback (and exports for multi-file job setups).\n */\nexport function createJobsBuilder<\n TSchema extends Record<string, unknown>,\n TEnvResult = Record<string, unknown>,\n TMessaging extends MessagingConfig | undefined = MessagingConfig,\n>() {\n return {\n /** Identity with inference: pins TInput from the schema output. */\n job<TInput = undefined>(\n def: Omit<\n QueueJobDefinition<TInput, TSchema, TEnvResult, TMessaging>,\n 'kind'\n >,\n ): QueueJobDefinition<TInput, TSchema, TEnvResult, TMessaging> {\n return { kind: 'job', ...def }\n },\n cron<const TSchedule extends string>(\n def: Omit<\n CronDefinition<TSchema, TEnvResult, TSchedule, TMessaging>,\n 'kind'\n >,\n ): CronDefinition<TSchema, TEnvResult, TSchedule, TMessaging> {\n parseCron(def.schedule)\n return { kind: 'cron', ...def }\n },\n /** Identity with validation: returns the defs map, typed. */\n define<TDefs extends BackgroundDefs>(defs: TDefs): TDefs {\n validateBackgroundDefs(defs)\n return defs\n },\n }\n}\n\n/** Type of the `j` instance — for builder callbacks declared in separate files. */\nexport type BunderstackJobsBuilder<\n TSchema extends Record<string, unknown>,\n TEnvResult = Record<string, unknown>,\n TMessaging extends MessagingConfig | undefined = MessagingConfig,\n> = ReturnType<typeof createJobsBuilder<TSchema, TEnvResult, TMessaging>>\n\n// Infers TInput from the JobDefinition's own type argument rather than\n// pattern-matching the (optional, so union-with-undefined) `input` property —\n// A required-property pattern fails structurally because `input` is optional,\n// so infer from the definition's own type argument instead.\ntype JobInputOf<TDef> =\n TDef extends QueueJobDefinition<infer TInput, any, any, any>\n ? TInput\n : undefined\n\n/**\n * `app.jobs`: `enqueue` narrowed to declared names + payloads. `Omit`s the\n * runtime facade's loose `enqueue` first — intersecting two same-named\n * methods instead would make TS treat them as overloaded, so the loose\n * `(name: string, ...)` signature would still accept any name.\n */\nexport type JobsFacade<TDefs extends JobsDefs> = Omit<\n JobsRuntimeFacade,\n 'enqueue'\n> & {\n enqueue<K extends QueueJobKeys<TDefs>>(\n name: K,\n ...rest: JobInputOf<TDefs[K]> extends undefined\n ? [input?: undefined, opts?: EnqueueOptions]\n : [input: JobInputOf<TDefs[K]>, opts?: EnqueueOptions]\n ): Promise<{ id: string }>\n}\n"]}
1
+ {"version":3,"file":"define.js","sourceRoot":"","sources":["../../src/jobs/define.ts"],"names":[],"mappings":"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","sourcesContent":["// src/jobs/define.ts — job definition types and the typed builder.\n// `createJobsBuilder` exists purely to carry\n// TSchema/TEnvResult typing into inline callbacks and extracted files.\nimport type { StandardSchemaV1 } from '@standard-schema/spec'\n\nimport type { BunderstackTx, DbFor } from '../db'\nimport type { AnyDb } from '../dialect'\nimport type { MessagingConfig, MessagingFacadesFor } from '../messaging'\nimport type { StorageFacade } from '../runtime'\n\nimport { parseCron } from './cron'\nimport { CRON_PREFIX, type CatchUp } from './slots'\n\nexport const DEFAULT_RETRIES = 3\nexport const DEFAULT_LEASE_DURATION_MS = 60_000\n/** @deprecated Use DEFAULT_LEASE_DURATION_MS. */\nexport const DEFAULT_TIMEOUT_MS = DEFAULT_LEASE_DURATION_MS\n\nexport type BackgroundTiming = {\n /** Lease duration in ms; an expired lease sends the job back to pending. */\n leaseDuration?: number\n /** Optional cooperative execution deadline in ms. */\n maxRuntime?: number\n /** @deprecated Use leaseDuration. */\n timeout?: number\n}\n\nexport function leaseDurationFor<T extends BackgroundTiming>(def: T): number {\n return def.leaseDuration ?? def.timeout ?? DEFAULT_LEASE_DURATION_MS\n}\n\n/**\n * Structural view of a Drizzle database or transaction handle that `enqueue`\n * can insert through. The loose runtime facade accepts any handle of the app's\n * dialect; `app.jobs` narrows it to the app's `BunderstackTx`.\n */\nexport type EnqueueTransaction = AnyDb\n\nexport type EnqueueOptions = {\n /**\n * Collapse duplicate enqueues while the queue row holds its key: until a\n * terminal state by default, or until it is claimed with\n * `dedupeUntil: 'start'`.\n */\n dedupeKey?: string\n /** Milliseconds from now until the job becomes claimable. */\n delay?: number\n /** Absolute time the job becomes claimable; wins over `delay`. */\n runAt?: Date | number\n /**\n * Insert the job row through this transaction. The job becomes visible to\n * workers only when the transaction commits; a rollback removes it. Must be\n * a transaction on the app's own database.\n */\n tx?: EnqueueTransaction\n}\n\n/** `EnqueueOptions` with `tx` narrowed to the app schema's transaction type. */\nexport type TypedEnqueueOptions<TSchema extends Record<string, unknown>> = Omit<\n EnqueueOptions,\n 'tx'\n> & {\n /**\n * Insert the job row through this transaction. The job becomes visible to\n * workers only when the transaction commits; a rollback removes it.\n */\n tx?: BunderstackTx<TSchema>\n}\n\n/**\n * How long a queue job's `dedupeKey` collapses new enqueues.\n * - `'finish'` (default): until the job reaches a terminal state.\n * - `'start'`: until a worker claims the job; later enqueues create a new row\n * that runs after it and reads the newer state.\n */\nexport type DedupeUntil = 'start' | 'finish'\n\nexport type TickResult = {\n /** Rows moved from pending to running this tick. */\n claimed: number\n /** Handlers that completed successfully. */\n ran: number\n /** Handlers that threw, whether or not they will be retried. */\n failed: number\n}\n\n/**\n * The untyped runtime facade. Job handlers and API context expose this shape;\n * `app.jobs` narrows `enqueue` to the declared job names/payloads.\n */\nexport type JobsRuntimeFacade = {\n enqueue(\n name: string,\n input?: unknown,\n opts?: EnqueueOptions,\n ): Promise<{ id: string }>\n /** Run one poll cycle deterministically (tests). `now` defaults to Date.now(). */\n tick(now?: number): Promise<TickResult>\n}\n\nimport type { RealtimeFacade } from '../realtime/facade'\n\nexport type JobContext<\n TSchema extends Record<string, unknown> = Record<string, unknown>,\n TEnvResult = Record<string, unknown>,\n TMessaging extends MessagingConfig | undefined = MessagingConfig,\n> = {\n db: DbFor<TSchema>\n env: TEnvResult\n messaging: MessagingFacadesFor<TMessaging>\n storage: StorageFacade\n jobs: JobsRuntimeFacade\n realtime: RealtimeFacade<TSchema>\n /** Aborted when execution reaches its deadline or the job loses its lease. */\n signal: AbortSignal\n}\n\nexport type BunderstackJobContext<\n TSchema extends Record<string, unknown> = Record<string, unknown>,\n TEnvResult = Record<string, unknown>,\n TMessaging extends MessagingConfig | undefined = MessagingConfig,\n> = JobContext<TSchema, TEnvResult, TMessaging>\n\nexport type QueueJobDefinition<\n TInput,\n TSchema extends Record<string, unknown> = Record<string, unknown>,\n TEnvResult = Record<string, unknown>,\n TMessaging extends MessagingConfig | undefined = MessagingConfig,\n> = BackgroundTiming & {\n kind: 'job'\n /** Standard Schema payload; parsed at enqueue AND before the handler runs. */\n input?: StandardSchemaV1<unknown, TInput>\n /** Attempts after the first failure. Default 3 (so 4 total attempts). */\n retries?: number\n /** Delay before retry N (1-based). Default exponential: 1s, 2s, 4s, … */\n backoff?: ((attempt: number) => number) | { baseMs?: number; factor?: number }\n /** Max simultaneous `running` rows of this type, enforced per worker. */\n concurrency?: number\n /**\n * When a `dedupeKey` stops collapsing new enqueues. Default `'finish'`:\n * held until a terminal state. `'start'`: released when a worker claims the\n * job, so an enqueue during the run schedules one more run.\n */\n dedupeUntil?: DedupeUntil\n handler: (\n input: TInput,\n ctx: JobContext<TSchema, TEnvResult, TMessaging>,\n ) => Promise<void> | void\n /** Fires once, after the final attempt fails. Errors here are logged, never retried. */\n onFailed?: (\n input: TInput,\n error: Error,\n ctx: JobContext<TSchema, TEnvResult, TMessaging>,\n ) => Promise<void> | void\n}\n\nexport type CronInvocation = { scheduledFor: Date }\n\nexport type CronDefinition<\n TSchema extends Record<string, unknown> = Record<string, unknown>,\n TEnvResult = Record<string, unknown>,\n TSchedule extends string = string,\n TMessaging extends MessagingConfig | undefined = MessagingConfig,\n> = BackgroundTiming & {\n kind: 'cron'\n schedule: TSchedule\n /** Attempts after the first failure. Default 3 (so 4 total attempts). */\n retries?: number\n /** Delay before retry N (1-based). Default exponential: 1s, 2s, 4s, … */\n backoff?: ((attempt: number) => number) | { baseMs?: number; factor?: number }\n /** How missed slots are handled on wake. Default 'latest'. */\n catchUp?: CatchUp\n /** How far back catch-up looks, in ms. Default 1 hour. */\n catchUpWindow?: number\n handler: (\n invocation: CronInvocation,\n ctx: JobContext<TSchema, TEnvResult, TMessaging>,\n ) => Promise<void> | void\n /** Fires once, after the final attempt fails. Errors here are logged, never retried. */\n onFailed?: (\n invocation: CronInvocation,\n error: Error,\n ctx: JobContext<TSchema, TEnvResult, TMessaging>,\n ) => Promise<void> | void\n}\n\nexport type BackgroundDefinition =\n | QueueJobDefinition<any, any, any, any>\n | CronDefinition<any, any, any, any>\nexport type BackgroundDefs = Record<string, BackgroundDefinition>\n\n/** @deprecated Use QueueJobDefinition. */\nexport type JobDefinition<\n TInput,\n TSchema extends Record<string, unknown> = Record<string, unknown>,\n TEnvResult = Record<string, unknown>,\n TMessaging extends MessagingConfig | undefined = MessagingConfig,\n> = QueueJobDefinition<TInput, TSchema, TEnvResult, TMessaging>\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type AnyJobDefinition = QueueJobDefinition<any, any, any, any>\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type AnyBackgroundDefinition =\n | QueueJobDefinition<any, any, any, any>\n | CronDefinition<any, any, any, any>\nexport type JobsDefs = BackgroundDefs\n\nexport type QueueJobKeys<TDefs extends BackgroundDefs> = {\n [K in keyof TDefs & string]: TDefs[K] extends QueueJobDefinition<\n any,\n any,\n any,\n any\n >\n ? K\n : never\n}[keyof TDefs & string]\n\n/** Throws when a definition is unusable. Safe to call more than once. */\nexport function validateBackgroundDefs(defs: BackgroundDefs): void {\n for (const [name, def] of Object.entries(defs)) {\n if (typeof def.handler !== 'function') {\n throw new Error(`[bunderstack] background task \"${name}\" has no handler`)\n }\n if (def.kind === 'job' && name.startsWith(CRON_PREFIX)) {\n throw new Error(\n `[bunderstack] job \"${name}\": the \"${CRON_PREFIX}\" prefix is reserved for cron tasks`,\n )\n }\n if (\n def.retries !== undefined &&\n (def.retries < 0 || !Number.isInteger(def.retries))\n ) {\n throw new Error(\n `[bunderstack] background task \"${name}\": retries must be a non-negative integer`,\n )\n }\n if (def.timeout !== undefined && def.leaseDuration !== undefined) {\n throw new Error(\n `[bunderstack] background task \"${name}\": timeout and leaseDuration cannot both be declared`,\n )\n }\n for (const field of ['timeout', 'leaseDuration', 'maxRuntime'] as const) {\n const value = def[field]\n if (\n value !== undefined &&\n (!Number.isFinite(value) || !Number.isInteger(value) || value <= 0)\n ) {\n throw new Error(\n `[bunderstack] background task \"${name}\": ${field} must be a positive finite integer`,\n )\n }\n }\n if (def.kind === 'cron') {\n parseCron(def.schedule)\n assertCronHasNoDedupeUntil(def, `cron \"${name}\"`)\n if ((def as { concurrency?: number }).concurrency !== undefined) {\n throw new Error(\n `[bunderstack] cron \"${name}\": concurrency is not supported for cron tasks — slots are already unique`,\n )\n }\n if (def.catchUpWindow !== undefined && def.catchUpWindow <= 0) {\n throw new Error(\n `[bunderstack] cron \"${name}\": catchUpWindow must be positive`,\n )\n }\n continue\n }\n if (\n def.concurrency !== undefined &&\n (def.concurrency < 1 || !Number.isInteger(def.concurrency))\n ) {\n throw new Error(\n `[bunderstack] job \"${name}\": concurrency must be a positive integer`,\n )\n }\n if (\n def.dedupeUntil !== undefined &&\n def.dedupeUntil !== 'start' &&\n def.dedupeUntil !== 'finish'\n ) {\n throw new Error(\n `[bunderstack] job \"${name}\": dedupeUntil must be 'start' or 'finish'`,\n )\n }\n }\n}\n\n/** Cron slot ownership depends on the retained dedupe key. */\nfunction assertCronHasNoDedupeUntil(def: object, label: string): void {\n if ((def as { dedupeUntil?: unknown }).dedupeUntil !== undefined) {\n throw new Error(\n `[bunderstack] ${label}: dedupeUntil is not supported for cron tasks — cron tasks retain their slot key`,\n )\n }\n}\n\n/** @deprecated Use validateBackgroundDefs. */\nexport const validateJobsDefs = validateBackgroundDefs\n\n/**\n * Delay in ms before retry `attempt` (1-based = the attempt that just failed).\n * Jittered by ±20% so a shared outage does not retry every job in lockstep.\n * A caller-supplied backoff function is returned verbatim — the caller owns it.\n */\nexport function backoffMs(\n def: AnyBackgroundDefinition,\n attempt: number,\n): number {\n const b = def.backoff\n if (typeof b === 'function') return b(attempt)\n const baseMs = b?.baseMs ?? 1000\n const factor = b?.factor ?? 2\n const flat = baseMs * factor ** (attempt - 1)\n return Math.round(flat * (0.8 + Math.random() * 0.4))\n}\n\n/**\n * Build the `j` instance bunderstack hands to the config's `jobs` builder\n * callback (and exports for multi-file job setups).\n */\nexport function createJobsBuilder<\n TSchema extends Record<string, unknown>,\n TEnvResult = Record<string, unknown>,\n TMessaging extends MessagingConfig | undefined = MessagingConfig,\n>() {\n return {\n /** Identity with inference: pins TInput from the schema output. */\n job<TInput = undefined>(\n def: Omit<\n QueueJobDefinition<TInput, TSchema, TEnvResult, TMessaging>,\n 'kind'\n >,\n ): QueueJobDefinition<TInput, TSchema, TEnvResult, TMessaging> {\n return { kind: 'job', ...def }\n },\n cron<const TSchedule extends string>(\n def: Omit<\n CronDefinition<TSchema, TEnvResult, TSchedule, TMessaging>,\n 'kind'\n >,\n ): CronDefinition<TSchema, TEnvResult, TSchedule, TMessaging> {\n parseCron(def.schedule)\n assertCronHasNoDedupeUntil(def, 'cron')\n return { kind: 'cron', ...def }\n },\n /** Identity with validation: returns the defs map, typed. */\n define<TDefs extends BackgroundDefs>(defs: TDefs): TDefs {\n validateBackgroundDefs(defs)\n return defs\n },\n }\n}\n\n/** Type of the `j` instance — for builder callbacks declared in separate files. */\nexport type BunderstackJobsBuilder<\n TSchema extends Record<string, unknown>,\n TEnvResult = Record<string, unknown>,\n TMessaging extends MessagingConfig | undefined = MessagingConfig,\n> = ReturnType<typeof createJobsBuilder<TSchema, TEnvResult, TMessaging>>\n\n// Infers TInput from the JobDefinition's own type argument rather than\n// pattern-matching the (optional, so union-with-undefined) `input` property —\n// A required-property pattern fails structurally because `input` is optional,\n// so infer from the definition's own type argument instead.\ntype JobInputOf<TDef> =\n TDef extends QueueJobDefinition<infer TInput, any, any, any>\n ? TInput\n : undefined\n\n/**\n * `app.jobs`: `enqueue` narrowed to declared names + payloads. `Omit`s the\n * runtime facade's loose `enqueue` first — intersecting two same-named\n * methods instead would make TS treat them as overloaded, so the loose\n * `(name: string, ...)` signature would still accept any name.\n */\nexport type JobsFacade<\n TDefs extends JobsDefs,\n TSchema extends Record<string, unknown> | undefined = undefined,\n> = Omit<JobsRuntimeFacade, 'enqueue'> & {\n enqueue<K extends QueueJobKeys<TDefs>>(\n name: K,\n ...rest: JobInputOf<TDefs[K]> extends undefined\n ? [input?: undefined, opts?: JobsFacadeEnqueueOptions<TSchema>]\n : [input: JobInputOf<TDefs[K]>, opts?: JobsFacadeEnqueueOptions<TSchema>]\n ): Promise<{ id: string }>\n}\n\ntype JobsFacadeEnqueueOptions<\n TSchema extends Record<string, unknown> | undefined,\n> = [TSchema] extends [Record<string, unknown>]\n ? TypedEnqueueOptions<TSchema>\n : EnqueueOptions\n"]}
@@ -1,6 +1,6 @@
1
1
  export { createJobsBuilder, validateBackgroundDefs, validateJobsDefs, DEFAULT_RETRIES, DEFAULT_LEASE_DURATION_MS, DEFAULT_TIMEOUT_MS, leaseDurationFor, } from './define.js';
2
- export type { AnyJobDefinition, BackgroundTiming, BackgroundDefinition, BackgroundDefs, BunderstackJobContext, BunderstackJobsBuilder, EnqueueOptions, JobContext, JobDefinition, QueueJobDefinition, CronDefinition, CronInvocation, QueueJobKeys, JobsDefs, JobsFacade, JobsRuntimeFacade, } from './define.js';
3
- export { enqueueJob } from './queue.js';
2
+ export type { AnyJobDefinition, BackgroundTiming, BackgroundDefinition, BackgroundDefs, BunderstackJobContext, BunderstackJobsBuilder, DedupeUntil, EnqueueOptions, EnqueueTransaction, JobContext, JobDefinition, QueueJobDefinition, CronDefinition, CronInvocation, QueueJobKeys, TypedEnqueueOptions, JobsDefs, JobsFacade, JobsRuntimeFacade, } from './define.js';
3
+ export { enqueueJob, enqueueTarget } from './queue.js';
4
4
  export { createJobRunner } from './worker.js';
5
5
  export { startJobWorker } from './runtime.js';
6
6
  export type { StartWorkerOptions, RunWorkerOptions, WorkerCycleResult, WorkerHandle, } from './runtime.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/jobs/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,gBAAgB,EAChB,eAAe,EACf,yBAAyB,EACzB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,UAAU,CAAA;AACjB,YAAY,EACV,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,EACpB,cAAc,EACd,qBAAqB,EACrB,sBAAsB,EACtB,cAAc,EACd,UAAU,EACV,aAAa,EACb,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,iBAAiB,GAClB,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACpC,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAC1C,YAAY,EACV,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,YAAY,GACb,MAAM,WAAW,CAAA;AAClB,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAA;AAC/C,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACnE,YAAY,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACtC,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/jobs/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,gBAAgB,EAChB,eAAe,EACf,yBAAyB,EACzB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,UAAU,CAAA;AACjB,YAAY,EACV,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,EACpB,cAAc,EACd,qBAAqB,EACrB,sBAAsB,EACtB,WAAW,EACX,cAAc,EACd,kBAAkB,EAClB,UAAU,EACV,aAAa,EACb,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,YAAY,EACZ,mBAAmB,EACnB,QAAQ,EACR,UAAU,EACV,iBAAiB,GAClB,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAC1C,YAAY,EACV,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,YAAY,GACb,MAAM,WAAW,CAAA;AAClB,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAA;AAC/C,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACnE,YAAY,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACtC,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA"}
@@ -1,6 +1,6 @@
1
1
  // src/jobs/index.ts — module surface consumed by bunderstack.
2
2
  export { createJobsBuilder, validateBackgroundDefs, validateJobsDefs, DEFAULT_RETRIES, DEFAULT_LEASE_DURATION_MS, DEFAULT_TIMEOUT_MS, leaseDurationFor, } from './define.js';
3
- export { enqueueJob } from './queue.js';
3
+ export { enqueueJob, enqueueTarget } from './queue.js';
4
4
  export { createJobRunner } from './worker.js';
5
5
  export { startJobWorker } from './runtime.js';
6
6
  export { parseCron, cronMatches } from './cron.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/jobs/index.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,gBAAgB,EAChB,eAAe,EACf,yBAAyB,EACzB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,UAAU,CAAA;AAmBjB,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACpC,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAO1C,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAA;AAC/C,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA","sourcesContent":["// src/jobs/index.ts — module surface consumed by bunderstack.\nexport {\n createJobsBuilder,\n validateBackgroundDefs,\n validateJobsDefs,\n DEFAULT_RETRIES,\n DEFAULT_LEASE_DURATION_MS,\n DEFAULT_TIMEOUT_MS,\n leaseDurationFor,\n} from './define'\nexport type {\n AnyJobDefinition,\n BackgroundTiming,\n BackgroundDefinition,\n BackgroundDefs,\n BunderstackJobContext,\n BunderstackJobsBuilder,\n EnqueueOptions,\n JobContext,\n JobDefinition,\n QueueJobDefinition,\n CronDefinition,\n CronInvocation,\n QueueJobKeys,\n JobsDefs,\n JobsFacade,\n JobsRuntimeFacade,\n} from './define'\nexport { enqueueJob } from './queue'\nexport { createJobRunner } from './worker'\nexport { startJobWorker } from './runtime'\nexport type {\n StartWorkerOptions,\n RunWorkerOptions,\n WorkerCycleResult,\n WorkerHandle,\n} from './runtime'\nexport { parseCron, cronMatches } from './cron'\nexport { slotsDue, floorSlot, CRON_PREFIX, SLOT_MS } from './slots'\nexport type { CatchUp } from './slots'\nexport type { TickResult } from './define'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/jobs/index.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,gBAAgB,EAChB,eAAe,EACf,yBAAyB,EACzB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,UAAU,CAAA;AAsBjB,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAO1C,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAA;AAC/C,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA","sourcesContent":["// src/jobs/index.ts — module surface consumed by bunderstack.\nexport {\n createJobsBuilder,\n validateBackgroundDefs,\n validateJobsDefs,\n DEFAULT_RETRIES,\n DEFAULT_LEASE_DURATION_MS,\n DEFAULT_TIMEOUT_MS,\n leaseDurationFor,\n} from './define'\nexport type {\n AnyJobDefinition,\n BackgroundTiming,\n BackgroundDefinition,\n BackgroundDefs,\n BunderstackJobContext,\n BunderstackJobsBuilder,\n DedupeUntil,\n EnqueueOptions,\n EnqueueTransaction,\n JobContext,\n JobDefinition,\n QueueJobDefinition,\n CronDefinition,\n CronInvocation,\n QueueJobKeys,\n TypedEnqueueOptions,\n JobsDefs,\n JobsFacade,\n JobsRuntimeFacade,\n} from './define'\nexport { enqueueJob, enqueueTarget } from './queue'\nexport { createJobRunner } from './worker'\nexport { startJobWorker } from './runtime'\nexport type {\n StartWorkerOptions,\n RunWorkerOptions,\n WorkerCycleResult,\n WorkerHandle,\n} from './runtime'\nexport { parseCron, cronMatches } from './cron'\nexport { slotsDue, floorSlot, CRON_PREFIX, SLOT_MS } from './slots'\nexport type { CatchUp } from './slots'\nexport type { TickResult } from './define'\n"]}
@@ -1,5 +1,13 @@
1
1
  import type { AnyDb } from '../dialect.js';
2
2
  import type { EnqueueOptions, JobsDefs } from './define.js';
3
+ /**
4
+ * The handle an enqueue inserts through: the caller's transaction when given,
5
+ * otherwise the app database. Drizzle transactions extend their dialect's
6
+ * database class, so the internal jobs table resolves the same way for both.
7
+ * A transaction from an unrelated connection of the same dialect cannot be
8
+ * detected cheaply and is unsupported.
9
+ */
10
+ export declare function enqueueTarget(db: AnyDb, tx: AnyDb | undefined): AnyDb;
3
11
  export declare function enqueueJob(db: AnyDb, defs: JobsDefs, name: string, input: unknown, opts?: EnqueueOptions, now?: number): Promise<{
4
12
  id: string;
5
13
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../../src/jobs/queue.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAOxD,wBAAsB,UAAU,CAC9B,EAAE,EAAE,KAAK,EACT,IAAI,EAAE,QAAQ,EACd,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,OAAO,EACd,IAAI,GAAE,cAAmB,EACzB,GAAG,GAAE,MAAmB,GACvB,OAAO,CAAC;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,CAgDzB"}
1
+ {"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../../src/jobs/queue.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,KAAK,EAAW,MAAM,YAAY,CAAA;AAChD,OAAO,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAaxD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,GAAG,SAAS,GAAG,KAAK,CAQrE;AAED,wBAAsB,UAAU,CAC9B,EAAE,EAAE,KAAK,EACT,IAAI,EAAE,QAAQ,EACd,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,OAAO,EACd,IAAI,GAAE,cAAmB,EACzB,GAAG,GAAE,MAAmB,GACvB,OAAO,CAAC;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,CAgDzB"}
@@ -1,9 +1,33 @@
1
1
  // src/jobs/queue.ts — durable enqueue with constraint-backed dedupe.
2
- import { and, eq } from 'drizzle-orm';
2
+ import { and, eq, is } from 'drizzle-orm';
3
+ import { PgDatabase } from 'drizzle-orm/pg-core';
4
+ import { BaseSQLiteDatabase } from 'drizzle-orm/sqlite-core';
3
5
  import { jobsTableFor } from '../internal-tables.js';
4
6
  import { validateStandardSchema } from '../standard-schema.js';
5
7
  import { generate } from '../typeid.js';
6
8
  import { CRON_PREFIX } from './slots.js';
9
+ function handleDialect(handle) {
10
+ if (is(handle, PgDatabase))
11
+ return 'pg';
12
+ if (is(handle, BaseSQLiteDatabase))
13
+ return 'sqlite';
14
+ return undefined;
15
+ }
16
+ /**
17
+ * The handle an enqueue inserts through: the caller's transaction when given,
18
+ * otherwise the app database. Drizzle transactions extend their dialect's
19
+ * database class, so the internal jobs table resolves the same way for both.
20
+ * A transaction from an unrelated connection of the same dialect cannot be
21
+ * detected cheaply and is unsupported.
22
+ */
23
+ export function enqueueTarget(db, tx) {
24
+ if (tx === undefined)
25
+ return db;
26
+ if (handleDialect(tx) !== handleDialect(db)) {
27
+ throw new Error('[bunderstack] enqueue tx belongs to a different database dialect');
28
+ }
29
+ return tx;
30
+ }
7
31
  export async function enqueueJob(db, defs, name, input, opts = {}, now = Date.now()) {
8
32
  const def = defs[name];
9
33
  if (!def) {
@@ -1 +1 @@
1
- {"version":3,"file":"queue.js","sourceRoot":"","sources":["../../src/jobs/queue.ts"],"names":[],"mappings":"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","sourcesContent":["// src/jobs/queue.ts — durable enqueue with constraint-backed dedupe.\nimport { and, eq } from 'drizzle-orm'\n\nimport type { AnyDb } from '../dialect'\nimport type { EnqueueOptions, JobsDefs } from './define'\n\nimport { jobsTableFor } from '../internal-tables'\nimport { validateStandardSchema } from '../standard-schema'\nimport { generate } from '../typeid'\nimport { CRON_PREFIX } from './slots'\n\nexport async function enqueueJob(\n db: AnyDb,\n defs: JobsDefs,\n name: string,\n input: unknown,\n opts: EnqueueOptions = {},\n now: number = Date.now(),\n): Promise<{ id: string }> {\n const def = defs[name]\n if (!def) {\n throw new Error(`[bunderstack] unknown background task \"${name}\"`)\n }\n const isCron = def.kind === 'cron'\n const type = isCron ? `${CRON_PREFIX}${name}` : name\n // Cron slots carry no payload; queue jobs validate theirs at the call site.\n const parsed = isCron\n ? null\n : def.input\n ? validateStandardSchema(def.input, input, `job \"${name}\" input`)\n : null\n const t = jobsTableFor(db)\n const runAt =\n opts.runAt !== undefined\n ? new Date(opts.runAt).getTime()\n : now + (opts.delay ?? 0)\n\n // Two rounds cover the race where the deduping row reaches a terminal state\n // (clearing its key) between our failed insert and our read.\n for (let round = 0; round < 2; round++) {\n const id = generate('job')\n const insertedRows = await db\n .insert(t)\n .values({\n id,\n type,\n payloadJson: JSON.stringify(parsed),\n status: 'pending',\n attempts: 0,\n runAt,\n dedupeKey: opts.dedupeKey ?? null,\n createdAt: now,\n })\n .onConflictDoNothing({ target: [t.type, t.dedupeKey] })\n .returning({ id: t.id })\n if (insertedRows[0]) return { id: String(insertedRows[0].id) }\n const existing = await db\n .select({ id: t.id })\n .from(t)\n .where(and(eq(t.type, type), eq(t.dedupeKey, opts.dedupeKey ?? '')))\n .limit(1)\n if (existing[0]) return { id: String(existing[0].id) }\n }\n throw new Error(\n `[bunderstack] enqueue of \"${name}\" lost a dedupe race twice — please retry`,\n )\n}\n"]}
1
+ {"version":3,"file":"queue.js","sourceRoot":"","sources":["../../src/jobs/queue.ts"],"names":[],"mappings":"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","sourcesContent":["// src/jobs/queue.ts — durable enqueue with constraint-backed dedupe.\nimport { and, eq, is } from 'drizzle-orm'\nimport { PgDatabase } from 'drizzle-orm/pg-core'\nimport { BaseSQLiteDatabase } from 'drizzle-orm/sqlite-core'\n\nimport type { AnyDb, Dialect } from '../dialect'\nimport type { EnqueueOptions, JobsDefs } from './define'\n\nimport { jobsTableFor } from '../internal-tables'\nimport { validateStandardSchema } from '../standard-schema'\nimport { generate } from '../typeid'\nimport { CRON_PREFIX } from './slots'\n\nfunction handleDialect(handle: unknown): Dialect | undefined {\n if (is(handle, PgDatabase)) return 'pg'\n if (is(handle, BaseSQLiteDatabase)) return 'sqlite'\n return undefined\n}\n\n/**\n * The handle an enqueue inserts through: the caller's transaction when given,\n * otherwise the app database. Drizzle transactions extend their dialect's\n * database class, so the internal jobs table resolves the same way for both.\n * A transaction from an unrelated connection of the same dialect cannot be\n * detected cheaply and is unsupported.\n */\nexport function enqueueTarget(db: AnyDb, tx: AnyDb | undefined): AnyDb {\n if (tx === undefined) return db\n if (handleDialect(tx) !== handleDialect(db)) {\n throw new Error(\n '[bunderstack] enqueue tx belongs to a different database dialect',\n )\n }\n return tx\n}\n\nexport async function enqueueJob(\n db: AnyDb,\n defs: JobsDefs,\n name: string,\n input: unknown,\n opts: EnqueueOptions = {},\n now: number = Date.now(),\n): Promise<{ id: string }> {\n const def = defs[name]\n if (!def) {\n throw new Error(`[bunderstack] unknown background task \"${name}\"`)\n }\n const isCron = def.kind === 'cron'\n const type = isCron ? `${CRON_PREFIX}${name}` : name\n // Cron slots carry no payload; queue jobs validate theirs at the call site.\n const parsed = isCron\n ? null\n : def.input\n ? validateStandardSchema(def.input, input, `job \"${name}\" input`)\n : null\n const t = jobsTableFor(db)\n const runAt =\n opts.runAt !== undefined\n ? new Date(opts.runAt).getTime()\n : now + (opts.delay ?? 0)\n\n // Two rounds cover the race where the deduping row reaches a terminal state\n // (clearing its key) between our failed insert and our read.\n for (let round = 0; round < 2; round++) {\n const id = generate('job')\n const insertedRows = await db\n .insert(t)\n .values({\n id,\n type,\n payloadJson: JSON.stringify(parsed),\n status: 'pending',\n attempts: 0,\n runAt,\n dedupeKey: opts.dedupeKey ?? null,\n createdAt: now,\n })\n .onConflictDoNothing({ target: [t.type, t.dedupeKey] })\n .returning({ id: t.id })\n if (insertedRows[0]) return { id: String(insertedRows[0].id) }\n const existing = await db\n .select({ id: t.id })\n .from(t)\n .where(and(eq(t.type, type), eq(t.dedupeKey, opts.dedupeKey ?? '')))\n .limit(1)\n if (existing[0]) return { id: String(existing[0].id) }\n }\n throw new Error(\n `[bunderstack] enqueue of \"${name}\" lost a dedupe race twice — please retry`,\n )\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/jobs/worker.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AACnD,OAAO,KAAK,EAEV,QAAQ,EACR,iBAAiB,EACjB,UAAU,EACX,MAAM,UAAU,CAAA;AAqDjB,KAAK,UAAU,GAAG;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAA;AAsD1C,wBAAgB,eAAe,CAAC,IAAI,EAAE;IACpC,EAAE,EAAE,KAAK,CAAA;IACT,IAAI,EAAE,QAAQ,CAAA;IACd,4EAA4E;IAC5E,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC5B,MAAM,CAAC,EAAE,iBAAiB,CAAA;CAC3B;eAqgBmB,MAAM,GAAgB,OAAO,CAAC,UAAU,CAAC;iBAnBlC,MAAM,KAAgB,OAAO,CAAC,UAAU,CAAC;iBAa1C,OAAO,CAAC,IAAI,CAAC;iBAYhB,MAAM;;;;;;;;;;;;oBAiEb,SAAS,GACT,SAAS,GACT,WAAW,GACX,QAAQ;;;;;;;qBAQH,iBAAiB;EAIrC"}
1
+ {"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/jobs/worker.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AACnD,OAAO,KAAK,EAEV,QAAQ,EACR,iBAAiB,EACjB,UAAU,EACX,MAAM,UAAU,CAAA;AAqDjB,KAAK,UAAU,GAAG;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAA;AA8D1C,wBAAgB,eAAe,CAAC,IAAI,EAAE;IACpC,EAAE,EAAE,KAAK,CAAA;IACT,IAAI,EAAE,QAAQ,CAAA;IACd,4EAA4E;IAC5E,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC5B,MAAM,CAAC,EAAE,iBAAiB,CAAA;CAC3B;eAugBmB,MAAM,GAAgB,OAAO,CAAC,UAAU,CAAC;iBAnBlC,MAAM,KAAgB,OAAO,CAAC,UAAU,CAAC;iBAa1C,OAAO,CAAC,IAAI,CAAC;iBAYhB,MAAM;;;;;;;;;;;;oBAiEb,SAAS,GACT,SAAS,GACT,WAAW,GACX,QAAQ;;;;;;;qBAQH,iBAAiB;EAIrC"}