easy-ping 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +374 -0
- package/dist/adapter-f0OR2DhY.d.cts +200 -0
- package/dist/adapter-f0OR2DhY.d.ts +200 -0
- package/dist/adapters/drizzle.cjs +440 -0
- package/dist/adapters/drizzle.cjs.map +1 -0
- package/dist/adapters/drizzle.d.cts +991 -0
- package/dist/adapters/drizzle.d.ts +991 -0
- package/dist/adapters/drizzle.js +409 -0
- package/dist/adapters/drizzle.js.map +1 -0
- package/dist/adapters/mongodb.cjs +458 -0
- package/dist/adapters/mongodb.cjs.map +1 -0
- package/dist/adapters/mongodb.d.cts +32 -0
- package/dist/adapters/mongodb.d.ts +32 -0
- package/dist/adapters/mongodb.js +376 -0
- package/dist/adapters/mongodb.js.map +1 -0
- package/dist/browser.cjs +96 -0
- package/dist/browser.cjs.map +1 -0
- package/dist/browser.d.cts +45 -0
- package/dist/browser.d.ts +45 -0
- package/dist/browser.js +89 -0
- package/dist/browser.js.map +1 -0
- package/dist/chunk-BV3U542P.js +11 -0
- package/dist/chunk-BV3U542P.js.map +1 -0
- package/dist/chunk-NYDVNMKG.js +62 -0
- package/dist/chunk-NYDVNMKG.js.map +1 -0
- package/dist/chunk-OZSOIX23.js +120 -0
- package/dist/chunk-OZSOIX23.js.map +1 -0
- package/dist/chunk-PTGHMCCG.js +23 -0
- package/dist/chunk-PTGHMCCG.js.map +1 -0
- package/dist/chunk-W3BMEPPQ.js +50 -0
- package/dist/chunk-W3BMEPPQ.js.map +1 -0
- package/dist/client.cjs +160 -0
- package/dist/client.cjs.map +1 -0
- package/dist/client.d.cts +42 -0
- package/dist/client.d.ts +42 -0
- package/dist/client.js +158 -0
- package/dist/client.js.map +1 -0
- package/dist/index.cjs +920 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +203 -0
- package/dist/index.d.ts +203 -0
- package/dist/index.js +723 -0
- package/dist/index.js.map +1 -0
- package/dist/node.cjs +49 -0
- package/dist/node.cjs.map +1 -0
- package/dist/node.d.cts +8 -0
- package/dist/node.d.ts +8 -0
- package/dist/node.js +46 -0
- package/dist/node.js.map +1 -0
- package/dist/plugin-Bqj9xAOS.d.cts +217 -0
- package/dist/plugin-bTJKmnlq.d.ts +217 -0
- package/dist/plugins/digests.cjs +206 -0
- package/dist/plugins/digests.cjs.map +1 -0
- package/dist/plugins/digests.d.cts +68 -0
- package/dist/plugins/digests.d.ts +68 -0
- package/dist/plugins/digests.js +201 -0
- package/dist/plugins/digests.js.map +1 -0
- package/dist/plugins/preferences-client.cjs +23 -0
- package/dist/plugins/preferences-client.cjs.map +1 -0
- package/dist/plugins/preferences-client.d.cts +29 -0
- package/dist/plugins/preferences-client.d.ts +29 -0
- package/dist/plugins/preferences-client.js +18 -0
- package/dist/plugins/preferences-client.js.map +1 -0
- package/dist/plugins/preferences-react.cjs +125 -0
- package/dist/plugins/preferences-react.cjs.map +1 -0
- package/dist/plugins/preferences-react.d.cts +40 -0
- package/dist/plugins/preferences-react.d.ts +40 -0
- package/dist/plugins/preferences-react.js +101 -0
- package/dist/plugins/preferences-react.js.map +1 -0
- package/dist/plugins/preferences.cjs +147 -0
- package/dist/plugins/preferences.cjs.map +1 -0
- package/dist/plugins/preferences.d.cts +22 -0
- package/dist/plugins/preferences.d.ts +22 -0
- package/dist/plugins/preferences.js +119 -0
- package/dist/plugins/preferences.js.map +1 -0
- package/dist/plugins/push.cjs +146 -0
- package/dist/plugins/push.cjs.map +1 -0
- package/dist/plugins/push.d.cts +60 -0
- package/dist/plugins/push.d.ts +60 -0
- package/dist/plugins/push.js +144 -0
- package/dist/plugins/push.js.map +1 -0
- package/dist/provider-CA45WVqt.d.cts +23 -0
- package/dist/provider-CA45WVqt.d.ts +23 -0
- package/dist/providers/resend.cjs +69 -0
- package/dist/providers/resend.cjs.map +1 -0
- package/dist/providers/resend.d.cts +20 -0
- package/dist/providers/resend.d.ts +20 -0
- package/dist/providers/resend.js +66 -0
- package/dist/providers/resend.js.map +1 -0
- package/dist/providers/web-push.cjs +211 -0
- package/dist/providers/web-push.cjs.map +1 -0
- package/dist/providers/web-push.d.cts +33 -0
- package/dist/providers/web-push.d.ts +33 -0
- package/dist/providers/web-push.js +193 -0
- package/dist/providers/web-push.js.map +1 -0
- package/dist/react.cjs +218 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +57 -0
- package/dist/react.d.ts +57 -0
- package/dist/react.js +193 -0
- package/dist/react.js.map +1 -0
- package/dist/schema.cjs +263 -0
- package/dist/schema.cjs.map +1 -0
- package/dist/schema.d.cts +180 -0
- package/dist/schema.d.ts +180 -0
- package/dist/schema.js +200 -0
- package/dist/schema.js.map +1 -0
- package/dist/testing.cjs +342 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.cts +38 -0
- package/dist/testing.d.ts +38 -0
- package/dist/testing.js +340 -0
- package/dist/testing.js.map +1 -0
- package/package.json +168 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/core/base64url.ts","../../src/schema/declaration.ts","../../src/core/store.ts","../../src/adapters/drizzle/adapter.ts","../../src/adapters/drizzle/schema.ts"],"names":["sql","options","pgTable","text","jsonb","timestamp","index","uniqueIndex","integer","boolean","primaryKey"],"mappings":"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","file":"drizzle.cjs","sourcesContent":["// Web APIs, not Buffer: this runs on Workers and Vercel Edge.\n\nexport function encodeBase64Url(input: string | Uint8Array): string {\n const bytes = typeof input === \"string\" ? new TextEncoder().encode(input) : input;\n let binary = \"\";\n for (const byte of bytes) binary += String.fromCharCode(byte);\n return btoa(binary).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\n// Uint8Array<ArrayBuffer>: bare Uint8Array widens and is not a BufferSource.\nexport function decodeBase64UrlBytes(value: string): Uint8Array<ArrayBuffer> | null {\n try {\n const binary = atob(value.replace(/-/g, \"+\").replace(/_/g, \"/\"));\n return Uint8Array.from(binary, (char) => char.charCodeAt(0));\n } catch {\n return null;\n }\n}\n\nexport function decodeBase64Url(value: string): string | null {\n const bytes = decodeBase64UrlBytes(value);\n return bytes ? new TextDecoder().decode(bytes) : null;\n}\n","import type { SchemaDeclaration } from \"../core/plugin\";\n\nexport const toSnakeCase = (value: string) => value.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`);\n\n/**\n * Core tables in the format plugins use. Less expressive than the hand-written\n * Drizzle schema (no partial indexes, ordering or cascade) — a drift test\n * asserts the two agree on the fields they share.\n */\nexport const coreSchema = {\n notification: {\n tableName: \"notification\",\n fields: {\n id: { type: \"string\", required: true },\n userId: { type: \"string\", required: true },\n type: { type: \"string\", required: true },\n payload: { type: \"json\", required: true },\n actorId: { type: \"string\" },\n groupKey: { type: \"string\" },\n dedupeKey: { type: \"string\" },\n seenAt: { type: \"date\" },\n readAt: { type: \"date\" },\n archivedAt: { type: \"date\" },\n createdAt: { type: \"date\", required: true, defaultNow: true },\n },\n primaryKey: [\"id\"],\n indexes: [\n { on: [\"userId\", \"createdAt\"], name: \"notification_feed_idx\" },\n { on: [\"userId\", \"groupKey\"], name: \"notification_group_idx\" },\n { on: [\"userId\", \"dedupeKey\"], unique: true, name: \"notification_dedupe_idx\" },\n ],\n },\n\n notificationDelivery: {\n tableName: \"notification_delivery\",\n fields: {\n id: { type: \"string\", required: true },\n notificationId: { type: \"string\", required: true },\n channel: { type: \"string\", required: true },\n status: { type: \"string\", required: true, default: \"pending\" },\n attempts: { type: \"number\", required: true, default: 0 },\n maxAttempts: { type: \"number\", required: true, default: 5 },\n notBefore: { type: \"date\", required: true, defaultNow: true },\n claimedAt: { type: \"date\" },\n claimedBy: { type: \"string\" },\n lastError: { type: \"string\" },\n updatedAt: { type: \"date\", required: true, defaultNow: true },\n },\n primaryKey: [\"id\"],\n indexes: [\n { on: [\"status\", \"notBefore\"], name: \"delivery_claim_idx\" },\n { on: [\"notificationId\"], name: \"delivery_notification_idx\" },\n ],\n },\n\n notificationPreference: {\n tableName: \"notification_preference\",\n fields: {\n userId: { type: \"string\", required: true },\n type: { type: \"string\", required: true },\n channel: { type: \"string\", required: true },\n enabled: { type: \"boolean\", required: true, default: true },\n frequency: { type: \"string\", required: true, default: \"instant\" },\n },\n primaryKey: [\"userId\", \"type\", \"channel\"],\n },\n} satisfies SchemaDeclaration;\n","import { toSnakeCase } from \"../schema/declaration\";\nimport { ConfigError } from \"./errors\";\nimport type { SchemaDeclaration, TableDeclaration } from \"./plugin\";\n\nexport type Scalar = string | number | boolean | Date | null;\n\n/**\n * One operator per condition. Operators are tagged objects so a bare value is\n * always an equality test; json columns therefore cannot be filtered, since an\n * object value would be ambiguous.\n */\nexport type WhereCondition =\n | Scalar\n | { in: readonly (string | number)[] }\n | { lt: Date | number }\n | { lte: Date | number }\n | { gt: Date | number }\n | { gte: Date | number }\n | { not: Scalar };\n\nexport type WhereClause = Record<string, WhereCondition>;\n\nexport type QueryOptions = {\n limit?: number;\n orderBy?: { field: string; direction: \"asc\" | \"desc\" };\n};\n\nexport type UpsertOptions = { onConflict: readonly string[] };\n\n/**\n * Table access for plugins, scoped to the tables that plugin declared.\n *\n * Plugins could always declare tables via schema() but had no way to read\n * them, so preferences had to bolt its queries onto the core adapter. That\n * does not generalise to digests or push.\n */\nexport type PluginStore = {\n find<T = Record<string, unknown>>(\n table: string,\n where?: WhereClause,\n options?: QueryOptions,\n ): Promise<T[]>;\n insert(table: string, rows: readonly Record<string, unknown>[]): Promise<number>;\n upsert(\n table: string,\n rows: readonly Record<string, unknown>[],\n options: UpsertOptions,\n ): Promise<number>;\n update(table: string, where: WhereClause, set: Record<string, unknown>): Promise<number>;\n remove(table: string, where: WhereClause): Promise<number>;\n};\n\nexport type TableStorage = {\n readonly naming?: \"snake_case\" | \"preserve\";\n readonly serializesJson?: boolean;\n\n queryTable(\n table: string,\n where: WhereClause,\n options: QueryOptions,\n ): Promise<Record<string, unknown>[]>;\n insertRows(\n table: string,\n rows: readonly Record<string, unknown>[],\n onConflict?: readonly string[],\n ): Promise<number>;\n updateRows(table: string, where: WhereClause, set: Record<string, unknown>): Promise<number>;\n deleteRows(table: string, where: WhereClause): Promise<number>;\n};\n\nconst OPERATORS = [\"in\", \"lt\", \"lte\", \"gt\", \"gte\", \"not\"] as const;\n\nexport const isOperator = (value: unknown): boolean =>\n typeof value === \"object\" &&\n value !== null &&\n !(value instanceof Date) &&\n OPERATORS.some((op) => op in value);\n\n/**\n * Every table and column a plugin touches is checked against its own\n * declaration. Without this a plugin could read the notification table, and\n * unvalidated identifiers would reach the SQL builder.\n */\nexport function createPluginStore(\n pluginId: string,\n schema: SchemaDeclaration | undefined,\n storage: TableStorage,\n prefix: string,\n): PluginStore {\n const byTableName = new Map<string, TableDeclaration>();\n for (const declaration of Object.values(schema ?? {})) {\n byTableName.set(declaration.tableName, declaration);\n }\n\n function resolve(table: string): TableDeclaration {\n const declaration = byTableName.get(table);\n if (!declaration) {\n throw new ConfigError(\n `plugin \"${pluginId}\" accessed table \"${table}\", which it does not declare in schema().`,\n );\n }\n return declaration;\n }\n\n function checkFields(table: TableDeclaration, fields: Iterable<string>, context: string) {\n for (const field of fields) {\n const spec = table.fields[field];\n if (!spec) {\n throw new ConfigError(\n `plugin \"${pluginId}\" used unknown field \"${field}\" on \"${table.tableName}\" (${context}).`,\n );\n }\n if (context === \"where\" && spec.type === \"json\") {\n throw new ConfigError(\n `plugin \"${pluginId}\" cannot filter on json field \"${field}\"; ` +\n \"a bare object value is indistinguishable from an operator.\",\n );\n }\n }\n }\n\n const qualified = (table: TableDeclaration) => prefix + table.tableName;\n\n // A document store keeps the declared names; a SQL adapter wants columns.\n const column = (field: string) => (storage.naming === \"preserve\" ? field : toSnakeCase(field));\n\n /**\n * json columns must be handed to the driver as text; postgres-js cannot bind\n * a plain object and the insert fails outright.\n */\n const serialize = (table: TableDeclaration, row: Record<string, unknown>) => {\n if (storage.serializesJson === false) return row;\n\n const out: Record<string, unknown> = {};\n for (const [field, value] of Object.entries(row)) {\n out[field] =\n table.fields[field]?.type === \"json\" && value !== null && value !== undefined\n ? JSON.stringify(value)\n : value;\n }\n return out;\n };\n\n return {\n async find(table, where = {}, options = {}) {\n const declaration = resolve(table);\n checkFields(declaration, Object.keys(where), \"where\");\n if (options.orderBy) checkFields(declaration, [options.orderBy.field], \"orderBy\");\n\n const rows = await storage.queryTable(qualified(declaration), where, options);\n\n // SELECT * returns snake_case columns; plugins declare camelCase fields\n // and type their reads that way. Without this every property is\n // undefined at runtime while typechecking perfectly.\n return rows.map((row) => {\n const mapped: Record<string, unknown> = {};\n for (const field of Object.keys(declaration.fields)) {\n const value = row[column(field)];\n // Drivers differ: some hand back parsed jsonb, some raw text.\n mapped[field] =\n declaration.fields[field]?.type === \"json\" && typeof value === \"string\"\n ? JSON.parse(value)\n : value;\n }\n return mapped;\n }) as never;\n },\n\n async insert(table, rows) {\n const declaration = resolve(table);\n for (const row of rows) checkFields(declaration, Object.keys(row), \"insert\");\n return storage.insertRows(\n qualified(declaration),\n rows.map((row) => serialize(declaration, row)),\n );\n },\n\n async upsert(table, rows, options) {\n const declaration = resolve(table);\n for (const row of rows) checkFields(declaration, Object.keys(row), \"upsert\");\n checkFields(declaration, options.onConflict, \"onConflict\");\n return storage.insertRows(\n qualified(declaration),\n rows.map((row) => serialize(declaration, row)),\n options.onConflict,\n );\n },\n\n async update(table, where, set) {\n const declaration = resolve(table);\n checkFields(declaration, Object.keys(where), \"where\");\n checkFields(declaration, Object.keys(set), \"update\");\n return storage.updateRows(qualified(declaration), where, serialize(declaration, set));\n },\n\n async remove(table, where) {\n const declaration = resolve(table);\n checkFields(declaration, Object.keys(where), \"where\");\n return storage.deleteRows(qualified(declaration), where);\n },\n };\n}\n","import { type SQL, sql } from \"drizzle-orm\";\nimport type {\n ClaimArgs,\n ClaimedDelivery,\n DatabaseAdapter,\n DeliveryRelease,\n FeedPage,\n FeedQuery,\n InsertNotification,\n} from \"../../core/adapter\";\nimport { decodeBase64Url, encodeBase64Url } from \"../../core/base64url\";\nimport { isOperator, type QueryOptions, type WhereClause } from \"../../core/store\";\nimport type { Channel, DeliveryRecord, DeliveryStatus, NotificationRecord } from \"../../core/types\";\nimport { toSnakeCase } from \"../../schema/declaration\";\n\ntype Row = Record<string, unknown>;\n\n/**\n * Minimal structural view of a Drizzle client. Deliberately not the real\n * inferred type — introspecting Drizzle's generics at a boundary costs\n * thousands of type instantiations for nothing. See plan §16.\n */\ntype ExecutableDb = {\n execute: (query: SQL) => Promise<unknown>;\n transaction: <T>(fn: (tx: ExecutableDb) => Promise<T>) => Promise<T>;\n};\n\nexport type DrizzleAdapterOptions = {\n prefix?: string;\n};\n\n/** postgres-js returns rows directly; node-postgres wraps them in { rows }. */\nfunction toRows(result: unknown): Row[] {\n if (Array.isArray(result)) return result as Row[];\n if (result && typeof result === \"object\" && \"rows\" in result) {\n const { rows } = result as { rows: unknown };\n if (Array.isArray(rows)) return rows as Row[];\n }\n return [];\n}\n\nconst str = (value: unknown): string => String(value);\nconst nullableStr = (value: unknown): string | null => (value == null ? null : String(value));\nconst num = (value: unknown): number => Number(value);\nconst date = (value: unknown): Date => (value instanceof Date ? value : new Date(String(value)));\nconst nullableDate = (value: unknown): Date | null =>\n value == null ? null : value instanceof Date ? value : new Date(String(value));\n\n/**\n * postgres-js rejects a Date bound to an explicitly-typed ::timestamptz param.\n * ISO strings bind cleanly on every driver, and the cast does the conversion.\n */\nconst ts = (value: Date) => value.toISOString();\n\nconst encodeCursor = (createdAt: Date, id: string) =>\n encodeBase64Url(`${createdAt.toISOString()}|${id}`);\n\nfunction decodeCursor(cursor: string): { createdAt: Date; id: string } | null {\n const decoded = decodeBase64Url(cursor);\n if (!decoded) return null;\n\n const [iso, id] = decoded.split(\"|\");\n if (!iso || !id) return null;\n\n const createdAt = new Date(iso);\n return Number.isNaN(createdAt.getTime()) ? null : { createdAt, id };\n}\n\nfunction toNotification(row: Row): NotificationRecord {\n return {\n id: str(row.id),\n userId: str(row.user_id),\n type: str(row.type),\n payload: row.payload,\n actorId: nullableStr(row.actor_id),\n groupKey: nullableStr(row.group_key),\n dedupeKey: nullableStr(row.dedupe_key),\n seenAt: nullableDate(row.seen_at),\n readAt: nullableDate(row.read_at),\n archivedAt: nullableDate(row.archived_at),\n createdAt: date(row.created_at),\n };\n}\n\nfunction toDelivery(row: Row): DeliveryRecord {\n return {\n id: str(row.id),\n notificationId: str(row.notification_id),\n channel: str(row.channel) as Channel,\n status: str(row.status) as DeliveryStatus,\n attempts: num(row.attempts),\n maxAttempts: num(row.max_attempts),\n notBefore: date(row.not_before),\n claimedAt: nullableDate(row.claimed_at),\n claimedBy: nullableStr(row.claimed_by),\n lastError: nullableStr(row.last_error),\n updatedAt: date(row.updated_at),\n };\n}\n\n/** Dates bind as ISO strings; see the note on `ts` above. */\nconst bind = (value: unknown): unknown => (value instanceof Date ? value.toISOString() : value);\n\n/**\n * Identifiers here are already validated by the PluginStore against the\n * plugin's own schema, and still go through sql.identifier rather than\n * interpolation. Values are always bound.\n */\nfunction whereSql(where: WhereClause): SQL {\n const entries = Object.entries(where);\n if (entries.length === 0) return sql``;\n\n const conditions = entries.map(([field, condition]) => {\n const column = sql.identifier(toSnakeCase(field));\n\n if (condition === null) return sql`${column} IS NULL`;\n if (!isOperator(condition)) return sql`${column} = ${bind(condition)}`;\n\n const operator = condition as Record<string, unknown>;\n if (\"in\" in operator) {\n const list = operator.in as readonly (string | number)[];\n if (list.length === 0) return sql`FALSE`;\n return sql`${column} IN (${sql.join(\n list.map((item) => sql`${item}`),\n sql`, `,\n )})`;\n }\n if (\"lt\" in operator) return sql`${column} < ${bind(operator.lt)}`;\n if (\"lte\" in operator) return sql`${column} <= ${bind(operator.lte)}`;\n if (\"gt\" in operator) return sql`${column} > ${bind(operator.gt)}`;\n if (\"gte\" in operator) return sql`${column} >= ${bind(operator.gte)}`;\n if (\"not\" in operator) {\n return operator.not === null\n ? sql`${column} IS NOT NULL`\n : sql`${column} IS DISTINCT FROM ${bind(operator.not)}`;\n }\n return sql`TRUE`;\n });\n\n return sql` WHERE ${sql.join(conditions, sql` AND `)}`;\n}\n\nexport function drizzleAdapter(\n client: unknown,\n options: DrizzleAdapterOptions = {},\n): DatabaseAdapter {\n const db = client as ExecutableDb;\n const prefix = options.prefix ?? \"\";\n\n const NOTIFICATION = sql.identifier(`${prefix}notification`);\n const DELIVERY = sql.identifier(`${prefix}notification_delivery`);\n\n const run = async (query: SQL, on: ExecutableDb = db) => toRows(await on.execute(query));\n\n return {\n name: \"drizzle-pg\",\n naming: \"snake_case\",\n serializesJson: true,\n\n async createNotifications(input: readonly InsertNotification[]) {\n if (input.length === 0) return { created: [], deduped: [] };\n\n // One clock. created_at/updated_at have a DEFAULT now() for hand-written\n // SQL, but now() is the *database* clock while every cutoff the library\n // compares it against (markSeen, getFailedDeliveries) comes from the app.\n // App and database are different hosts in production, and NTP skew of a\n // few hundred ms is routine, so the newest rows fell outside markSeen and\n // the unseen badge never cleared. The mongo adapter always wrote these in\n // the app; this makes both agree.\n const now = new Date();\n\n return db.transaction(async (tx) => {\n const values = input.map(\n (row) => sql`(\n ${row.id}::text,\n ${row.userId}::text,\n ${row.type}::text,\n ${JSON.stringify(row.payload ?? null)}::jsonb,\n ${row.actorId ?? null}::text,\n ${row.groupKey ?? null}::text,\n ${row.dedupeKey ?? null}::text,\n ${ts(now)}::timestamptz\n )`,\n );\n\n // Rows with a NULL dedupe_key never conflict — Postgres treats NULLs\n // as distinct — so unlimited undeduped notifications coexist.\n const inserted = await run(\n sql`\n INSERT INTO ${NOTIFICATION} (id, user_id, type, payload, actor_id, group_key, dedupe_key, created_at)\n VALUES ${sql.join(values, sql`, `)}\n ON CONFLICT (user_id, dedupe_key) DO NOTHING\n RETURNING id\n `,\n tx,\n );\n\n const created = new Set(inserted.map((row) => str(row.id)));\n\n const deliveries = input\n .filter((row) => created.has(row.id))\n .flatMap((row) =>\n row.deliveries.map(\n (delivery) => sql`(\n ${delivery.id}::text,\n ${row.id}::text,\n ${delivery.channel}::text,\n ${delivery.maxAttempts}::integer,\n ${ts(delivery.notBefore)}::timestamptz,\n ${ts(now)}::timestamptz\n )`,\n ),\n );\n\n if (deliveries.length > 0) {\n await tx.execute(sql`\n INSERT INTO ${DELIVERY} (id, notification_id, channel, max_attempts, not_before, updated_at)\n VALUES ${sql.join(deliveries, sql`, `)}\n `);\n }\n\n return {\n created: [...created],\n deduped: input.filter((row) => !created.has(row.id)).map((row) => row.id),\n };\n });\n },\n\n async claimPendingDeliveries(args: ClaimArgs): Promise<readonly ClaimedDelivery[]> {\n const now = args.now ?? new Date();\n const staleBefore = new Date(now.getTime() - args.leaseMs);\n\n const channelFilter =\n args.channels && args.channels.length > 0\n ? sql`AND channel IN (${sql.join(\n args.channels.map((channel) => sql`${channel}`),\n sql`, `,\n )})`\n : sql``;\n\n const idFilter =\n args.ids && args.ids.length > 0\n ? sql`AND id IN (${sql.join(\n args.ids.map((id) => sql`${id}`),\n sql`, `,\n )})`\n : sql``;\n\n // SKIP LOCKED is what lets concurrent sweeps step around each other\n // rather than block. The CTE joins the notification in the same round\n // trip — claiming 20 rows then looking each one up is the N+1 this\n // primitive exists to avoid. See RFC 0003 §5.\n const rows = await run(sql`\n WITH claimed AS (\n UPDATE ${DELIVERY}\n SET status = 'claimed', claimed_at = ${ts(now)}::timestamptz, claimed_by = ${args.claimToken}::text\n WHERE id IN (\n SELECT id FROM ${DELIVERY}\n WHERE (status = 'pending' OR (status = 'claimed' AND claimed_at < ${ts(staleBefore)}::timestamptz))\n AND not_before <= ${ts(now)}::timestamptz\n AND attempts < max_attempts\n ${channelFilter}\n ${idFilter}\n ORDER BY not_before ASC, id ASC\n LIMIT ${args.limit}\n FOR UPDATE SKIP LOCKED\n )\n RETURNING id, notification_id, channel, attempts, max_attempts\n )\n SELECT c.id, c.notification_id, c.channel, c.attempts, c.max_attempts,\n n.user_id, n.type, n.payload, n.actor_id\n FROM claimed c\n JOIN ${NOTIFICATION} n ON n.id = c.notification_id\n `);\n\n return rows.map((row) => ({\n id: str(row.id),\n notificationId: str(row.notification_id),\n channel: str(row.channel) as Channel,\n attempts: num(row.attempts),\n maxAttempts: num(row.max_attempts),\n notification: {\n userId: str(row.user_id),\n type: str(row.type),\n payload: row.payload,\n actorId: nullableStr(row.actor_id),\n },\n }));\n },\n\n async releaseDeliveries(releases: readonly DeliveryRelease[]) {\n if (releases.length === 0) return;\n const now = new Date();\n\n const values = releases.map(({ id, outcome, nextAttemptAt }) => {\n const retryable = outcome.result === \"failed\" && outcome.retryable;\n const error = outcome.result === \"failed\" ? outcome.error.slice(0, 2000) : null;\n\n return sql`(\n ${id}::text,\n ${outcome.result}::text,\n ${retryable}::boolean,\n ${error}::text,\n ${nextAttemptAt ? ts(nextAttemptAt) : null}::timestamptz\n )`;\n });\n\n // Written unconditionally — no claimed_by predicate. If the lease expired\n // and another worker re-sent, the duplicate already happened; recording\n // the true terminal state beats wedging the row in 'claimed'. RFC 0003 §6.\n await db.execute(sql`\n UPDATE ${DELIVERY} d\n SET status = CASE\n WHEN v.result = 'sent' THEN 'sent'\n WHEN v.retryable AND d.attempts + 1 < d.max_attempts THEN 'pending'\n ELSE 'failed'\n END,\n attempts = CASE WHEN v.result = 'sent' THEN d.attempts ELSE d.attempts + 1 END,\n last_error = v.error,\n not_before = COALESCE(v.not_before, d.not_before),\n claimed_at = NULL,\n claimed_by = NULL,\n updated_at = ${ts(now)}::timestamptz\n FROM (VALUES ${sql.join(values, sql`, `)}) AS v(id, result, retryable, error, not_before)\n WHERE d.id = v.id\n `);\n },\n\n async listNotifications(query: FeedQuery): Promise<FeedPage> {\n const cursor = query.cursor ? decodeCursor(query.cursor) : null;\n\n const rows = await run(sql`\n SELECT * FROM ${NOTIFICATION}\n WHERE user_id = ${query.userId}::text\n AND archived_at IS NULL\n ${query.unreadOnly ? sql`AND read_at IS NULL` : sql``}\n ${\n cursor\n ? sql`AND (created_at, id) < (${ts(cursor.createdAt)}::timestamptz, ${cursor.id}::text)`\n : sql``\n }\n ORDER BY created_at DESC, id DESC\n LIMIT ${query.limit + 1}\n `);\n\n const page = rows.slice(0, query.limit).map(toNotification);\n const last = page.at(-1);\n\n return {\n notifications: page,\n nextCursor:\n rows.length > query.limit && last ? encodeCursor(last.createdAt, last.id) : null,\n };\n },\n\n async countUnseen(userId: string) {\n const rows = await run(sql`\n SELECT count(*)::int AS count FROM ${NOTIFICATION}\n WHERE user_id = ${userId}::text AND seen_at IS NULL AND archived_at IS NULL\n `);\n return num(rows[0]?.count ?? 0);\n },\n\n async markSeen(userId: string, before: Date) {\n await db.execute(sql`\n UPDATE ${NOTIFICATION} SET seen_at = ${ts(new Date())}::timestamptz\n WHERE user_id = ${userId}::text AND seen_at IS NULL AND created_at <= ${ts(before)}::timestamptz\n `);\n },\n\n async markRead(userId: string, notificationIds: readonly string[]) {\n if (notificationIds.length === 0) return 0;\n\n // Scoped by user_id as well as id — a caller must never be able to flip\n // someone else's row by guessing an id. RFC 0002 §2.\n // No `read_at IS NULL` filter: re-marking must be idempotent. Returning\n // 0 for an already-read row made the route 404, which made the client\n // roll its optimistic update back and show the item as unread again.\n // COALESCE preserves the original timestamp.\n const rows = await run(sql`\n UPDATE ${NOTIFICATION}\n SET read_at = COALESCE(read_at, ${ts(new Date())}::timestamptz)\n WHERE user_id = ${userId}::text\n AND id IN (${sql.join(\n notificationIds.map((id) => sql`${id}`),\n sql`, `,\n )})\n RETURNING id\n `);\n return rows.length;\n },\n\n async markAllRead(userId: string) {\n const rows = await run(sql`\n UPDATE ${NOTIFICATION} SET read_at = ${ts(new Date())}::timestamptz\n WHERE user_id = ${userId}::text AND read_at IS NULL\n RETURNING id\n `);\n return rows.length;\n },\n\n async queryTable(table: string, where: WhereClause, options: QueryOptions) {\n const order = options.orderBy\n ? sql` ORDER BY ${sql.identifier(toSnakeCase(options.orderBy.field))} ${\n options.orderBy.direction === \"desc\" ? sql`DESC` : sql`ASC`\n }`\n : sql``;\n const limit = options.limit ? sql` LIMIT ${options.limit}` : sql``;\n\n return run(\n sql`SELECT * FROM ${sql.identifier(table)}${whereSql(where)}${order}${limit}`,\n ) as Promise<Record<string, unknown>[]>;\n },\n\n async insertRows(\n table: string,\n rows: readonly Record<string, unknown>[],\n onConflict?: readonly string[],\n ) {\n if (rows.length === 0) return 0;\n\n // Column order is taken from the first row and every row is projected\n // onto it, so a ragged batch cannot shift values into other columns.\n const columns = Object.keys(rows[0] ?? {});\n if (columns.length === 0) return 0;\n\n const values = rows.map(\n (row) =>\n sql`(${sql.join(\n columns.map((column) => sql`${bind(row[column])}`),\n sql`, `,\n )})`,\n );\n\n const conflict =\n onConflict && onConflict.length > 0\n ? sql` ON CONFLICT (${sql.join(\n onConflict.map((field) => sql.identifier(toSnakeCase(field))),\n sql`, `,\n )}) DO UPDATE SET ${sql.join(\n columns\n .filter((column) => !onConflict.includes(column))\n .map(\n (column) =>\n sql`${sql.identifier(toSnakeCase(column))} = EXCLUDED.${sql.identifier(\n toSnakeCase(column),\n )}`,\n ),\n sql`, `,\n )}`\n : sql``;\n\n const inserted = await run(sql`\n INSERT INTO ${sql.identifier(table)} (${sql.join(\n columns.map((column) => sql.identifier(toSnakeCase(column))),\n sql`, `,\n )})\n VALUES ${sql.join(values, sql`, `)}${conflict}\n RETURNING 1 AS ok\n `);\n return inserted.length;\n },\n\n async updateRows(table: string, where: WhereClause, set: Record<string, unknown>) {\n const assignments = Object.entries(set).map(\n ([field, value]) => sql`${sql.identifier(toSnakeCase(field))} = ${bind(value)}`,\n );\n if (assignments.length === 0) return 0;\n\n const rows = await run(sql`\n UPDATE ${sql.identifier(table)}\n SET ${sql.join(assignments, sql`, `)}${whereSql(where)}\n RETURNING 1 AS ok\n `);\n return rows.length;\n },\n\n async deleteRows(table: string, where: WhereClause) {\n const rows = await run(\n sql`DELETE FROM ${sql.identifier(table)}${whereSql(where)} RETURNING 1 AS ok`,\n );\n return rows.length;\n },\n\n async getFailedDeliveries(args: { since: Date; limit: number }) {\n const rows = await run(sql`\n SELECT * FROM ${DELIVERY}\n WHERE status = 'failed' AND updated_at >= ${ts(args.since)}::timestamptz\n ORDER BY updated_at DESC\n LIMIT ${args.limit}\n `);\n return rows.map(toDelivery);\n },\n };\n}\n","import { sql } from \"drizzle-orm\";\nimport {\n boolean,\n index,\n integer,\n jsonb,\n pgTable,\n primaryKey,\n text,\n timestamp,\n uniqueIndex,\n} from \"drizzle-orm/pg-core\";\nimport type { Channel, DeliveryStatus, Frequency } from \"../../core/types\";\n\nexport function createSchema(prefix = \"\") {\n const name = (base: string) => `${prefix}${base}`;\n\n const notification = pgTable(\n name(\"notification\"),\n {\n id: text(\"id\").primaryKey(),\n userId: text(\"user_id\").notNull(),\n type: text(\"type\").notNull(),\n payload: jsonb(\"payload\").notNull(),\n actorId: text(\"actor_id\"),\n groupKey: text(\"group_key\"),\n dedupeKey: text(\"dedupe_key\"),\n seenAt: timestamp(\"seen_at\", { withTimezone: true }),\n readAt: timestamp(\"read_at\", { withTimezone: true }),\n archivedAt: timestamp(\"archived_at\", { withTimezone: true }),\n createdAt: timestamp(\"created_at\", { withTimezone: true }).notNull().defaultNow(),\n },\n (table) => [\n index(name(\"notification_feed_idx\")).on(table.userId, table.createdAt.desc()),\n index(name(\"notification_unseen_idx\")).on(table.userId).where(sql`${table.seenAt} is null`),\n index(name(\"notification_group_idx\")).on(table.userId, table.groupKey),\n // Postgres treats NULLs as distinct here, so unlimited rows may carry no\n // dedupe key while a present one is enforced unique per user.\n uniqueIndex(name(\"notification_dedupe_idx\")).on(table.userId, table.dedupeKey),\n ],\n );\n\n const notificationDelivery = pgTable(\n name(\"notification_delivery\"),\n {\n id: text(\"id\").primaryKey(),\n notificationId: text(\"notification_id\")\n .notNull()\n .references(() => notification.id, { onDelete: \"cascade\" }),\n channel: text(\"channel\").$type<Channel>().notNull(),\n status: text(\"status\").$type<DeliveryStatus>().notNull().default(\"pending\"),\n attempts: integer(\"attempts\").notNull().default(0),\n maxAttempts: integer(\"max_attempts\").notNull().default(5),\n notBefore: timestamp(\"not_before\", { withTimezone: true }).notNull().defaultNow(),\n claimedAt: timestamp(\"claimed_at\", { withTimezone: true }),\n claimedBy: text(\"claimed_by\"),\n lastError: text(\"last_error\"),\n updatedAt: timestamp(\"updated_at\", { withTimezone: true }).notNull().defaultNow(),\n },\n (table) => [\n index(name(\"delivery_claim_idx\")).on(table.status, table.notBefore),\n index(name(\"delivery_notification_idx\")).on(table.notificationId),\n ],\n );\n\n const notificationPreference = pgTable(\n name(\"notification_preference\"),\n {\n userId: text(\"user_id\").notNull(),\n type: text(\"type\").notNull(),\n channel: text(\"channel\").$type<Channel>().notNull(),\n enabled: boolean(\"enabled\").notNull().default(true),\n frequency: text(\"frequency\").$type<Frequency>().notNull().default(\"instant\"),\n },\n (table) => [primaryKey({ columns: [table.userId, table.type, table.channel] })],\n );\n\n return { notification, notificationDelivery, notificationPreference };\n}\n\nexport type EasyPingSchema = ReturnType<typeof createSchema>;\n\nexport const schema = createSchema();\n"]}
|