easy-ping 0.10.1 → 0.10.3

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.
Files changed (130) hide show
  1. package/README.md +10 -5
  2. package/dist/adapters/drizzle.cjs +0 -2
  3. package/dist/adapters/drizzle.js +3 -5
  4. package/dist/adapters/mongodb.cjs +0 -2
  5. package/dist/adapters/mongodb.js +4 -6
  6. package/dist/adapters/mysql.cjs +0 -2
  7. package/dist/adapters/mysql.js +6 -8
  8. package/dist/adapters/postgres.cjs +0 -2
  9. package/dist/adapters/postgres.js +8 -10
  10. package/dist/adapters/sqlite.cjs +0 -2
  11. package/dist/adapters/sqlite.js +6 -8
  12. package/dist/browser.cjs +0 -2
  13. package/dist/browser.js +0 -2
  14. package/dist/{chunk-JKTMUMQK.js → chunk-2UOEAG6H.js} +1 -3
  15. package/dist/{chunk-R3BE67L7.js → chunk-74KSKP6B.js} +0 -2
  16. package/dist/{chunk-O2PBBL5X.js → chunk-7C4K5WKW.js} +1 -3
  17. package/dist/{chunk-CK5UPVYP.js → chunk-CRSGL5SG.js} +0 -2
  18. package/dist/{chunk-PTGHMCCG.js → chunk-D3UX3WWD.js} +0 -2
  19. package/dist/{chunk-CMWOIYVQ.js → chunk-EN3NMHSI.js} +4 -6
  20. package/dist/{chunk-ORZKH224.js → chunk-G32BYQAT.js} +0 -2
  21. package/dist/{chunk-A4HX5YTG.js → chunk-IXXPJMYS.js} +3 -5
  22. package/dist/{chunk-CFLVAF2R.js → chunk-JDR3U7X4.js} +2 -4
  23. package/dist/{chunk-AEYYTE5I.js → chunk-JRZ2U4IT.js} +0 -2
  24. package/dist/{chunk-UM2L26PV.js → chunk-KOS3YZMZ.js} +1 -3
  25. package/dist/{chunk-HSB5T3LD.js → chunk-OKIQ5BEV.js} +1 -3
  26. package/dist/{chunk-JN3UCQBP.js → chunk-SZWEW3WN.js} +0 -2
  27. package/dist/{chunk-SSHVZHSJ.js → chunk-VA4W4UFI.js} +0 -2
  28. package/dist/client.cjs +0 -2
  29. package/dist/client.js +0 -2
  30. package/dist/index.cjs +0 -2
  31. package/dist/index.js +12 -14
  32. package/dist/node.cjs +0 -2
  33. package/dist/node.js +3 -5
  34. package/dist/plugins/digests.cjs +0 -2
  35. package/dist/plugins/digests.js +1 -3
  36. package/dist/plugins/mobile-push.cjs +0 -2
  37. package/dist/plugins/mobile-push.js +3 -5
  38. package/dist/plugins/preferences-client.cjs +0 -2
  39. package/dist/plugins/preferences-client.js +1 -3
  40. package/dist/plugins/preferences-react.cjs +0 -1
  41. package/dist/plugins/preferences-react.js +0 -1
  42. package/dist/plugins/preferences.cjs +0 -2
  43. package/dist/plugins/preferences.js +3 -5
  44. package/dist/plugins/push.cjs +0 -2
  45. package/dist/plugins/push.js +4 -6
  46. package/dist/plugins/telegram.cjs +0 -2
  47. package/dist/plugins/telegram.js +5 -7
  48. package/dist/providers/expo-push.cjs +0 -2
  49. package/dist/providers/expo-push.js +0 -2
  50. package/dist/providers/resend.cjs +0 -2
  51. package/dist/providers/resend.js +0 -2
  52. package/dist/providers/telegram.cjs +0 -2
  53. package/dist/providers/telegram.js +1 -3
  54. package/dist/providers/web-push.cjs +0 -2
  55. package/dist/providers/web-push.js +1 -3
  56. package/dist/react-native.cjs +0 -1
  57. package/dist/react-native.js +0 -1
  58. package/dist/react.cjs +0 -1
  59. package/dist/react.js +0 -1
  60. package/dist/schema.cjs +0 -2
  61. package/dist/schema.js +4 -6
  62. package/dist/sw.cjs +0 -2
  63. package/dist/sw.js +0 -2
  64. package/dist/testing.cjs +0 -2
  65. package/dist/testing.js +0 -2
  66. package/package.json +7 -5
  67. package/dist/adapters/drizzle.cjs.map +0 -1
  68. package/dist/adapters/drizzle.js.map +0 -1
  69. package/dist/adapters/mongodb.cjs.map +0 -1
  70. package/dist/adapters/mongodb.js.map +0 -1
  71. package/dist/adapters/mysql.cjs.map +0 -1
  72. package/dist/adapters/mysql.js.map +0 -1
  73. package/dist/adapters/postgres.cjs.map +0 -1
  74. package/dist/adapters/postgres.js.map +0 -1
  75. package/dist/adapters/sqlite.cjs.map +0 -1
  76. package/dist/adapters/sqlite.js.map +0 -1
  77. package/dist/browser.cjs.map +0 -1
  78. package/dist/browser.js.map +0 -1
  79. package/dist/chunk-A4HX5YTG.js.map +0 -1
  80. package/dist/chunk-AEYYTE5I.js.map +0 -1
  81. package/dist/chunk-CFLVAF2R.js.map +0 -1
  82. package/dist/chunk-CK5UPVYP.js.map +0 -1
  83. package/dist/chunk-CMWOIYVQ.js.map +0 -1
  84. package/dist/chunk-HSB5T3LD.js.map +0 -1
  85. package/dist/chunk-JKTMUMQK.js.map +0 -1
  86. package/dist/chunk-JN3UCQBP.js.map +0 -1
  87. package/dist/chunk-O2PBBL5X.js.map +0 -1
  88. package/dist/chunk-ORZKH224.js.map +0 -1
  89. package/dist/chunk-PTGHMCCG.js.map +0 -1
  90. package/dist/chunk-R3BE67L7.js.map +0 -1
  91. package/dist/chunk-SSHVZHSJ.js.map +0 -1
  92. package/dist/chunk-UM2L26PV.js.map +0 -1
  93. package/dist/client.cjs.map +0 -1
  94. package/dist/client.js.map +0 -1
  95. package/dist/index.cjs.map +0 -1
  96. package/dist/index.js.map +0 -1
  97. package/dist/node.cjs.map +0 -1
  98. package/dist/node.js.map +0 -1
  99. package/dist/plugins/digests.cjs.map +0 -1
  100. package/dist/plugins/digests.js.map +0 -1
  101. package/dist/plugins/mobile-push.cjs.map +0 -1
  102. package/dist/plugins/mobile-push.js.map +0 -1
  103. package/dist/plugins/preferences-client.cjs.map +0 -1
  104. package/dist/plugins/preferences-client.js.map +0 -1
  105. package/dist/plugins/preferences-react.cjs.map +0 -1
  106. package/dist/plugins/preferences-react.js.map +0 -1
  107. package/dist/plugins/preferences.cjs.map +0 -1
  108. package/dist/plugins/preferences.js.map +0 -1
  109. package/dist/plugins/push.cjs.map +0 -1
  110. package/dist/plugins/push.js.map +0 -1
  111. package/dist/plugins/telegram.cjs.map +0 -1
  112. package/dist/plugins/telegram.js.map +0 -1
  113. package/dist/providers/expo-push.cjs.map +0 -1
  114. package/dist/providers/expo-push.js.map +0 -1
  115. package/dist/providers/resend.cjs.map +0 -1
  116. package/dist/providers/resend.js.map +0 -1
  117. package/dist/providers/telegram.cjs.map +0 -1
  118. package/dist/providers/telegram.js.map +0 -1
  119. package/dist/providers/web-push.cjs.map +0 -1
  120. package/dist/providers/web-push.js.map +0 -1
  121. package/dist/react-native.cjs.map +0 -1
  122. package/dist/react-native.js.map +0 -1
  123. package/dist/react.cjs.map +0 -1
  124. package/dist/react.js.map +0 -1
  125. package/dist/schema.cjs.map +0 -1
  126. package/dist/schema.js.map +0 -1
  127. package/dist/sw.cjs.map +0 -1
  128. package/dist/sw.js.map +0 -1
  129. package/dist/testing.cjs.map +0 -1
  130. package/dist/testing.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/adapters/sql/adapter.ts","../src/adapters/sql/dialect.ts"],"names":[],"mappings":"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","file":"chunk-CMWOIYVQ.js","sourcesContent":["import 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\";\nimport type { Dialect, Row, SqlExec, SqlResult } from \"./dialect\";\nimport { Statement } from \"./statement\";\n\nexport type SqlAdapterOptions = {\n /** Table-name prefix. Must match the instance's `tablePrefix`. */\n prefix?: string;\n /** Makes createNotifications atomic and, on MySQL, lets a claim use FOR UPDATE SKIP LOCKED. */\n transaction?: <T>(fn: (exec: SqlExec) => Promise<T>) => Promise<T>;\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);\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 const [iso, id] = decoded.split(\"|\");\n if (!iso || !id) return null;\n const createdAt = new Date(iso);\n return Number.isNaN(createdAt.getTime()) ? null : { createdAt, id };\n}\n\nconst NONE: SqlResult = { rows: [], affectedRows: 0 };\n\nconst sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\n/** InnoDB rolls back the loser of a deadlock and asks for a retry; bounded and jittered. */\nasync function retryingLocks<T>(dialect: Dialect, attempt: () => Promise<T>): Promise<T> {\n for (let tries = 0; ; tries += 1) {\n try {\n return await attempt();\n } catch (error) {\n if (tries >= 5 || !dialect.isRetryableLock(error)) throw error;\n await sleep(5 + Math.random() * 20 * (tries + 1));\n }\n }\n}\n\n/**\n * Every SQL engine that is not Postgres, behind one dialect: the same queries\n * minus RETURNING, casts and IS DISTINCT FROM. RFC 0005.\n */\nexport function createSqlAdapter(\n dialect: Dialect,\n exec: SqlExec,\n options: SqlAdapterOptions = {},\n): DatabaseAdapter {\n const prefix = options.prefix ?? \"\";\n const q = dialect.quote;\n const NOTIFICATION = q(`${prefix}notification`);\n const DELIVERY = q(`${prefix}notification_delivery`);\n\n const stmt = () => new Statement({ placeholder: dialect.placeholder, bind: dialect.bind });\n const run = (statement: Statement, on: SqlExec = exec) => on(statement.text, statement.params);\n const atomically = <T>(fn: (on: SqlExec) => Promise<T>): Promise<T> =>\n options.transaction ? options.transaction(fn) : fn(exec);\n\n const toNotification = (row: Row): NotificationRecord => ({\n id: str(row.id),\n userId: str(row.user_id),\n type: str(row.type),\n payload: dialect.readJson(row.payload),\n actorId: nullableStr(row.actor_id),\n groupKey: nullableStr(row.group_key),\n dedupeKey: nullableStr(row.dedupe_key),\n seenAt: dialect.readNullableDate(row.seen_at),\n readAt: dialect.readNullableDate(row.read_at),\n archivedAt: dialect.readNullableDate(row.archived_at),\n createdAt: dialect.readDate(row.created_at),\n });\n\n const toDelivery = (row: Row): DeliveryRecord => ({\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: dialect.readDate(row.not_before),\n claimedAt: dialect.readNullableDate(row.claimed_at),\n claimedBy: nullableStr(row.claimed_by),\n lastError: nullableStr(row.last_error),\n updatedAt: dialect.readDate(row.updated_at),\n });\n\n function appendWhere(statement: Statement, where: WhereClause): void {\n const entries = Object.entries(where);\n if (entries.length === 0) return;\n\n statement.raw(\" WHERE \");\n\n entries.forEach(([field, condition], index) => {\n if (index > 0) statement.raw(\" AND \");\n const column = q(toSnakeCase(field));\n\n if (condition === null) {\n statement.raw(`${column} IS NULL`);\n return;\n }\n if (!isOperator(condition)) {\n statement.raw(`${column} = `).value(condition);\n return;\n }\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) statement.raw(\"1 = 0\");\n else statement.raw(`${column} IN (`).list(list).raw(\")\");\n return;\n }\n if (\"lt\" in operator) statement.raw(`${column} < `).value(operator.lt);\n else if (\"lte\" in operator) statement.raw(`${column} <= `).value(operator.lte);\n else if (\"gt\" in operator) statement.raw(`${column} > `).value(operator.gt);\n else if (\"gte\" in operator) statement.raw(`${column} >= `).value(operator.gte);\n else if (\"not\" in operator) {\n if (operator.not === null) statement.raw(`${column} IS NOT NULL`);\n else dialect.distinctFrom(statement, column, operator.not);\n } else statement.raw(\"1 = 1\");\n });\n }\n\n function eligible(statement: Statement, args: ClaimArgs, now: Date, staleBefore: Date): void {\n statement\n .raw(\"(status = 'pending' OR (status = 'claimed' AND claimed_at < \")\n .value(staleBefore)\n .raw(\")) AND not_before <= \")\n .value(now)\n .raw(\" AND attempts < max_attempts\");\n if (args.channels && args.channels.length > 0) {\n statement.raw(\" AND channel IN (\").list(args.channels).raw(\")\");\n }\n if (args.ids && args.ids.length > 0) {\n statement.raw(\" AND id IN (\").list(args.ids).raw(\")\");\n }\n }\n\n /** The token is unique per call, so this is exactly what the call just claimed. */\n const readClaimed = (token: string, on: SqlExec) =>\n run(\n stmt()\n .raw(\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 FROM ${DELIVERY} c` +\n ` JOIN ${NOTIFICATION} n ON n.id = c.notification_id WHERE c.claimed_by = `,\n )\n .value(token)\n .raw(\" AND c.status = 'claimed' ORDER BY c.not_before ASC, c.id ASC\"),\n on,\n );\n\n return {\n name: dialect.name,\n naming: \"snake_case\",\n serializesJson: true,\n\n async createNotifications(input: readonly InsertNotification[]) {\n if (input.length === 0) return { created: [], deduped: [] };\n const now = new Date();\n\n return atomically(async (on) => {\n const created: string[] = [];\n const deduped: string[] = [];\n\n // No RETURNING on MySQL and INSERT IGNORE hides real errors: look, insert, treat a race as dedupe.\n for (const row of input) {\n if (row.dedupeKey) {\n const existing = await run(\n stmt()\n .raw(`SELECT id FROM ${NOTIFICATION} WHERE user_id = `)\n .value(row.userId)\n .raw(\" AND dedupe_key = \")\n .value(row.dedupeKey),\n on,\n );\n if (existing.rows.length > 0) {\n deduped.push(row.id);\n continue;\n }\n }\n\n try {\n await run(\n stmt()\n .raw(\n `INSERT INTO ${NOTIFICATION} ` +\n \"(id, user_id, type, payload, actor_id, group_key, dedupe_key, created_at) VALUES (\",\n )\n .list([\n row.id,\n row.userId,\n row.type,\n JSON.stringify(row.payload ?? null),\n row.actorId ?? null,\n row.groupKey ?? null,\n row.dedupeKey ?? null,\n now,\n ])\n .raw(\")\"),\n on,\n );\n } catch (error) {\n if (row.dedupeKey && dialect.isDuplicateKey(error)) {\n deduped.push(row.id);\n continue;\n }\n throw error;\n }\n created.push(row.id);\n }\n\n const createdSet = new Set(created);\n const deliveries = input\n .filter((row) => createdSet.has(row.id))\n .flatMap((row) => row.deliveries.map((delivery) => ({ row, delivery })));\n\n if (deliveries.length > 0) {\n const insert = stmt().raw(\n `INSERT INTO ${DELIVERY} ` +\n \"(id, notification_id, channel, max_attempts, not_before, updated_at) VALUES \",\n );\n deliveries.forEach(({ row, delivery }, index) => {\n if (index > 0) insert.raw(\", \");\n insert\n .raw(\"(\")\n .list([\n delivery.id,\n row.id,\n delivery.channel,\n delivery.maxAttempts,\n delivery.notBefore,\n now,\n ])\n .raw(\")\");\n });\n await run(insert, on);\n }\n\n return { created, deduped };\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 const strategy = options.transaction\n ? dialect.claim.withTransaction\n : dialect.claim.withoutTransaction;\n\n const claimSet = (statement: Statement) =>\n statement\n .raw(\"status = 'claimed', claimed_at = \")\n .value(now)\n .raw(\", claimed_by = \")\n .value(args.claimToken);\n\n let result: SqlResult;\n\n if (strategy === \"skip-locked\") {\n result = await atomically(async (on) => {\n const pick = stmt().raw(`SELECT id FROM ${DELIVERY} WHERE `);\n eligible(pick, args, now, staleBefore);\n pick\n .raw(\" ORDER BY not_before ASC, id ASC LIMIT \")\n .value(args.limit)\n .raw(\" FOR UPDATE SKIP LOCKED\");\n const ids = (await run(pick, on)).rows.map((row) => str(row.id));\n if (ids.length === 0) return NONE;\n\n const update = stmt().raw(`UPDATE ${DELIVERY} SET `);\n claimSet(update).raw(\" WHERE id IN (\").list(ids).raw(\")\");\n await run(update, on);\n return readClaimed(args.claimToken, on);\n });\n } else {\n const update = stmt();\n if (strategy === \"derived-update\") {\n // The derived table is materialised, which is what lets MySQL update the table it\n // selects from; the outer predicate is re-evaluated on the locked row.\n update.raw(`UPDATE ${DELIVERY} d JOIN (SELECT id FROM ${DELIVERY} WHERE `);\n eligible(update, args, now, staleBefore);\n update\n .raw(\" ORDER BY not_before ASC, id ASC LIMIT \")\n .value(args.limit)\n .raw(\") s ON s.id = d.id SET d.status = 'claimed', d.claimed_at = \")\n .value(now)\n .raw(\", d.claimed_by = \")\n .value(args.claimToken)\n .raw(\" WHERE d.status = 'pending' OR (d.status = 'claimed' AND d.claimed_at < \")\n .value(staleBefore)\n .raw(\")\");\n } else {\n update.raw(`UPDATE ${DELIVERY} SET `);\n claimSet(update).raw(` WHERE id IN (SELECT id FROM ${DELIVERY} WHERE `);\n eligible(update, args, now, staleBefore);\n update.raw(\" ORDER BY not_before ASC, id ASC LIMIT \").value(args.limit).raw(\")\");\n }\n const updated = await retryingLocks(dialect, () => run(update));\n result = updated.affectedRows > 0 ? await readClaimed(args.claimToken, exec) : NONE;\n }\n\n return result.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: dialect.readJson(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 // No claimed_by predicate (RFC 0003 §6). `status` before `attempts`: MySQL evaluates SET\n // left to right with updated values, and the CASE needs the old count.\n await atomically(async (on) => {\n for (const { id, outcome, nextAttemptAt } of releases) {\n const result = outcome.result;\n const retryable = outcome.result === \"failed\" && outcome.retryable;\n const error =\n outcome.result === \"failed\"\n ? outcome.error.slice(0, 2000)\n : outcome.result === \"skipped\"\n ? outcome.reason.slice(0, 2000)\n : null;\n\n await run(\n stmt()\n .raw(`UPDATE ${DELIVERY} SET status = CASE WHEN `)\n .value(result)\n .raw(\" = 'sent' THEN 'sent' WHEN \")\n .value(result)\n .raw(\" = 'skipped' THEN 'skipped' WHEN \")\n .value(retryable)\n .raw(\" = 1 AND attempts + 1 < max_attempts THEN 'pending' ELSE 'failed' END\")\n .raw(\", attempts = CASE WHEN \")\n .value(result)\n .raw(\" IN ('sent', 'skipped') THEN attempts ELSE attempts + 1 END\")\n .raw(\", last_error = \")\n .value(error)\n .raw(\", not_before = COALESCE(\")\n .value(nextAttemptAt ?? null)\n .raw(\", not_before), claimed_at = NULL, claimed_by = NULL, updated_at = \")\n .value(now)\n .raw(\" WHERE id = \")\n .value(id),\n on,\n );\n }\n });\n },\n\n async listNotifications(feed: FeedQuery): Promise<FeedPage> {\n const cursor = feed.cursor ? decodeCursor(feed.cursor) : null;\n\n const statement = stmt()\n .raw(`SELECT * FROM ${NOTIFICATION} WHERE user_id = `)\n .value(feed.userId)\n .raw(\" AND archived_at IS NULL\");\n if (feed.unreadOnly) statement.raw(\" AND read_at IS NULL\");\n if (cursor) {\n statement\n .raw(\" AND (created_at, id) < (\")\n .value(cursor.createdAt)\n .raw(\", \")\n .value(cursor.id)\n .raw(\")\");\n }\n statement.raw(\" ORDER BY created_at DESC, id DESC LIMIT \").value(feed.limit + 1);\n\n const { rows } = await run(statement);\n const page = rows.slice(0, feed.limit).map(toNotification);\n const last = page.at(-1);\n\n return {\n notifications: page,\n nextCursor: rows.length > feed.limit && last ? encodeCursor(last.createdAt, last.id) : null,\n };\n },\n\n async countUnseen(userId: string) {\n const { rows } = await run(\n stmt()\n .raw(`SELECT COUNT(*) AS count FROM ${NOTIFICATION} WHERE user_id = `)\n .value(userId)\n .raw(\" 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 run(\n stmt()\n .raw(`UPDATE ${NOTIFICATION} SET seen_at = `)\n .value(new Date())\n .raw(\" WHERE user_id = \")\n .value(userId)\n .raw(\" AND seen_at IS NULL AND created_at <= \")\n .value(before),\n );\n },\n\n async markRead(userId: string, notificationIds: readonly string[]) {\n if (notificationIds.length === 0) return 0;\n\n // Idempotent, so the answer is rows matched; MySQL's affectedRows reports rows changed.\n await run(\n stmt()\n .raw(`UPDATE ${NOTIFICATION} SET read_at = COALESCE(read_at, `)\n .value(new Date())\n .raw(\") WHERE user_id = \")\n .value(userId)\n .raw(\" AND id IN (\")\n .list(notificationIds)\n .raw(\")\"),\n );\n const { rows } = await run(\n stmt()\n .raw(`SELECT COUNT(*) AS count FROM ${NOTIFICATION} WHERE user_id = `)\n .value(userId)\n .raw(\" AND id IN (\")\n .list(notificationIds)\n .raw(\")\"),\n );\n return num(rows[0]?.count ?? 0);\n },\n\n async markAllRead(userId: string) {\n const { affectedRows } = await run(\n stmt()\n .raw(`UPDATE ${NOTIFICATION} SET read_at = `)\n .value(new Date())\n .raw(\" WHERE user_id = \")\n .value(userId)\n .raw(\" AND read_at IS NULL\"),\n );\n return affectedRows;\n },\n\n async getFailedDeliveries(args: { since: Date; limit: number }) {\n const { rows } = await run(\n stmt()\n .raw(`SELECT * FROM ${DELIVERY} WHERE status = 'failed' AND updated_at >= `)\n .value(args.since)\n .raw(\" ORDER BY updated_at DESC LIMIT \")\n .value(args.limit),\n );\n return rows.map(toDelivery);\n },\n\n async queryTable(table: string, where: WhereClause, queryOptions: QueryOptions) {\n const statement = stmt().raw(`SELECT * FROM ${q(table)}`);\n appendWhere(statement, where);\n if (queryOptions.orderBy) {\n const direction = queryOptions.orderBy.direction === \"desc\" ? \"DESC\" : \"ASC\";\n statement.raw(` ORDER BY ${q(toSnakeCase(queryOptions.orderBy.field))} ${direction}`);\n }\n if (queryOptions.limit) statement.raw(\" LIMIT \").value(queryOptions.limit);\n return [...(await run(statement)).rows];\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 const columns = Object.keys(rows[0] ?? {});\n if (columns.length === 0) return 0;\n\n const statement = stmt().raw(\n `INSERT INTO ${q(table)} (${columns.map((c) => q(toSnakeCase(c))).join(\", \")}) VALUES `,\n );\n rows.forEach((row, index) => {\n if (index > 0) statement.raw(\", \");\n statement\n .raw(\"(\")\n .list(columns.map((column) => row[column]))\n .raw(\")\");\n });\n\n if (onConflict && onConflict.length > 0) {\n statement.raw(\n dialect.upsertClause(\n onConflict.map((f) => q(toSnakeCase(f))),\n columns.filter((c) => !onConflict.includes(c)).map((c) => q(toSnakeCase(c))),\n ),\n );\n await run(statement);\n // MySQL reports 2 for an updated row; the rows handed in is the honest count.\n return rows.length;\n }\n\n return (await run(statement)).affectedRows;\n },\n\n async updateRows(table: string, where: WhereClause, set: Record<string, unknown>) {\n const assignments = Object.entries(set);\n if (assignments.length === 0) return 0;\n\n const statement = stmt().raw(`UPDATE ${q(table)} SET `);\n assignments.forEach(([field, value], index) => {\n if (index > 0) statement.raw(\", \");\n statement.raw(`${q(toSnakeCase(field))} = `).value(value);\n });\n appendWhere(statement, where);\n return (await run(statement)).affectedRows;\n },\n\n async deleteRows(table: string, where: WhereClause) {\n const statement = stmt().raw(`DELETE FROM ${q(table)}`);\n appendWhere(statement, where);\n return (await run(statement)).affectedRows;\n },\n };\n}\n","import type { Placeholder, Statement } from \"./statement\";\nimport { quoteWith } from \"./statement\";\n\nexport type Row = Record<string, unknown>;\n\nexport type SqlResult = {\n rows: readonly Row[];\n /** Rows an INSERT/UPDATE/DELETE touched; drivers without RETURNING report through this. */\n affectedRows: number;\n};\n\n/** The whole driver contract for the MySQL and SQLite adapters. */\nexport type SqlExec = (text: string, params: readonly unknown[]) => Promise<SqlResult>;\n\n/** How a claim takes its rows. RFC 0005 §3. */\nexport type ClaimStrategy = \"skip-locked\" | \"derived-update\" | \"subquery-update\";\n\nexport type Dialect = {\n name: string;\n placeholder: Placeholder;\n quote: (identifier: string) => string;\n bind: (value: unknown) => unknown;\n readDate: (value: unknown) => Date;\n readNullableDate: (value: unknown) => Date | null;\n readJson: (value: unknown) => unknown;\n /** Appends `column IS DISTINCT FROM value` in the engine's spelling. */\n distinctFrom: (statement: Statement, column: string, value: unknown) => void;\n /** The clause after a multi-row INSERT that makes it an upsert. Columns arrive quoted. */\n upsertClause: (conflict: readonly string[], update: readonly string[]) => string;\n isDuplicateKey: (error: unknown) => boolean;\n /** A deadlock or busy lock the engine asks the caller to retry; the statement was rolled back. */\n isRetryableLock: (error: unknown) => boolean;\n claim: { withTransaction: ClaimStrategy; withoutTransaction: ClaimStrategy };\n};\n\nconst parseUtc = (value: unknown): Date => {\n if (value instanceof Date) return value;\n const text = String(value).trim();\n // \"2026-09-23 10:11:12.345\" from a dateStrings driver carries no zone; it was written as UTC.\n return new Date(/^\\d{4}-\\d{2}-\\d{2} /.test(text) ? `${text.replace(\" \", \"T\")}Z` : text);\n};\n\nconst readJson = (value: unknown) => (typeof value === \"string\" ? JSON.parse(value) : value);\n\nconst bindCommon = (value: unknown) => {\n if (typeof value === \"boolean\") return value ? 1 : 0;\n if (value === undefined) return null;\n return value;\n};\n\nconst code = (error: unknown) => (error as { code?: unknown } | null)?.code;\nconst errno = (error: unknown) => (error as { errno?: unknown } | null)?.errno;\n\nexport const mysqlDialect: Dialect = {\n name: \"mysql\",\n placeholder: \"question\",\n quote: quoteWith(\"`\"),\n bind: (value) =>\n value instanceof Date ? value.toISOString().slice(0, 23).replace(\"T\", \" \") : bindCommon(value),\n readDate: parseUtc,\n readNullableDate: (value) => (value == null ? null : parseUtc(value)),\n readJson,\n distinctFrom: (statement, column, value) => {\n statement.raw(`NOT (${column} <=> `).value(value).raw(\")\");\n },\n // VALUES() rather than the 8.0.19 `AS new` alias: it also works on MariaDB.\n upsertClause: (conflict, update) =>\n ` ON DUPLICATE KEY UPDATE ${(\n update.length === 0\n ? [`${conflict[0]} = ${conflict[0]}`]\n : update.map((column) => `${column} = VALUES(${column})`)\n ).join(\", \")}`,\n isDuplicateKey: (error) => code(error) === \"ER_DUP_ENTRY\" || errno(error) === 1062,\n isRetryableLock: (error) =>\n code(error) === \"ER_LOCK_DEADLOCK\" ||\n errno(error) === 1213 ||\n code(error) === \"ER_LOCK_WAIT_TIMEOUT\" ||\n errno(error) === 1205,\n claim: { withTransaction: \"skip-locked\", withoutTransaction: \"derived-update\" },\n};\n\nexport const sqliteDialect: Dialect = {\n name: \"sqlite\",\n placeholder: \"question\",\n quote: quoteWith('\"'),\n bind: (value) => (value instanceof Date ? value.toISOString() : bindCommon(value)),\n readDate: parseUtc,\n readNullableDate: (value) => (value == null ? null : parseUtc(value)),\n readJson,\n distinctFrom: (statement, column, value) => {\n statement.raw(`${column} IS NOT `).value(value);\n },\n upsertClause: (conflict, update) =>\n ` ON CONFLICT (${conflict.join(\", \")}) DO ${\n update.length === 0\n ? \"NOTHING\"\n : `UPDATE SET ${update.map((column) => `${column} = excluded.${column}`).join(\", \")}`\n }`,\n isDuplicateKey: (error) =>\n code(error) === \"SQLITE_CONSTRAINT_UNIQUE\" ||\n /UNIQUE constraint failed/i.test(String((error as Error | null)?.message ?? \"\")),\n isRetryableLock: (error) =>\n code(error) === \"SQLITE_BUSY\" ||\n /database is locked/i.test(String((error as Error | null)?.message ?? \"\")),\n claim: { withTransaction: \"subquery-update\", withoutTransaction: \"subquery-update\" },\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/schema/render-sqlite.ts"],"names":[],"mappings":"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","file":"chunk-HSB5T3LD.js","sourcesContent":["import type { FieldDeclaration, SchemaDeclaration } from \"../core/plugin\";\nimport { toSnakeCase } from \"./declaration\";\n\nexport const SQLITE_TYPE: Record<FieldDeclaration[\"type\"], string> = {\n string: \"TEXT\",\n number: \"INTEGER\",\n boolean: \"INTEGER\",\n // ISO-8601 with milliseconds and a trailing Z, the same 24 characters\n // `Date#toISOString` writes, so `<=` on the text is `<=` on the instant.\n date: \"TEXT\",\n json: \"TEXT\",\n};\n\nconst quote = (value: string) => `\"${value.split('\"').join('\"\"')}\"`;\n\nconst defaultClause = (spec: FieldDeclaration): string => {\n if (spec.defaultNow) return \" DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))\";\n if (spec.default === undefined) return \"\";\n if (typeof spec.default === \"string\") return ` DEFAULT '${spec.default.replace(/'/g, \"''\")}'`;\n if (typeof spec.default === \"boolean\") return ` DEFAULT ${spec.default ? 1 : 0}`;\n return ` DEFAULT ${spec.default}`;\n};\n\n/** One column's definition, as CREATE TABLE and ALTER TABLE ... ADD COLUMN both need it. */\nexport function sqliteColumnDefinition(field: string, spec: FieldDeclaration): string {\n const nullability = spec.required ? \" NOT NULL\" : \"\";\n return `${quote(toSnakeCase(field))} ${SQLITE_TYPE[spec.type]}${nullability}${defaultClause(spec)}`;\n}\n\n/** SchemaDeclaration to SQLite DDL. Every statement is `IF NOT EXISTS`. */\nexport function renderSqliteDdl(schema: SchemaDeclaration, prefix = \"\"): string[] {\n const statements: string[] = [];\n\n for (const table of Object.values(schema)) {\n const name = quote(prefix + table.tableName);\n\n const columns = Object.entries(table.fields).map(\n ([field, spec]) => ` ${sqliteColumnDefinition(field, spec)}`,\n );\n\n if (table.primaryKey?.length) {\n columns.push(\n ` PRIMARY KEY (${table.primaryKey.map((f) => quote(toSnakeCase(f))).join(\", \")})`,\n );\n }\n\n statements.push(`CREATE TABLE IF NOT EXISTS ${name} (\\n${columns.join(\",\\n\")}\\n)`);\n\n for (const index of table.indexes ?? []) {\n const indexName = quote(\n prefix + (index.name ?? `${table.tableName}_${index.on.join(\"_\")}_idx`),\n );\n const cols = index.on.map((f) => quote(toSnakeCase(f))).join(\", \");\n statements.push(\n `CREATE ${index.unique ? \"UNIQUE \" : \"\"}INDEX IF NOT EXISTS ${indexName} ON ${name} (${cols})`,\n );\n }\n }\n\n return statements;\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/core/backoff.ts","../src/core/runner.ts"],"names":[],"mappings":"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","file":"chunk-JKTMUMQK.js","sourcesContent":["/** Exponential backoff with ±20% jitter, floored by the sweep interval. RFC 0001 §4. */\nconst SCHEDULE_MS = [30_000, 120_000, 480_000, 1_920_000] as const;\n\nexport type Backoff = \"exponential\" | ((attempt: number) => number);\n\nexport function backoffMs(attempt: number, strategy: Backoff = \"exponential\"): number {\n if (typeof strategy === \"function\") return Math.max(0, strategy(attempt));\n\n const index = Math.min(Math.max(attempt, 1), SCHEDULE_MS.length) - 1;\n const base = SCHEDULE_MS[index] ?? SCHEDULE_MS[SCHEDULE_MS.length - 1] ?? 30_000;\n return Math.round(base * (0.8 + Math.random() * 0.4));\n}\n\nexport const nextAttemptAt = (attempt: number, strategy?: Backoff, now = new Date()): Date =>\n new Date(now.getTime() + backoffMs(attempt, strategy));\n","import type { ClaimedDelivery, DatabaseAdapter, DeliveryOutcome, DeliveryRelease } from \"./adapter\";\nimport { type Backoff, nextAttemptAt } from \"./backoff\";\nimport type { ChannelsConfig, Worker } from \"./config\";\nimport type { NotificationDefinitions } from \"./definition\";\nimport type { Logger } from \"./errors\";\nimport type { AnyPlugin } from \"./plugin\";\nimport { DELIVERIES_CHANNEL, type Signals } from \"./signals\";\nimport type { Channel, Recipient } from \"./types\";\n\nexport type RunnerDeps = {\n adapter: DatabaseAdapter;\n definitions: NotificationDefinitions;\n channels: ChannelsConfig;\n plugins: readonly AnyPlugin[];\n getRecipients: (userIds: readonly string[]) => Promise<readonly Recipient[]>;\n logger: Logger;\n leaseMs: number;\n batchSize: number;\n backoff: Backoff;\n /** Wakes an idle worker the moment a send commits, so the idle interval can be long. */\n signals?: Signals | undefined;\n};\n\n/** Long on purpose: a send in this process wakes the loop, and the cron sweep backstops the rest. */\nexport const DEFAULT_WORKER_IDLE_MS = 10_000;\n\nexport type SweepResult = {\n claimed: number;\n sent: number;\n failed: number;\n /** Deliveries that had nothing to do. Not errors; see DeliveryOutcome. */\n skipped: number;\n};\n\nasync function withTimeout<T>(\n run: (signal: AbortSignal) => Promise<T>,\n ms: number,\n label: string,\n): Promise<T> {\n const controller = new AbortController();\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const timeout = new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n // Abort first: racing a promise leaves the original request running.\n controller.abort();\n reject(new Error(`${label} timed out after ${ms}ms`));\n }, ms);\n });\n\n try {\n return await Promise.race([run(controller.signal), timeout]);\n } finally {\n clearTimeout(timer);\n }\n}\n\n/** Persisted to `last_error`, which admin tooling reads: addresses do not go in. */\nexport const redactErrorMessage = (text: string) =>\n text.replace(/[^\\s\"'<>()[\\]{},;]+@[^\\s\"'<>()[\\]{},;]+/g, \"[redacted-email]\");\n\nconst message = (error: unknown) =>\n redactErrorMessage(error instanceof Error ? error.message : String(error));\n\nexport function createRunner(deps: RunnerDeps) {\n async function deliverOne(\n delivery: ClaimedDelivery,\n recipient: Recipient | undefined,\n ): Promise<DeliveryOutcome> {\n if (!recipient) {\n return { result: \"failed\", error: \"recipient could not be resolved\", retryable: false };\n }\n\n if (delivery.channel === \"inApp\") {\n // The notification row is the delivery. Nothing to transmit.\n return { result: \"sent\" };\n }\n\n if (delivery.channel === \"email\") {\n const provider = deps.channels.email?.provider;\n if (!provider) {\n return { result: \"failed\", error: \"no email provider configured\", retryable: false };\n }\n // Captured before the closure below: narrowing does not survive into it.\n const to = recipient.email;\n if (!to) {\n return { result: \"failed\", error: \"recipient has no email address\", retryable: false };\n }\n\n const email = deps.definitions[delivery.notification.type]?.email;\n if (!email) {\n return {\n result: \"failed\",\n error: `notification type \"${delivery.notification.type}\" has no email template`,\n retryable: false,\n };\n }\n\n try {\n const payload = delivery.notification.payload as never;\n const html = await email.template(payload);\n\n await withTimeout(\n (signal) =>\n provider.send({\n to,\n subject: email.subject(payload),\n html,\n // Delivery is at-least-once; the provider dedupes on this.\n idempotencyKey: delivery.id,\n signal,\n }),\n provider.timeoutMs ?? 30_000,\n `${provider.name} send`,\n );\n\n return { result: \"sent\" };\n } catch (error) {\n return {\n result: \"failed\",\n error: message(error),\n retryable: provider.isRetryable(error),\n };\n }\n }\n\n const owner = deps.plugins.find(\n (plugin) => plugin.channels?.includes(delivery.channel) && plugin.hooks?.deliver,\n );\n if (!owner?.hooks?.deliver) {\n return {\n result: \"failed\",\n error: `no plugin delivers channel \"${delivery.channel}\"`,\n retryable: false,\n };\n }\n\n try {\n return await withTimeout(\n (signal) =>\n Promise.resolve(\n // biome-ignore lint/style/noNonNullAssertion: guarded directly above\n owner.hooks!.deliver!({\n channel: delivery.channel,\n deliveryId: delivery.id,\n attempt: delivery.attempts + 1,\n recipient,\n notification: {\n id: delivery.notificationId,\n userId: delivery.notification.userId,\n type: delivery.notification.type,\n payload: delivery.notification.payload,\n },\n signal,\n }),\n ),\n 30_000,\n `plugin \"${owner.id}\" deliver`,\n );\n } catch (error) {\n return { result: \"failed\", error: message(error), retryable: true };\n }\n }\n\n async function notifyAfterDeliver(delivery: ClaimedDelivery, outcome: DeliveryOutcome) {\n const isTerminal =\n outcome.result !== \"failed\" ||\n !outcome.retryable ||\n delivery.attempts + 1 >= delivery.maxAttempts;\n\n for (const plugin of deps.plugins) {\n const hook = plugin.hooks?.afterDeliver;\n if (!hook) continue;\n try {\n await hook({\n deliveryId: delivery.id,\n notificationId: delivery.notificationId,\n userId: delivery.notification.userId,\n channel: delivery.channel,\n outcome,\n attempt: delivery.attempts + 1,\n isTerminal,\n });\n } catch (error) {\n // Fail open: the message is already sent. RFC 0004 §5.\n deps.logger.error(`plugin \"${plugin.id}\" afterDeliver threw; ignoring`, {\n deliveryId: delivery.id,\n error,\n });\n }\n }\n }\n\n async function runOnce(\n options: { limit?: number; channels?: readonly Channel[]; ids?: readonly string[] } = {},\n ): Promise<SweepResult> {\n const claimToken = crypto.randomUUID();\n\n const claimed = await deps.adapter.claimPendingDeliveries({\n limit: options.limit ?? deps.batchSize,\n leaseMs: deps.leaseMs,\n claimToken,\n ...(options.channels ? { channels: options.channels } : {}),\n ...(options.ids ? { ids: options.ids } : {}),\n });\n\n if (claimed.length === 0) return { claimed: 0, sent: 0, failed: 0, skipped: 0 };\n\n const userIds = [...new Set(claimed.map((row) => row.notification.userId))];\n const recipients = await deps.getRecipients(userIds);\n const byUserId = new Map(recipients.map((recipient) => [recipient.userId, recipient]));\n\n const releases: DeliveryRelease[] = [];\n let sent = 0;\n let failed = 0;\n let skipped = 0;\n\n for (const delivery of claimed) {\n const outcome = await deliverOne(delivery, byUserId.get(delivery.notification.userId));\n\n if (outcome.result === \"sent\") sent += 1;\n else if (outcome.result === \"skipped\") skipped += 1;\n else failed += 1;\n\n releases.push({\n id: delivery.id,\n claimToken,\n outcome,\n ...(outcome.result === \"failed\" && outcome.retryable\n ? { nextAttemptAt: nextAttemptAt(delivery.attempts + 1, deps.backoff) }\n : {}),\n });\n\n await notifyAfterDeliver(delivery, outcome);\n }\n\n await deps.adapter.releaseDeliveries(releases);\n return { claimed: claimed.length, sent, failed, skipped };\n }\n\n /** Drains until empty. Pass `ids` to bound it to one send. */\n async function drain(\n options: { maxSweeps?: number; ids?: readonly string[] } = {},\n ): Promise<SweepResult> {\n const maxSweeps = options.maxSweeps ?? 50;\n const total: SweepResult = { claimed: 0, sent: 0, failed: 0, skipped: 0 };\n\n for (let sweep = 0; sweep < maxSweeps; sweep += 1) {\n const result = await runOnce(options.ids ? { ids: options.ids } : {});\n total.claimed += result.claimed;\n total.sent += result.sent;\n total.failed += result.failed;\n total.skipped += result.skipped;\n if (result.claimed === 0) break;\n }\n\n return total;\n }\n\n let warnedMissingWaitUntil = false;\n\n function scheduleDeferred(\n waitUntil?: (promise: Promise<unknown>) => void,\n ids?: readonly string[],\n ): void {\n if (waitUntil) {\n waitUntil(\n drain({ maxSweeps: 10, ...(ids ? { ids } : {}) }).catch((error) =>\n deps.logger.error(\"deferred sweep failed\", { error }),\n ),\n );\n return;\n }\n\n if (!warnedMissingWaitUntil) {\n warnedMissingWaitUntil = true;\n deps.logger.warn(\n \"delivery.mode is 'deferred' but no delivery.waitUntil was supplied — \" +\n \"falling back to the cron sweep. Notifications are not lost, but they \" +\n \"will not be delivered until POST /cron runs.\",\n );\n }\n }\n\n function startWorker(options: { intervalMs?: number; batchSize?: number } = {}): Worker {\n const intervalMs = options.intervalMs ?? DEFAULT_WORKER_IDLE_MS;\n let stopped = false;\n let wake: (() => void) | undefined;\n let pending = false;\n\n const unsubscribe = deps.signals?.subscribe(DELIVERIES_CHANNEL, () => {\n pending = true;\n wake?.();\n });\n\n // Sleeps for the interval, or until a send publishes, whichever is first.\n const idle = (ms: number) =>\n new Promise<void>((resolve) => {\n if (pending) {\n pending = false;\n resolve();\n return;\n }\n const timer = setTimeout(done, ms);\n function done() {\n clearTimeout(timer);\n wake = undefined;\n pending = false;\n resolve();\n }\n wake = done;\n });\n\n const loop = (async () => {\n while (!stopped) {\n try {\n const result = await runOnce({ limit: options.batchSize ?? deps.batchSize });\n // Only idle when there was nothing to do; otherwise keep draining.\n if (result.claimed === 0 && !stopped) await idle(intervalMs);\n } catch (error) {\n deps.logger.error(\"worker sweep failed\", { error });\n if (!stopped) await idle(intervalMs);\n }\n }\n })();\n\n return {\n stop: async () => {\n stopped = true;\n unsubscribe?.();\n wake?.();\n // Let the in-flight sweep finish and release its leases.\n await loop;\n },\n };\n }\n\n return { runOnce, drain, scheduleDeferred, startWorker };\n}\n\nexport type Runner = ReturnType<typeof createRunner>;\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/core/plugin.ts"],"names":[],"mappings":";AAwKO,SAAS,aACd,MAAA,EAC8B;AAC9B,EAAA,OAAO,MAAA;AACT;AAgBO,SAAS,mBACd,MAAA,EAC6B;AAC7B,EAAA,OAAO,MAAA;AACT","file":"chunk-JN3UCQBP.js","sourcesContent":["import type { DeliveryOutcome } from \"./adapter\";\nimport type { Logger } from \"./errors\";\nimport type { PluginStore } from \"./store\";\nimport type { SignRequest, TokenClaims } from \"./tokens\";\nimport type { Channel, Recipient } from \"./types\";\n\nexport type Promisable<T> = T | Promise<T>;\n\nexport type PrepareContext = {\n type: string;\n recipients: readonly Recipient[];\n};\n\nexport type BeforeSendContext = {\n type: string;\n recipient: Recipient;\n payload: unknown;\n actorId: string | null;\n dedupeKey: string | null;\n /** Whatever this plugin's own prepare() returned for this send. */\n prepared: unknown;\n};\n\nexport type BeforeSendResult =\n | { action: \"continue\"; payload?: unknown; dedupeKey?: string }\n | { action: \"cancel\"; reason: string };\n\nexport type ChannelDecision = {\n channel: Channel;\n action: \"send\" | \"skip\" | \"defer\";\n /** defer only. Written to notification_delivery.not_before. */\n notBefore?: Date | undefined;\n};\n\nexport type ResolveChannelsContext = {\n type: string;\n recipient: Recipient;\n payload: unknown;\n decisions: readonly ChannelDecision[];\n /** Whatever this plugin's own prepare() returned for this send. */\n prepared: unknown;\n};\n\nexport type DeliverContext = {\n channel: Channel;\n deliveryId: string;\n attempt: number;\n recipient: Recipient;\n notification: { id: string; userId: string; type: string; payload: unknown };\n /** Aborted on the runner's timeout. */\n signal: AbortSignal;\n};\n\nexport type AfterSendContext = {\n type: string;\n payload: unknown;\n /** Committed rows. Ids exist only at this point. */\n notifications: readonly { id: string; userId: string }[];\n /** Whatever this plugin's own prepare() returned for this send. */\n prepared: unknown;\n};\n\nexport type AfterDeliverContext = {\n deliveryId: string;\n notificationId: string;\n userId: string;\n channel: Channel;\n outcome: DeliveryOutcome;\n attempt: number;\n isTerminal: boolean;\n};\n\n/** beforeSend and resolveChannels fail closed; afterDeliver fails open. */\nexport type PluginHooks = {\n /** Once per send, for bulk loading — the per-recipient hooks would be an N+1. */\n prepare?: (ctx: PrepareContext) => Promisable<unknown>;\n beforeSend?: (ctx: BeforeSendContext) => Promisable<BeforeSendResult>;\n resolveChannels?: (ctx: ResolveChannelsContext) => Promisable<readonly ChannelDecision[]>;\n /** Once per send, after commit. The only hook that sees notification ids. Fails open. */\n afterSend?: (ctx: AfterSendContext) => Promisable<void>;\n /** Delivers one of the plugin's declared `channels`. Core carries inApp and email. */\n deliver?: (ctx: DeliverContext) => Promisable<DeliveryOutcome>;\n afterDeliver?: (ctx: AfterDeliverContext) => Promisable<void>;\n};\n\nexport type FieldType = \"string\" | \"number\" | \"boolean\" | \"date\" | \"json\";\n\nexport type FieldDeclaration = {\n type: FieldType;\n required?: boolean;\n default?: string | number | boolean;\n /** date fields only. Renders as defaultNow() / CURRENT_TIMESTAMP. */\n defaultNow?: boolean;\n};\n\nexport type IndexDeclaration = {\n on: readonly string[];\n unique?: boolean;\n name?: string;\n};\n\nexport type TableDeclaration = {\n tableName: string;\n fields: Record<string, FieldDeclaration>;\n primaryKey?: readonly string[];\n indexes?: readonly IndexDeclaration[];\n readOnly?: boolean;\n};\n\nexport type SchemaDeclaration = Record<string, TableDeclaration>;\n\nexport type RouteScope =\n | { type: \"user\" }\n | { type: \"machine\" }\n | { type: \"signed\"; purpose: string }\n | { type: \"custom\"; justification: string };\n\nexport type RouteContext = {\n request: Request;\n /** Resolved for user-scoped routes, so a handler cannot forget to scope. */\n userId: string | null;\n /** Verified before dispatch, so a handler cannot forget to check. */\n claims: TokenClaims | null;\n params: Record<string, string>;\n};\n\nexport type RouteDefinition = {\n path: string;\n method: \"GET\" | \"POST\";\n scope: RouteScope;\n handler: (ctx: RouteContext) => Promise<Response>;\n};\n\n/** Handed to a plugin at startup so it need not be given the database twice. */\nexport type PluginInitContext = {\n /** Scoped to the tables this plugin declares in schema(). */\n store: PluginStore;\n /** Mints tokens for this plugin's own `signed` routes only. The secret itself is never handed out. */\n sign: (request: SignRequest) => Promise<string>;\n logger: Logger;\n /** Configured notification names, so a route can refuse to store rows for types that do not exist. */\n notificationTypes: readonly string[];\n /** The app's resolver, for plugins that need timezones or addresses. */\n getRecipients: (userIds: readonly string[]) => Promise<readonly Recipient[]>;\n /** The instance's own send(), so a composed message inherits retry, backoff and idempotency. */\n send: (\n type: string,\n args: { to: string | readonly string[]; payload: unknown; dedupeKey?: string },\n ) => Promise<unknown>;\n};\n\nexport type EasyPingPlugin<\n TId extends string = string,\n TSchema extends SchemaDeclaration = SchemaDeclaration,\n> = {\n id: TId;\n dependsOn?: readonly string[];\n schema?: TSchema;\n routes?: readonly RouteDefinition[];\n hooks?: PluginHooks;\n /** Channels this plugin delivers. Makes them usable in a notification. */\n channels?: readonly Channel[];\n /** Called once by easyPing() before any hook or route can run. */\n init?: (ctx: PluginInitContext) => void;\n};\n\nexport type AnyPlugin = EasyPingPlugin<string, SchemaDeclaration>;\n\nexport function definePlugin<TId extends string, TSchema extends SchemaDeclaration>(\n plugin: EasyPingPlugin<TId, TSchema>,\n): EasyPingPlugin<TId, TSchema> {\n return plugin;\n}\n\nexport type ClientFetch = (path: string, init?: RequestInit) => Promise<unknown>;\n\nexport type ClientPlugin<\n TId extends string = string,\n TActions extends Record<string, unknown> = Record<string, unknown>,\n> = {\n id: TId;\n /** Phantom. Links client types to the server plugin so a mismatch is a type error. */\n $InferServerPlugin?: AnyPlugin;\n actions: (fetch: ClientFetch) => TActions;\n};\n\nexport type AnyClientPlugin = ClientPlugin<string, Record<string, unknown>>;\n\nexport function defineClientPlugin<TId extends string, TActions extends Record<string, unknown>>(\n plugin: ClientPlugin<TId, TActions>,\n): ClientPlugin<TId, TActions> {\n return plugin;\n}\n\nexport type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends (\n k: infer I,\n) => void\n ? I\n : never;\n\n/** Naked type parameter, so the conditional distributes over the union. */\ntype ExtractActions<TPlugin> = TPlugin extends { actions: (fetch: never) => infer TActions }\n ? TActions\n : never;\n\n/** Indexes the tuple rather than recursing: a fold blows the instantiation depth limit. */\nexport type ActionsOf<TPlugins extends readonly AnyClientPlugin[]> = UnionToIntersection<\n ExtractActions<TPlugins[number]>\n>;\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/core/errors.ts","../src/core/store.ts"],"names":[],"mappings":"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","file":"chunk-O2PBBL5X.js","sourcesContent":["export class EasyPingError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"EasyPingError\";\n }\n}\n\n/** Thrown when a payload fails its Standard Schema. Developer error. */\nexport class ValidationError extends EasyPingError {\n readonly issues: readonly { message: string; path?: string }[];\n\n constructor(type: string, issues: readonly { message: string; path?: string }[]) {\n super(`Payload for \"${type}\" failed validation: ${issues.map((i) => i.message).join(\"; \")}`);\n this.name = \"ValidationError\";\n this.issues = issues;\n }\n}\n\n/** Thrown at startup for a malformed config. Never at send time. */\nexport class ConfigError extends EasyPingError {\n constructor(message: string) {\n super(message);\n this.name = \"ConfigError\";\n }\n}\n\nexport type Logger = {\n warn: (message: string, meta?: Record<string, unknown>) => void;\n error: (message: string, meta?: Record<string, unknown>) => void;\n};\n\nexport const consoleLogger: Logger = {\n warn: (message, meta) => console.warn(`[easy-ping] ${message}`, meta ?? \"\"),\n error: (message, meta) => console.error(`[easy-ping] ${message}`, meta ?? \"\"),\n};\n","import { toSnakeCase } from \"../schema/declaration\";\nimport { ConfigError } from \"./errors\";\nimport type { FieldDeclaration, SchemaDeclaration, TableDeclaration } from \"./plugin\";\n\nexport type Scalar = string | number | boolean | Date | null;\n\n/** Operators are tagged objects so a bare value is always equality; json columns cannot be filtered. */\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/** Table access for plugins, scoped to the tables the plugin declared. */\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) => Object.hasOwn(value, op));\n\nconst isScalar = (value: unknown): value is Scalar =>\n value === null ||\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\" ||\n value instanceof Date;\n\n/** A forwarded JSON body can carry `{ $ne: … }`; refuse anything that is not a scalar or a known operator. */\nfunction assertCondition(pluginId: string, field: string, condition: unknown): void {\n if (isScalar(condition)) return;\n\n if (isOperator(condition)) {\n const operator = condition as Record<string, unknown>;\n const keys = Object.keys(operator);\n const [op] = keys;\n if (keys.length !== 1 || op === undefined) {\n throw new ConfigError(`plugin \"${pluginId}\" gave field \"${field}\" more than one operator.`);\n }\n const payload = operator[op];\n const valid =\n op === \"in\"\n ? Array.isArray(payload) &&\n payload.every((item) => typeof item === \"string\" || typeof item === \"number\")\n : isScalar(payload);\n if (valid) return;\n }\n\n throw new ConfigError(\n `plugin \"${pluginId}\" passed a non-scalar value for field \"${field}\"; ` +\n \"validate request bodies before querying with them.\",\n );\n}\n\nfunction assertValue(pluginId: string, field: string, spec: FieldDeclaration, value: unknown) {\n if (value === undefined || isScalar(value)) return;\n if (spec.type === \"json\" && typeof value === \"object\") return;\n throw new ConfigError(\n `plugin \"${pluginId}\" passed a non-scalar value for ${spec.type} field \"${field}\".`,\n );\n}\n\n/** Every table and column is checked against the plugin's own declaration. */\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 writable(table: string): TableDeclaration {\n const declaration = resolve(table);\n if (declaration.readOnly) {\n throw new ConfigError(\n `plugin \"${pluginId}\" tried to write \"${table}\", which it declares readOnly.`,\n );\n }\n return declaration;\n }\n\n function notEverything(table: string, where: WhereClause, action: string) {\n if (Object.keys(where).length === 0) {\n throw new ConfigError(\n `plugin \"${pluginId}\" called ${action}(\"${table}\", {}), which would touch every row. ` +\n \"Pass a condition.\",\n );\n }\n }\n\n function checkFields(table: TableDeclaration, fields: Iterable<string>, context: string) {\n for (const field of fields) {\n // hasOwn, not a lookup: `constructor` and `__proto__` are not columns.\n const spec = Object.hasOwn(table.fields, field) ? table.fields[field] : undefined;\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 function checkWhere(table: TableDeclaration, where: WhereClause) {\n checkFields(table, Object.keys(where), \"where\");\n for (const [field, condition] of Object.entries(where)) {\n assertCondition(pluginId, field, condition);\n }\n }\n\n function checkRow(table: TableDeclaration, row: Record<string, unknown>, context: string) {\n checkFields(table, Object.keys(row), context);\n for (const [field, value] of Object.entries(row)) {\n const spec = table.fields[field];\n if (spec) assertValue(pluginId, field, spec, value);\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 // json must reach postgres-js as text.\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 checkWhere(declaration, 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; plugins read camelCase.\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 const type = declaration.fields[field]?.type;\n // Drivers differ on jsonb (parsed or text) and timestamptz (Date or text).\n if (type === \"json\" && typeof value === \"string\") mapped[field] = JSON.parse(value);\n else if (type === \"date\" && typeof value === \"string\") mapped[field] = new Date(value);\n // MySQL and SQLite have no boolean column; they hand back 0 and 1.\n else if (type === \"boolean\" && typeof value === \"number\") mapped[field] = value !== 0;\n else mapped[field] = value;\n }\n return mapped;\n }) as never;\n },\n\n async insert(table, rows) {\n const declaration = writable(table);\n for (const row of rows) checkRow(declaration, 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 = writable(table);\n for (const row of rows) checkRow(declaration, 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 = writable(table);\n checkWhere(declaration, where);\n notEverything(table, where, \"update\");\n checkRow(declaration, set, \"update\");\n return storage.updateRows(qualified(declaration), where, serialize(declaration, set));\n },\n\n async remove(table, where) {\n const declaration = writable(table);\n checkWhere(declaration, where);\n notEverything(table, where, \"remove\");\n return storage.deleteRows(qualified(declaration), where);\n },\n };\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/core/signals.ts"],"names":[],"mappings":"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","file":"chunk-ORZKH224.js","sourcesContent":["/**\n * The wake-up seam. A publish carries no payload: it says \"something changed\n * on this channel, go look\", so losing one costs latency, never data.\n * RFC 0006 §4C.\n */\nexport type Signals = {\n /** True when a publish in one process reaches subscribers in another. */\n readonly crossProcess: boolean;\n publish(channel: string): void;\n subscribe(channel: string, handler: () => void): () => void;\n close?(): Promise<void> | void;\n};\n\nexport const DELIVERIES_CHANNEL = \"deliveries\";\nexport const inboxChannel = (userId: string) => `inbox:${userId}`;\n\n/** In-process only. The default, and exactly right on SQLite or a single replica. */\nexport function createMemorySignals(): Signals {\n const handlers = new Map<string, Set<() => void>>();\n\n return {\n crossProcess: false,\n publish(channel) {\n const set = handlers.get(channel);\n if (!set) return;\n for (const handler of [...set]) {\n queueMicrotask(() => {\n try {\n handler();\n } catch {\n // a subscriber's failure is its own problem\n }\n });\n }\n },\n subscribe(channel, handler) {\n let set = handlers.get(channel);\n if (!set) {\n set = new Set();\n handlers.set(channel, set);\n }\n set.add(handler);\n return () => {\n set?.delete(handler);\n if (set?.size === 0) handlers.delete(channel);\n };\n },\n };\n}\n\n/**\n * Wraps a cross-process transport (LISTEN/NOTIFY, a change stream) so local\n * subscribers still hear local publishes even if the transport drops them,\n * and a transport failure degrades to in-process rather than to silence.\n */\nexport function createBridgedSignals(\n transport: {\n publish(channel: string): Promise<void> | void;\n subscribe(channel: string, handler: () => void): Promise<() => void> | (() => void);\n },\n onError: (error: unknown) => void,\n): Signals {\n const local = createMemorySignals();\n const remoteUnsubscribes = new Map<string, Promise<() => void>>();\n const counts = new Map<string, number>();\n\n return {\n crossProcess: true,\n publish(channel) {\n local.publish(channel);\n Promise.resolve()\n .then(() => transport.publish(channel))\n .catch(onError);\n },\n subscribe(channel, handler) {\n const unsubscribeLocal = local.subscribe(channel, handler);\n counts.set(channel, (counts.get(channel) ?? 0) + 1);\n\n if (!remoteUnsubscribes.has(channel)) {\n remoteUnsubscribes.set(\n channel,\n Promise.resolve()\n .then(() => transport.subscribe(channel, () => local.publish(channel)))\n .catch((error) => {\n onError(error);\n return () => {};\n }),\n );\n }\n\n return () => {\n unsubscribeLocal();\n const remaining = (counts.get(channel) ?? 1) - 1;\n if (remaining > 0) {\n counts.set(channel, remaining);\n return;\n }\n counts.delete(channel);\n const pending = remoteUnsubscribes.get(channel);\n remoteUnsubscribes.delete(channel);\n pending?.then((unsubscribe) => unsubscribe()).catch(onError);\n };\n },\n };\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/core/base64url.ts"],"names":[],"mappings":";AAEO,SAAS,gBAAgB,KAAA,EAAoC;AAClE,EAAA,MAAM,KAAA,GAAQ,OAAO,KAAA,KAAU,QAAA,GAAW,IAAI,WAAA,EAAY,CAAE,MAAA,CAAO,KAAK,CAAA,GAAI,KAAA;AAC5E,EAAA,IAAI,MAAA,GAAS,EAAA;AACb,EAAA,KAAA,MAAW,IAAA,IAAQ,KAAA,EAAO,MAAA,IAAU,MAAA,CAAO,aAAa,IAAI,CAAA;AAC5D,EAAA,OAAO,IAAA,CAAK,MAAM,CAAA,CAAE,OAAA,CAAQ,KAAA,EAAO,GAAG,CAAA,CAAE,OAAA,CAAQ,KAAA,EAAO,GAAG,CAAA,CAAE,OAAA,CAAQ,OAAO,EAAE,CAAA;AAC/E;AAGO,SAAS,qBAAqB,KAAA,EAA+C;AAClF,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,OAAA,CAAQ,IAAA,EAAM,GAAG,CAAA,CAAE,OAAA,CAAQ,IAAA,EAAM,GAAG,CAAC,CAAA;AAC/D,IAAA,OAAO,UAAA,CAAW,KAAK,MAAA,EAAQ,CAAC,SAAS,IAAA,CAAK,UAAA,CAAW,CAAC,CAAC,CAAA;AAAA,EAC7D,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AAEO,SAAS,gBAAgB,KAAA,EAA8B;AAC5D,EAAA,MAAM,KAAA,GAAQ,qBAAqB,KAAK,CAAA;AACxC,EAAA,OAAO,QAAQ,IAAI,WAAA,EAAY,CAAE,MAAA,CAAO,KAAK,CAAA,GAAI,IAAA;AACnD","file":"chunk-PTGHMCCG.js","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"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/core/html.ts"],"names":[],"mappings":";AAAA,IAAM,OAAA,GAAkC;AAAA,EACtC,GAAA,EAAK,OAAA;AAAA,EACL,GAAA,EAAK,MAAA;AAAA,EACL,GAAA,EAAK,MAAA;AAAA,EACL,GAAA,EAAK,QAAA;AAAA,EACL,GAAA,EAAK;AACP,CAAA;AAGO,IAAM,UAAA,GAAa,CAAC,KAAA,KACzB,MAAA,CAAO,SAAS,EAAE,CAAA,CAAE,OAAA,CAAQ,UAAA,EAAY,CAAC,IAAA,KAAS,OAAA,CAAQ,IAAI,KAAK,IAAI","file":"chunk-R3BE67L7.js","sourcesContent":["const ESCAPES: Record<string, string> = {\n \"&\": \"&amp;\",\n \"<\": \"&lt;\",\n \">\": \"&gt;\",\n '\"': \"&quot;\",\n \"'\": \"&#39;\",\n};\n\n/** For template literals: anything a user typed goes through this, or their HTML ships from your domain. */\nexport const escapeHtml = (value: unknown): string =>\n String(value ?? \"\").replace(/[&<>\"']/g, (char) => ESCAPES[char] ?? char);\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/providers/telegram/index.ts"],"names":[],"mappings":"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","file":"chunk-SSHVZHSJ.js","sourcesContent":["export type TelegramButton = { text: string; url: string };\n\nexport type TelegramMessage = {\n chatId: string;\n /** Telegram HTML: <b>, <i>, <a href>, <code>, <pre>. Escape user text with escapeHtml(). */\n text: string;\n buttons?: readonly TelegramButton[] | undefined;\n signal?: AbortSignal | undefined;\n};\n\nexport type TelegramSendResult =\n | { ok: true; messageId: number }\n | {\n ok: false;\n error: string;\n /** Worth another attempt: rate limit, 5xx, network. */\n retryable: boolean;\n /** The chat can never receive again (user blocked the bot, chat deleted). Prune it. */\n gone: boolean;\n retryAfterSeconds?: number | undefined;\n };\n\n/** The slice of a Bot API update the plugin reads. Everything else passes through untouched. */\nexport type TelegramUpdate = {\n update_id: number;\n message?: {\n text?: string;\n chat: { id: number | string; type: string; username?: string; title?: string };\n from?: { id: number; username?: string; first_name?: string };\n };\n my_chat_member?: {\n chat: { id: number | string; type: string; username?: string; title?: string };\n new_chat_member: { status: string };\n };\n};\n\nexport type TelegramProvider = {\n name: string;\n send(message: TelegramMessage): Promise<TelegramSendResult>;\n /** Long-polls for updates. For development or hosts without a public URL. */\n getUpdates(options: {\n offset?: number | undefined;\n timeoutSeconds?: number | undefined;\n signal?: AbortSignal | undefined;\n }): Promise<TelegramUpdate[]>;\n /** Points Telegram at your webhook route. Run once per deployment URL. */\n setWebhook(url: string, secretToken: string): Promise<void>;\n deleteWebhook(): Promise<void>;\n getMe(): Promise<{ id: number; username: string }>;\n};\n\nexport type TelegramBotOptions = {\n /** From @BotFather. Never appears in errors or logs. */\n token: string;\n fetch?: typeof globalThis.fetch | undefined;\n /** Default https://api.telegram.org. A local fake for tests. */\n apiBase?: string | undefined;\n timeoutMs?: number | undefined;\n};\n\ntype ApiResponse<T> =\n | { ok: true; result: T }\n | { ok: false; description?: string; error_code?: number; parameters?: { retry_after?: number } };\n\n/** Deep link that opens the bot with a start parameter. Codes are [A-Za-z0-9_-], at most 64 chars. */\nexport const telegramStartLink = (botUsername: string, code: string) =>\n `https://t.me/${botUsername.replace(/^@/, \"\")}?start=${encodeURIComponent(code)}`;\n\n/** Bot API over fetch, so it runs on edge runtimes. Failures are classified, never thrown, on send. */\nexport function telegramBot(options: TelegramBotOptions): TelegramProvider {\n const doFetch = options.fetch ?? globalThis.fetch;\n const base = `${(options.apiBase ?? \"https://api.telegram.org\").replace(/\\/$/, \"\")}/bot${options.token}`;\n const timeoutMs = options.timeoutMs ?? 10_000;\n const redact = (text: string) => text.split(options.token).join(\"<token>\");\n\n async function call<T>(\n method: string,\n body: Record<string, unknown>,\n signal?: AbortSignal,\n budgetMs = timeoutMs,\n ): Promise<{ status: number; data: ApiResponse<T> }> {\n const timeout = AbortSignal.timeout(budgetMs);\n const merged = signal ? AbortSignal.any([signal, timeout]) : timeout;\n const response = await doFetch(`${base}/${method}`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(body),\n signal: merged,\n });\n const data = (await response.json().catch(() => ({ ok: false }))) as ApiResponse<T>;\n return { status: response.status, data };\n }\n\n async function must<T>(method: string, body: Record<string, unknown>): Promise<T> {\n const { data } = await call<T>(method, body);\n if (!data.ok)\n throw new Error(redact(`telegram ${method} failed: ${data.description ?? \"unknown\"}`));\n return data.result;\n }\n\n return {\n name: \"telegram\",\n\n async send(message) {\n const body: Record<string, unknown> = {\n chat_id: message.chatId,\n text: message.text,\n parse_mode: \"HTML\",\n link_preview_options: { is_disabled: true },\n };\n if (message.buttons?.length) {\n body.reply_markup = {\n inline_keyboard: [\n message.buttons.map((button) => ({ text: button.text, url: button.url })),\n ],\n };\n }\n\n let status: number;\n let data: ApiResponse<{ message_id: number }>;\n try {\n ({ status, data } = await call<{ message_id: number }>(\n \"sendMessage\",\n body,\n message.signal,\n ));\n } catch (error) {\n const text = error instanceof Error ? error.message : String(error);\n return { ok: false, error: redact(text), retryable: true, gone: false };\n }\n\n if (data.ok) return { ok: true, messageId: data.result.message_id };\n\n const description = redact(data.description ?? `HTTP ${status}`);\n const code = data.error_code ?? status;\n if (code === 429) {\n return {\n ok: false,\n error: description,\n retryable: true,\n gone: false,\n retryAfterSeconds: data.parameters?.retry_after,\n };\n }\n // 403: the user blocked the bot or was deactivated. 400 \"chat not found\": deleted or never existed.\n const gone =\n code === 403 ||\n (code === 400 && /chat not found|user is deactivated|bot was kicked/i.test(description));\n return { ok: false, error: description, retryable: code >= 500, gone };\n },\n\n async getUpdates({ offset, timeoutSeconds = 25, signal }) {\n // Telegram holds the request open for `timeout` seconds; the budget must outlive it.\n const { data } = await call<TelegramUpdate[]>(\n \"getUpdates\",\n {\n ...(offset === undefined ? {} : { offset }),\n timeout: timeoutSeconds,\n allowed_updates: [\"message\", \"my_chat_member\"],\n },\n signal,\n (timeoutSeconds + 10) * 1000,\n );\n if (!data.ok)\n throw new Error(redact(`telegram getUpdates failed: ${data.description ?? \"unknown\"}`));\n return data.result;\n },\n\n setWebhook: async (url, secretToken) => {\n await must(\"setWebhook\", {\n url,\n secret_token: secretToken,\n allowed_updates: [\"message\", \"my_chat_member\"],\n });\n },\n deleteWebhook: async () => {\n await must(\"deleteWebhook\", {});\n },\n getMe: () => must(\"getMe\", {}),\n };\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/schema/render-mysql.ts"],"names":[],"mappings":"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","file":"chunk-UM2L26PV.js","sourcesContent":["import type { FieldDeclaration, SchemaDeclaration, TableDeclaration } from \"../core/plugin\";\nimport { toSnakeCase } from \"./declaration\";\n\nconst quote = (value: string) => `\\`${value.split(\"`\").join(\"``\")}\\``;\n\n/** InnoDB caps a key at 3072 bytes: 768 utf8mb4 chars for one column, 255 each for three. TEXT cannot be a key or carry a default. */\nfunction stringType(field: string, table: TableDeclaration, spec: FieldDeclaration): string {\n const keys = [\n ...(table.primaryKey ? [table.primaryKey] : []),\n ...(table.indexes ?? []).map((index) => index.on),\n ].filter((columns) => columns.includes(field));\n\n if (keys.some((columns) => columns.length > 1)) return \"VARCHAR(255)\";\n if (keys.length > 0) return \"VARCHAR(768)\";\n if (spec.default !== undefined) return \"VARCHAR(255)\";\n return \"TEXT\";\n}\n\nfunction columnType(field: string, table: TableDeclaration, spec: FieldDeclaration): string {\n switch (spec.type) {\n case \"string\":\n return stringType(field, table, spec);\n case \"number\":\n return \"INT\";\n case \"boolean\":\n return \"TINYINT(1)\";\n case \"date\":\n return \"DATETIME(3)\";\n case \"json\":\n return \"JSON\";\n }\n}\n\nconst defaultClause = (spec: FieldDeclaration): string => {\n if (spec.defaultNow) return \" DEFAULT CURRENT_TIMESTAMP(3)\";\n if (spec.default === undefined) return \"\";\n if (typeof spec.default === \"string\") return ` DEFAULT '${spec.default.replace(/'/g, \"''\")}'`;\n if (typeof spec.default === \"boolean\") return ` DEFAULT ${spec.default ? 1 : 0}`;\n return ` DEFAULT ${spec.default}`;\n};\n\n/** One column's definition, as CREATE TABLE and ALTER TABLE ... ADD COLUMN both need it. */\nexport function mysqlColumnDefinition(\n field: string,\n table: TableDeclaration,\n spec: FieldDeclaration,\n): string {\n const nullability = spec.required ? \" NOT NULL\" : \"\";\n return `${quote(toSnakeCase(field))} ${columnType(field, table, spec)}${nullability}${defaultClause(spec)}`;\n}\n\n/** MySQL DDL, one CREATE TABLE IF NOT EXISTS per table with indexes inline (no CREATE INDEX IF NOT EXISTS). */\nexport function renderMysqlDdl(schema: SchemaDeclaration, prefix = \"\"): string[] {\n const statements: string[] = [];\n\n for (const table of Object.values(schema)) {\n const lines = Object.entries(table.fields).map(\n ([field, spec]) => ` ${mysqlColumnDefinition(field, table, spec)}`,\n );\n\n if (table.primaryKey?.length) {\n lines.push(\n ` PRIMARY KEY (${table.primaryKey.map((f) => quote(toSnakeCase(f))).join(\", \")})`,\n );\n }\n\n for (const index of table.indexes ?? []) {\n const name = quote(prefix + (index.name ?? `${table.tableName}_${index.on.join(\"_\")}_idx`));\n const columns = index.on.map((f) => quote(toSnakeCase(f))).join(\", \");\n lines.push(` ${index.unique ? \"UNIQUE \" : \"\"}INDEX ${name} (${columns})`);\n }\n\n statements.push(\n `CREATE TABLE IF NOT EXISTS ${quote(prefix + table.tableName)} (\\n${lines.join(\",\\n\")}\\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`,\n );\n }\n\n return statements;\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/client/index.ts"],"names":[],"mappings":"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","file":"client.cjs","sourcesContent":["export type NotificationView = {\n id: string;\n userId: string;\n type: string;\n payload: unknown;\n actorId: string | null;\n groupKey: string | null;\n seenAt: string | null;\n readAt: string | null;\n createdAt: string;\n};\n\nexport type NotifyState = {\n notifications: readonly NotificationView[];\n /** Badge count — cleared when the dropdown opens. Server-authoritative. */\n unseenCount: number;\n /** Items still bold. Derived from what is loaded, not a server total. */\n unreadCount: number;\n nextCursor: string | null;\n isLoading: boolean;\n error: Error | null;\n};\n\nexport type Transport = \"auto\" | \"sse\" | \"poll\";\n\nexport type TransportStatus = {\n /** \"leader\" holds this browser's single connection; \"follower\" mirrors it; \"solo\" has no Web Locks. */\n role: \"leader\" | \"follower\" | \"solo\";\n /** What the leader is using right now. */\n transport: \"sse\" | \"poll\";\n /** True while the event stream has said `ready` and not since closed. */\n connected: boolean;\n};\n\ntype LockManagerLike = {\n request(\n name: string,\n options: { mode: \"exclusive\"; signal?: AbortSignal },\n callback: () => Promise<void>,\n ): Promise<void>;\n};\n\ntype ChannelLike = {\n postMessage(message: unknown): void;\n addEventListener(type: \"message\", handler: (event: { data: unknown }) => void): void;\n removeEventListener(type: \"message\", handler: (event: { data: unknown }) => void): void;\n close(): void;\n};\n\ntype MessageTargetLike = {\n addEventListener(type: string, handler: (event: { data?: unknown }) => void): void;\n removeEventListener(type: string, handler: (event: { data?: unknown }) => void): void;\n};\n\nexport type NotifyClientOptions = {\n baseUrl?: string | undefined;\n fetch?: typeof globalThis.fetch | undefined;\n limit?: number | undefined;\n /** Poll interval while the user is active and no stream is connected. Default 15 s. */\n pollIntervalMs?: number | undefined;\n /** Ceiling the idle backoff grows to. Default 10 min. */\n maxPollIntervalMs?: number | undefined;\n /** Poll interval while the event stream is connected: a safety net. Default 5 min. */\n safetyNetMs?: number | undefined;\n /** How long after user input polling stays at the base interval. Default 60 s. */\n activeWindowMs?: number | undefined;\n /** \"auto\" streams when a DOM is present, \"poll\" never streams, \"sse\" always tries. */\n transport?: Transport | undefined;\n /** Injectable so the poller can be tested without a DOM. */\n isDocumentHidden?: (() => boolean) | undefined;\n /** Web Locks, for one connection per browser. Defaults to navigator.locks; null disables. */\n locks?: LockManagerLike | null | undefined;\n /** Cross-tab channel factory. Defaults to BroadcastChannel; null disables. */\n channel?: (() => ChannelLike) | null | undefined;\n /** Where push relays arrive. Defaults to navigator.serviceWorker; null disables. */\n serviceWorker?: MessageTargetLike | null | undefined;\n /** Where user activity is observed. Defaults to window; null disables. */\n activityTarget?: MessageTargetLike | null | undefined;\n now?: (() => number) | undefined;\n /**\n * Separates browsers' tab groups when identity is not a cookie: pass the\n * current user's id (or session id) so tabs signed in as different users\n * never share a leader or mirror each other's inbox.\n */\n scope?: string | undefined;\n};\n\nconst INITIAL: NotifyState = {\n notifications: [],\n unseenCount: 0,\n unreadCount: 0,\n nextCursor: null,\n isLoading: true,\n error: null,\n};\n\nconst countUnread = (rows: readonly NotificationView[]) =>\n rows.reduce((total, row) => (row.readAt === null ? total + 1 : total), 0);\n\n/** A stream that dies this fast, repeatedly, is being cut by the platform: stop trying. */\nconst SHORT_STREAM_MS = 15_000;\nconst SHORT_STREAMS_BEFORE_GIVING_UP = 3;\n\ntype TabMessage =\n | { type: \"hello\" }\n | { type: \"state\"; state: NotifyState }\n | { type: \"mutated\" }\n | { type: \"leader\" };\n\nexport function createNotifyClient(options: NotifyClientOptions = {}) {\n const baseUrl = (options.baseUrl ?? \"/api/notifications\").replace(/\\/$/, \"\");\n const doFetch = options.fetch ?? ((...args) => globalThis.fetch(...args));\n const limit = options.limit ?? 20;\n const pollIntervalMs = options.pollIntervalMs ?? 15_000;\n const maxPollIntervalMs = options.maxPollIntervalMs ?? 10 * 60_000;\n const safetyNetMs = options.safetyNetMs ?? 5 * 60_000;\n const activeWindowMs = options.activeWindowMs ?? 60_000;\n const now = options.now ?? (() => Date.now());\n const groupKey = options.scope ? `${baseUrl}#${options.scope}` : baseUrl;\n const hasDom = typeof window !== \"undefined\" && typeof document !== \"undefined\";\n const transport: Transport = options.transport ?? \"auto\";\n const wantsStream = transport === \"sse\" || (transport === \"auto\" && hasDom);\n const isHidden =\n options.isDocumentHidden ??\n (() => typeof document !== \"undefined\" && document.visibilityState === \"hidden\");\n\n const locks =\n options.locks === undefined\n ? typeof navigator !== \"undefined\"\n ? ((navigator as { locks?: LockManagerLike }).locks ?? null)\n : null\n : options.locks;\n const makeChannel =\n options.channel === undefined\n ? typeof BroadcastChannel !== \"undefined\"\n ? () => new BroadcastChannel(`easy-ping:${groupKey}`) as unknown as ChannelLike\n : null\n : options.channel;\n const serviceWorker =\n options.serviceWorker === undefined\n ? typeof navigator !== \"undefined\"\n ? ((navigator as { serviceWorker?: MessageTargetLike }).serviceWorker ?? null)\n : null\n : options.serviceWorker;\n const activityTarget =\n options.activityTarget === undefined\n ? hasDom\n ? (window as unknown as MessageTargetLike)\n : null\n : options.activityTarget;\n\n let state: NotifyState = INITIAL;\n const listeners = new Set<(state: NotifyState) => void>();\n\n let role: TransportStatus[\"role\"] = \"solo\";\n let running = false;\n let channel: ChannelLike | undefined;\n let releaseLock: (() => void) | undefined;\n let lockAbort: AbortController | undefined;\n\n let timer: ReturnType<typeof setTimeout> | undefined;\n let consecutiveFailures = 0;\n let idleTicks = 0;\n let activeUntil = 0;\n let lastRefreshAt = 0;\n let refreshing: Promise<void> | undefined;\n let refreshAgain = false;\n\n let streamAbort: AbortController | undefined;\n let streamConnected = false;\n let streamDisabled = !wantsStream;\n let shortStreams = 0;\n let streamFailures = 0;\n\n // Bumped per mutation so an in-flight poll cannot revert an optimistic update.\n let generation = 0;\n\n // A follower's own optimistic changes, kept until the leader's state reflects them.\n const localReads = new Set<string>();\n let localSeen = false;\n\n const stamp = () => new Date().toISOString();\n\n function emit() {\n for (const listener of listeners) listener(state);\n }\n\n function set(patch: Partial<NotifyState>) {\n state = { ...state, ...patch };\n state = { ...state, unreadCount: countUnread(state.notifications) };\n emit();\n if (role === \"leader\") post({ type: \"state\", state });\n }\n\n function post(message: TabMessage) {\n try {\n channel?.postMessage(message);\n } catch {\n // channel closed\n }\n }\n\n async function request<T>(path: string, init?: RequestInit): Promise<T> {\n const response = await doFetch(`${baseUrl}${path}`, {\n ...init,\n headers: { \"content-type\": \"application/json\", ...init?.headers },\n });\n if (!response.ok)\n throw new Error(`${init?.method ?? \"GET\"} ${path} failed: ${response.status}`);\n return (await response.json()) as T;\n }\n\n async function fetchPage(): Promise<void> {\n const startedAt = generation;\n\n const page = await request<{\n notifications: NotificationView[];\n nextCursor: string | null;\n unseenCount?: number;\n }>(`/?limit=${limit}`);\n\n const unseenCount = page.unseenCount ?? (await request<{ unseen: number }>(\"/count\")).unseen;\n\n // A mutation landed while this was in flight; the response is stale.\n if (generation !== startedAt) return;\n\n set({\n notifications: page.notifications,\n nextCursor: page.nextCursor,\n unseenCount,\n isLoading: false,\n error: null,\n });\n }\n\n /** Coalesces overlapping calls: a second request while one is in flight runs once after it. */\n function refresh(): Promise<void> {\n if (refreshing) {\n refreshAgain = true;\n return refreshing;\n }\n refreshing = (async () => {\n try {\n do {\n refreshAgain = false;\n await fetchPage();\n lastRefreshAt = now();\n } while (refreshAgain);\n } finally {\n refreshing = undefined;\n }\n })();\n return refreshing;\n }\n\n let loadingMore: Promise<void> | undefined;\n\n async function loadMore(): Promise<void> {\n if (!state.nextCursor) return;\n\n // Share the in-flight request so two clicks cannot append the same page twice.\n if (loadingMore) return loadingMore;\n\n const cursor = state.nextCursor;\n\n loadingMore = (async () => {\n try {\n const page = await request<{\n notifications: NotificationView[];\n nextCursor: string | null;\n }>(`/?limit=${limit}&cursor=${encodeURIComponent(cursor)}`);\n\n const known = new Set(state.notifications.map((row) => row.id));\n\n set({\n notifications: [\n ...state.notifications,\n ...page.notifications.filter((row) => !known.has(row.id)),\n ],\n nextCursor: page.nextCursor,\n });\n } finally {\n loadingMore = undefined;\n }\n })();\n\n return loadingMore;\n }\n\n /** Applies a patch immediately and reverts it if the request fails. */\n async function optimistic(\n apply: (rows: readonly NotificationView[]) => readonly NotificationView[],\n patch: Partial<NotifyState>,\n send: () => Promise<unknown>,\n ): Promise<void> {\n const previous = state;\n generation += 1;\n set({ notifications: apply(state.notifications), ...patch });\n\n try {\n await send();\n if (role === \"follower\") post({ type: \"mutated\" });\n } catch (error) {\n generation += 1;\n state = previous;\n emit();\n throw error;\n }\n }\n\n async function markAsRead(id: string): Promise<void> {\n if (role === \"follower\") localReads.add(id);\n await optimistic(\n (rows) =>\n rows.map((row) => (row.id === id ? { ...row, readAt: row.readAt ?? stamp() } : row)),\n {},\n () => request(\"/read\", { method: \"POST\", body: JSON.stringify({ ids: [id] }) }),\n ).catch((error) => {\n localReads.delete(id);\n throw error;\n });\n }\n\n async function markAllRead(): Promise<void> {\n if (role === \"follower\") for (const row of state.notifications) localReads.add(row.id);\n await optimistic(\n (rows) => rows.map((row) => ({ ...row, readAt: row.readAt ?? stamp() })),\n {},\n () => request(\"/read-all\", { method: \"POST\" }),\n ).catch((error) => {\n localReads.clear();\n throw error;\n });\n }\n\n async function markSeen(): Promise<void> {\n // Only what the user could have seen: no cutoff would mark newer arrivals seen too.\n const newest = state.notifications[0]?.createdAt;\n if (role === \"follower\") localSeen = true;\n\n await optimistic(\n (rows) => rows.map((row) => ({ ...row, seenAt: row.seenAt ?? stamp() })),\n { unseenCount: 0 },\n () =>\n request(\"/seen\", {\n method: \"POST\",\n body: JSON.stringify(newest ? { before: newest } : {}),\n }),\n ).catch((error) => {\n localSeen = false;\n throw error;\n });\n }\n\n // ---- follower: mirror the leader, keep own optimistic changes until confirmed ----\n\n function applyRemoteState(remote: NotifyState) {\n const notifications = remote.notifications.map((row) => {\n if (!localReads.has(row.id)) return row;\n if (row.readAt) {\n localReads.delete(row.id);\n return row;\n }\n return { ...row, readAt: stamp() };\n });\n if (localSeen && remote.unseenCount === 0) localSeen = false;\n state = {\n ...remote,\n notifications,\n unseenCount: localSeen ? 0 : remote.unseenCount,\n unreadCount: countUnread(notifications),\n };\n emit();\n }\n\n function onTabMessage(event: { data: unknown }) {\n const message = event.data as TabMessage | null;\n if (!message || typeof message !== \"object\") return;\n if (role === \"leader\") {\n if (message.type === \"hello\") post({ type: \"state\", state });\n else if (message.type === \"mutated\") void refresh().catch(() => {});\n return;\n }\n if (message.type === \"state\") applyRemoteState(message.state);\n }\n\n // ---- leader / solo: polling with activity-shaped backoff ----\n\n function schedule(delay: number) {\n clearTimeout(timer);\n timer = setTimeout(tick, delay);\n }\n\n function nextDelay(): number {\n if (consecutiveFailures > 0) {\n return Math.min(pollIntervalMs * 2 ** consecutiveFailures, maxPollIntervalMs);\n }\n if (streamConnected) return safetyNetMs;\n if (now() < activeUntil) return pollIntervalMs;\n idleTicks += 1;\n return Math.min(pollIntervalMs * 2 ** idleTicks, maxPollIntervalMs);\n }\n\n async function tick() {\n if (!running) return;\n // A hidden tab is not looking at the bell.\n if (isHidden()) {\n schedule(pollIntervalMs);\n return;\n }\n\n try {\n await refresh();\n consecutiveFailures = 0;\n } catch (error) {\n consecutiveFailures += 1;\n set({ isLoading: false, error: error instanceof Error ? error : new Error(String(error)) });\n }\n\n if (running) schedule(nextDelay());\n }\n\n /** User input or a returning tab: poll fast again, and catch up now if the last refresh is stale. */\n function onActivity() {\n if (!running || role === \"follower\") return;\n activeUntil = now() + activeWindowMs;\n idleTicks = 0;\n if (!isHidden() && now() - lastRefreshAt > pollIntervalMs) {\n void refresh().catch(() => {});\n schedule(nextDelay());\n }\n }\n\n const onVisibility = () => {\n if (!isHidden()) onActivity();\n };\n\n const ACTIVITY_EVENTS = [\"focus\", \"pointerdown\", \"keydown\", \"online\"];\n\n // ---- leader / solo: the event stream ----\n\n function changed() {\n if (!running) return;\n void refresh().catch(() => {});\n }\n\n async function readStream(signal: AbortSignal): Promise<void> {\n const openedAt = now();\n const response = await doFetch(`${baseUrl}/events`, {\n headers: { accept: \"text/event-stream\" },\n signal,\n });\n if (!response.ok) throw new Error(`GET /events failed: ${response.status}`);\n if (!response.body) {\n // This fetch cannot stream a body (React Native's built-in one, some polyfills): poll instead.\n streamDisabled = true;\n schedule(nextDelay());\n return;\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n let eventName = \"\";\n\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n let newline = buffer.indexOf(\"\\n\");\n while (newline >= 0) {\n const line = buffer.slice(0, newline).replace(/\\r$/, \"\");\n buffer = buffer.slice(newline + 1);\n if (line === \"\") {\n if (eventName === \"ready\") {\n streamConnected = true;\n streamFailures = 0;\n schedule(nextDelay());\n } else if (eventName === \"changed\") changed();\n eventName = \"\";\n } else if (line.startsWith(\"event:\")) eventName = line.slice(6).trim();\n newline = buffer.indexOf(\"\\n\");\n }\n }\n } finally {\n streamConnected = false;\n if (now() - openedAt < SHORT_STREAM_MS) shortStreams += 1;\n else shortStreams = 0;\n }\n }\n\n const isFollower = () => role === \"follower\";\n\n async function streamLoop() {\n while (running && !streamDisabled && !isFollower()) {\n streamAbort = new AbortController();\n try {\n await readStream(streamAbort.signal);\n } catch {\n if (!running) return;\n streamFailures += 1;\n }\n if (!running || isFollower() || streamDisabled) return;\n if (shortStreams >= SHORT_STREAMS_BEFORE_GIVING_UP) {\n // The platform cuts streams too fast for them to be worth it here.\n streamDisabled = true;\n schedule(nextDelay());\n return;\n }\n await new Promise((resolve) =>\n setTimeout(resolve, Math.min(3_000 * 2 ** streamFailures, 60_000)),\n );\n }\n }\n\n // ---- roles ----\n\n function becomeLeader() {\n role = \"leader\";\n post({ type: \"leader\" });\n post({ type: \"state\", state });\n void tick();\n if (!streamDisabled) void streamLoop();\n }\n\n function becomeFollower() {\n role = \"follower\";\n post({ type: \"hello\" });\n }\n\n function acquireLeadership() {\n if (!locks || !channel) {\n role = \"solo\";\n void tick();\n if (!streamDisabled) void streamLoop();\n return;\n }\n becomeFollower();\n lockAbort = new AbortController();\n locks\n .request(\n `easy-ping:leader:${groupKey}`,\n { mode: \"exclusive\", signal: lockAbort.signal },\n () =>\n new Promise<void>((resolve) => {\n releaseLock = resolve;\n if (running) becomeLeader();\n else resolve();\n }),\n )\n .catch(() => {\n // aborted on stop, or Web Locks refused; either way no leadership here\n });\n }\n\n function start() {\n running = true;\n channel = makeChannel ? makeChannel() : undefined;\n channel?.addEventListener(\"message\", onTabMessage);\n serviceWorker?.addEventListener(\"message\", onRelay);\n if (activityTarget) {\n for (const name of ACTIVITY_EVENTS) activityTarget.addEventListener(name, onActivity);\n }\n if (typeof document !== \"undefined\") {\n document.addEventListener(\"visibilitychange\", onVisibility);\n }\n activeUntil = now() + activeWindowMs;\n acquireLeadership();\n }\n\n function stop() {\n running = false;\n clearTimeout(timer);\n timer = undefined;\n streamAbort?.abort();\n streamAbort = undefined;\n lockAbort?.abort();\n lockAbort = undefined;\n releaseLock?.();\n releaseLock = undefined;\n channel?.removeEventListener(\"message\", onTabMessage);\n channel?.close();\n channel = undefined;\n serviceWorker?.removeEventListener(\"message\", onRelay);\n if (activityTarget) {\n for (const name of ACTIVITY_EVENTS) activityTarget.removeEventListener(name, onActivity);\n }\n if (typeof document !== \"undefined\") {\n document.removeEventListener(\"visibilitychange\", onVisibility);\n }\n role = \"solo\";\n }\n\n // ---- signals from outside: the service worker relay and the piggyback header ----\n\n function onRelay(event: { data?: unknown }) {\n const data = event.data as { source?: unknown; type?: unknown } | null;\n if (data?.source === \"easy-ping\" && data.type === \"changed\") changed();\n }\n\n let lastInboxVersion: string | null = null;\n\n // Only the app's own origin may steer the bell; a third-party response cannot.\n const sameOrigin = (response: Response) => {\n if (!response.url || typeof location === \"undefined\") return true;\n try {\n return new URL(response.url).origin === location.origin;\n } catch {\n return false;\n }\n };\n\n /**\n * Wraps your app's fetch so responses carrying the inbox-version header\n * refresh the bell when the version moves. Active users then never need a\n * dedicated poll. RFC 0006 idea 1.\n */\n function instrument(inner: typeof globalThis.fetch): typeof globalThis.fetch {\n return (async (...args: Parameters<typeof globalThis.fetch>) => {\n const response = await inner(...args);\n const version = sameOrigin(response) ? response.headers.get(\"x-easy-ping-inbox\") : null;\n if (version !== null && version !== lastInboxVersion) {\n const first = lastInboxVersion === null;\n lastInboxVersion = version;\n if (!first && role !== \"follower\") changed();\n }\n return response;\n }) as typeof globalThis.fetch;\n }\n\n function subscribe(listener: (state: NotifyState) => void): () => void {\n listeners.add(listener);\n listener(state);\n\n // One transport regardless of how many components subscribe.\n if (listeners.size === 1) start();\n\n return () => {\n listeners.delete(listener);\n if (listeners.size === 0) stop();\n };\n }\n\n return {\n subscribe,\n refresh,\n loadMore,\n markAsRead,\n markAllRead,\n markSeen,\n instrument,\n getState: () => state,\n getTransport: (): TransportStatus => ({\n role,\n transport: streamDisabled ? \"poll\" : \"sse\",\n connected: streamConnected,\n }),\n };\n}\n\nexport type NotifyClient = ReturnType<typeof createNotifyClient>;\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/client/index.ts"],"names":[],"mappings":"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","file":"client.js","sourcesContent":["export type NotificationView = {\n id: string;\n userId: string;\n type: string;\n payload: unknown;\n actorId: string | null;\n groupKey: string | null;\n seenAt: string | null;\n readAt: string | null;\n createdAt: string;\n};\n\nexport type NotifyState = {\n notifications: readonly NotificationView[];\n /** Badge count — cleared when the dropdown opens. Server-authoritative. */\n unseenCount: number;\n /** Items still bold. Derived from what is loaded, not a server total. */\n unreadCount: number;\n nextCursor: string | null;\n isLoading: boolean;\n error: Error | null;\n};\n\nexport type Transport = \"auto\" | \"sse\" | \"poll\";\n\nexport type TransportStatus = {\n /** \"leader\" holds this browser's single connection; \"follower\" mirrors it; \"solo\" has no Web Locks. */\n role: \"leader\" | \"follower\" | \"solo\";\n /** What the leader is using right now. */\n transport: \"sse\" | \"poll\";\n /** True while the event stream has said `ready` and not since closed. */\n connected: boolean;\n};\n\ntype LockManagerLike = {\n request(\n name: string,\n options: { mode: \"exclusive\"; signal?: AbortSignal },\n callback: () => Promise<void>,\n ): Promise<void>;\n};\n\ntype ChannelLike = {\n postMessage(message: unknown): void;\n addEventListener(type: \"message\", handler: (event: { data: unknown }) => void): void;\n removeEventListener(type: \"message\", handler: (event: { data: unknown }) => void): void;\n close(): void;\n};\n\ntype MessageTargetLike = {\n addEventListener(type: string, handler: (event: { data?: unknown }) => void): void;\n removeEventListener(type: string, handler: (event: { data?: unknown }) => void): void;\n};\n\nexport type NotifyClientOptions = {\n baseUrl?: string | undefined;\n fetch?: typeof globalThis.fetch | undefined;\n limit?: number | undefined;\n /** Poll interval while the user is active and no stream is connected. Default 15 s. */\n pollIntervalMs?: number | undefined;\n /** Ceiling the idle backoff grows to. Default 10 min. */\n maxPollIntervalMs?: number | undefined;\n /** Poll interval while the event stream is connected: a safety net. Default 5 min. */\n safetyNetMs?: number | undefined;\n /** How long after user input polling stays at the base interval. Default 60 s. */\n activeWindowMs?: number | undefined;\n /** \"auto\" streams when a DOM is present, \"poll\" never streams, \"sse\" always tries. */\n transport?: Transport | undefined;\n /** Injectable so the poller can be tested without a DOM. */\n isDocumentHidden?: (() => boolean) | undefined;\n /** Web Locks, for one connection per browser. Defaults to navigator.locks; null disables. */\n locks?: LockManagerLike | null | undefined;\n /** Cross-tab channel factory. Defaults to BroadcastChannel; null disables. */\n channel?: (() => ChannelLike) | null | undefined;\n /** Where push relays arrive. Defaults to navigator.serviceWorker; null disables. */\n serviceWorker?: MessageTargetLike | null | undefined;\n /** Where user activity is observed. Defaults to window; null disables. */\n activityTarget?: MessageTargetLike | null | undefined;\n now?: (() => number) | undefined;\n /**\n * Separates browsers' tab groups when identity is not a cookie: pass the\n * current user's id (or session id) so tabs signed in as different users\n * never share a leader or mirror each other's inbox.\n */\n scope?: string | undefined;\n};\n\nconst INITIAL: NotifyState = {\n notifications: [],\n unseenCount: 0,\n unreadCount: 0,\n nextCursor: null,\n isLoading: true,\n error: null,\n};\n\nconst countUnread = (rows: readonly NotificationView[]) =>\n rows.reduce((total, row) => (row.readAt === null ? total + 1 : total), 0);\n\n/** A stream that dies this fast, repeatedly, is being cut by the platform: stop trying. */\nconst SHORT_STREAM_MS = 15_000;\nconst SHORT_STREAMS_BEFORE_GIVING_UP = 3;\n\ntype TabMessage =\n | { type: \"hello\" }\n | { type: \"state\"; state: NotifyState }\n | { type: \"mutated\" }\n | { type: \"leader\" };\n\nexport function createNotifyClient(options: NotifyClientOptions = {}) {\n const baseUrl = (options.baseUrl ?? \"/api/notifications\").replace(/\\/$/, \"\");\n const doFetch = options.fetch ?? ((...args) => globalThis.fetch(...args));\n const limit = options.limit ?? 20;\n const pollIntervalMs = options.pollIntervalMs ?? 15_000;\n const maxPollIntervalMs = options.maxPollIntervalMs ?? 10 * 60_000;\n const safetyNetMs = options.safetyNetMs ?? 5 * 60_000;\n const activeWindowMs = options.activeWindowMs ?? 60_000;\n const now = options.now ?? (() => Date.now());\n const groupKey = options.scope ? `${baseUrl}#${options.scope}` : baseUrl;\n const hasDom = typeof window !== \"undefined\" && typeof document !== \"undefined\";\n const transport: Transport = options.transport ?? \"auto\";\n const wantsStream = transport === \"sse\" || (transport === \"auto\" && hasDom);\n const isHidden =\n options.isDocumentHidden ??\n (() => typeof document !== \"undefined\" && document.visibilityState === \"hidden\");\n\n const locks =\n options.locks === undefined\n ? typeof navigator !== \"undefined\"\n ? ((navigator as { locks?: LockManagerLike }).locks ?? null)\n : null\n : options.locks;\n const makeChannel =\n options.channel === undefined\n ? typeof BroadcastChannel !== \"undefined\"\n ? () => new BroadcastChannel(`easy-ping:${groupKey}`) as unknown as ChannelLike\n : null\n : options.channel;\n const serviceWorker =\n options.serviceWorker === undefined\n ? typeof navigator !== \"undefined\"\n ? ((navigator as { serviceWorker?: MessageTargetLike }).serviceWorker ?? null)\n : null\n : options.serviceWorker;\n const activityTarget =\n options.activityTarget === undefined\n ? hasDom\n ? (window as unknown as MessageTargetLike)\n : null\n : options.activityTarget;\n\n let state: NotifyState = INITIAL;\n const listeners = new Set<(state: NotifyState) => void>();\n\n let role: TransportStatus[\"role\"] = \"solo\";\n let running = false;\n let channel: ChannelLike | undefined;\n let releaseLock: (() => void) | undefined;\n let lockAbort: AbortController | undefined;\n\n let timer: ReturnType<typeof setTimeout> | undefined;\n let consecutiveFailures = 0;\n let idleTicks = 0;\n let activeUntil = 0;\n let lastRefreshAt = 0;\n let refreshing: Promise<void> | undefined;\n let refreshAgain = false;\n\n let streamAbort: AbortController | undefined;\n let streamConnected = false;\n let streamDisabled = !wantsStream;\n let shortStreams = 0;\n let streamFailures = 0;\n\n // Bumped per mutation so an in-flight poll cannot revert an optimistic update.\n let generation = 0;\n\n // A follower's own optimistic changes, kept until the leader's state reflects them.\n const localReads = new Set<string>();\n let localSeen = false;\n\n const stamp = () => new Date().toISOString();\n\n function emit() {\n for (const listener of listeners) listener(state);\n }\n\n function set(patch: Partial<NotifyState>) {\n state = { ...state, ...patch };\n state = { ...state, unreadCount: countUnread(state.notifications) };\n emit();\n if (role === \"leader\") post({ type: \"state\", state });\n }\n\n function post(message: TabMessage) {\n try {\n channel?.postMessage(message);\n } catch {\n // channel closed\n }\n }\n\n async function request<T>(path: string, init?: RequestInit): Promise<T> {\n const response = await doFetch(`${baseUrl}${path}`, {\n ...init,\n headers: { \"content-type\": \"application/json\", ...init?.headers },\n });\n if (!response.ok)\n throw new Error(`${init?.method ?? \"GET\"} ${path} failed: ${response.status}`);\n return (await response.json()) as T;\n }\n\n async function fetchPage(): Promise<void> {\n const startedAt = generation;\n\n const page = await request<{\n notifications: NotificationView[];\n nextCursor: string | null;\n unseenCount?: number;\n }>(`/?limit=${limit}`);\n\n const unseenCount = page.unseenCount ?? (await request<{ unseen: number }>(\"/count\")).unseen;\n\n // A mutation landed while this was in flight; the response is stale.\n if (generation !== startedAt) return;\n\n set({\n notifications: page.notifications,\n nextCursor: page.nextCursor,\n unseenCount,\n isLoading: false,\n error: null,\n });\n }\n\n /** Coalesces overlapping calls: a second request while one is in flight runs once after it. */\n function refresh(): Promise<void> {\n if (refreshing) {\n refreshAgain = true;\n return refreshing;\n }\n refreshing = (async () => {\n try {\n do {\n refreshAgain = false;\n await fetchPage();\n lastRefreshAt = now();\n } while (refreshAgain);\n } finally {\n refreshing = undefined;\n }\n })();\n return refreshing;\n }\n\n let loadingMore: Promise<void> | undefined;\n\n async function loadMore(): Promise<void> {\n if (!state.nextCursor) return;\n\n // Share the in-flight request so two clicks cannot append the same page twice.\n if (loadingMore) return loadingMore;\n\n const cursor = state.nextCursor;\n\n loadingMore = (async () => {\n try {\n const page = await request<{\n notifications: NotificationView[];\n nextCursor: string | null;\n }>(`/?limit=${limit}&cursor=${encodeURIComponent(cursor)}`);\n\n const known = new Set(state.notifications.map((row) => row.id));\n\n set({\n notifications: [\n ...state.notifications,\n ...page.notifications.filter((row) => !known.has(row.id)),\n ],\n nextCursor: page.nextCursor,\n });\n } finally {\n loadingMore = undefined;\n }\n })();\n\n return loadingMore;\n }\n\n /** Applies a patch immediately and reverts it if the request fails. */\n async function optimistic(\n apply: (rows: readonly NotificationView[]) => readonly NotificationView[],\n patch: Partial<NotifyState>,\n send: () => Promise<unknown>,\n ): Promise<void> {\n const previous = state;\n generation += 1;\n set({ notifications: apply(state.notifications), ...patch });\n\n try {\n await send();\n if (role === \"follower\") post({ type: \"mutated\" });\n } catch (error) {\n generation += 1;\n state = previous;\n emit();\n throw error;\n }\n }\n\n async function markAsRead(id: string): Promise<void> {\n if (role === \"follower\") localReads.add(id);\n await optimistic(\n (rows) =>\n rows.map((row) => (row.id === id ? { ...row, readAt: row.readAt ?? stamp() } : row)),\n {},\n () => request(\"/read\", { method: \"POST\", body: JSON.stringify({ ids: [id] }) }),\n ).catch((error) => {\n localReads.delete(id);\n throw error;\n });\n }\n\n async function markAllRead(): Promise<void> {\n if (role === \"follower\") for (const row of state.notifications) localReads.add(row.id);\n await optimistic(\n (rows) => rows.map((row) => ({ ...row, readAt: row.readAt ?? stamp() })),\n {},\n () => request(\"/read-all\", { method: \"POST\" }),\n ).catch((error) => {\n localReads.clear();\n throw error;\n });\n }\n\n async function markSeen(): Promise<void> {\n // Only what the user could have seen: no cutoff would mark newer arrivals seen too.\n const newest = state.notifications[0]?.createdAt;\n if (role === \"follower\") localSeen = true;\n\n await optimistic(\n (rows) => rows.map((row) => ({ ...row, seenAt: row.seenAt ?? stamp() })),\n { unseenCount: 0 },\n () =>\n request(\"/seen\", {\n method: \"POST\",\n body: JSON.stringify(newest ? { before: newest } : {}),\n }),\n ).catch((error) => {\n localSeen = false;\n throw error;\n });\n }\n\n // ---- follower: mirror the leader, keep own optimistic changes until confirmed ----\n\n function applyRemoteState(remote: NotifyState) {\n const notifications = remote.notifications.map((row) => {\n if (!localReads.has(row.id)) return row;\n if (row.readAt) {\n localReads.delete(row.id);\n return row;\n }\n return { ...row, readAt: stamp() };\n });\n if (localSeen && remote.unseenCount === 0) localSeen = false;\n state = {\n ...remote,\n notifications,\n unseenCount: localSeen ? 0 : remote.unseenCount,\n unreadCount: countUnread(notifications),\n };\n emit();\n }\n\n function onTabMessage(event: { data: unknown }) {\n const message = event.data as TabMessage | null;\n if (!message || typeof message !== \"object\") return;\n if (role === \"leader\") {\n if (message.type === \"hello\") post({ type: \"state\", state });\n else if (message.type === \"mutated\") void refresh().catch(() => {});\n return;\n }\n if (message.type === \"state\") applyRemoteState(message.state);\n }\n\n // ---- leader / solo: polling with activity-shaped backoff ----\n\n function schedule(delay: number) {\n clearTimeout(timer);\n timer = setTimeout(tick, delay);\n }\n\n function nextDelay(): number {\n if (consecutiveFailures > 0) {\n return Math.min(pollIntervalMs * 2 ** consecutiveFailures, maxPollIntervalMs);\n }\n if (streamConnected) return safetyNetMs;\n if (now() < activeUntil) return pollIntervalMs;\n idleTicks += 1;\n return Math.min(pollIntervalMs * 2 ** idleTicks, maxPollIntervalMs);\n }\n\n async function tick() {\n if (!running) return;\n // A hidden tab is not looking at the bell.\n if (isHidden()) {\n schedule(pollIntervalMs);\n return;\n }\n\n try {\n await refresh();\n consecutiveFailures = 0;\n } catch (error) {\n consecutiveFailures += 1;\n set({ isLoading: false, error: error instanceof Error ? error : new Error(String(error)) });\n }\n\n if (running) schedule(nextDelay());\n }\n\n /** User input or a returning tab: poll fast again, and catch up now if the last refresh is stale. */\n function onActivity() {\n if (!running || role === \"follower\") return;\n activeUntil = now() + activeWindowMs;\n idleTicks = 0;\n if (!isHidden() && now() - lastRefreshAt > pollIntervalMs) {\n void refresh().catch(() => {});\n schedule(nextDelay());\n }\n }\n\n const onVisibility = () => {\n if (!isHidden()) onActivity();\n };\n\n const ACTIVITY_EVENTS = [\"focus\", \"pointerdown\", \"keydown\", \"online\"];\n\n // ---- leader / solo: the event stream ----\n\n function changed() {\n if (!running) return;\n void refresh().catch(() => {});\n }\n\n async function readStream(signal: AbortSignal): Promise<void> {\n const openedAt = now();\n const response = await doFetch(`${baseUrl}/events`, {\n headers: { accept: \"text/event-stream\" },\n signal,\n });\n if (!response.ok) throw new Error(`GET /events failed: ${response.status}`);\n if (!response.body) {\n // This fetch cannot stream a body (React Native's built-in one, some polyfills): poll instead.\n streamDisabled = true;\n schedule(nextDelay());\n return;\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n let eventName = \"\";\n\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n let newline = buffer.indexOf(\"\\n\");\n while (newline >= 0) {\n const line = buffer.slice(0, newline).replace(/\\r$/, \"\");\n buffer = buffer.slice(newline + 1);\n if (line === \"\") {\n if (eventName === \"ready\") {\n streamConnected = true;\n streamFailures = 0;\n schedule(nextDelay());\n } else if (eventName === \"changed\") changed();\n eventName = \"\";\n } else if (line.startsWith(\"event:\")) eventName = line.slice(6).trim();\n newline = buffer.indexOf(\"\\n\");\n }\n }\n } finally {\n streamConnected = false;\n if (now() - openedAt < SHORT_STREAM_MS) shortStreams += 1;\n else shortStreams = 0;\n }\n }\n\n const isFollower = () => role === \"follower\";\n\n async function streamLoop() {\n while (running && !streamDisabled && !isFollower()) {\n streamAbort = new AbortController();\n try {\n await readStream(streamAbort.signal);\n } catch {\n if (!running) return;\n streamFailures += 1;\n }\n if (!running || isFollower() || streamDisabled) return;\n if (shortStreams >= SHORT_STREAMS_BEFORE_GIVING_UP) {\n // The platform cuts streams too fast for them to be worth it here.\n streamDisabled = true;\n schedule(nextDelay());\n return;\n }\n await new Promise((resolve) =>\n setTimeout(resolve, Math.min(3_000 * 2 ** streamFailures, 60_000)),\n );\n }\n }\n\n // ---- roles ----\n\n function becomeLeader() {\n role = \"leader\";\n post({ type: \"leader\" });\n post({ type: \"state\", state });\n void tick();\n if (!streamDisabled) void streamLoop();\n }\n\n function becomeFollower() {\n role = \"follower\";\n post({ type: \"hello\" });\n }\n\n function acquireLeadership() {\n if (!locks || !channel) {\n role = \"solo\";\n void tick();\n if (!streamDisabled) void streamLoop();\n return;\n }\n becomeFollower();\n lockAbort = new AbortController();\n locks\n .request(\n `easy-ping:leader:${groupKey}`,\n { mode: \"exclusive\", signal: lockAbort.signal },\n () =>\n new Promise<void>((resolve) => {\n releaseLock = resolve;\n if (running) becomeLeader();\n else resolve();\n }),\n )\n .catch(() => {\n // aborted on stop, or Web Locks refused; either way no leadership here\n });\n }\n\n function start() {\n running = true;\n channel = makeChannel ? makeChannel() : undefined;\n channel?.addEventListener(\"message\", onTabMessage);\n serviceWorker?.addEventListener(\"message\", onRelay);\n if (activityTarget) {\n for (const name of ACTIVITY_EVENTS) activityTarget.addEventListener(name, onActivity);\n }\n if (typeof document !== \"undefined\") {\n document.addEventListener(\"visibilitychange\", onVisibility);\n }\n activeUntil = now() + activeWindowMs;\n acquireLeadership();\n }\n\n function stop() {\n running = false;\n clearTimeout(timer);\n timer = undefined;\n streamAbort?.abort();\n streamAbort = undefined;\n lockAbort?.abort();\n lockAbort = undefined;\n releaseLock?.();\n releaseLock = undefined;\n channel?.removeEventListener(\"message\", onTabMessage);\n channel?.close();\n channel = undefined;\n serviceWorker?.removeEventListener(\"message\", onRelay);\n if (activityTarget) {\n for (const name of ACTIVITY_EVENTS) activityTarget.removeEventListener(name, onActivity);\n }\n if (typeof document !== \"undefined\") {\n document.removeEventListener(\"visibilitychange\", onVisibility);\n }\n role = \"solo\";\n }\n\n // ---- signals from outside: the service worker relay and the piggyback header ----\n\n function onRelay(event: { data?: unknown }) {\n const data = event.data as { source?: unknown; type?: unknown } | null;\n if (data?.source === \"easy-ping\" && data.type === \"changed\") changed();\n }\n\n let lastInboxVersion: string | null = null;\n\n // Only the app's own origin may steer the bell; a third-party response cannot.\n const sameOrigin = (response: Response) => {\n if (!response.url || typeof location === \"undefined\") return true;\n try {\n return new URL(response.url).origin === location.origin;\n } catch {\n return false;\n }\n };\n\n /**\n * Wraps your app's fetch so responses carrying the inbox-version header\n * refresh the bell when the version moves. Active users then never need a\n * dedicated poll. RFC 0006 idea 1.\n */\n function instrument(inner: typeof globalThis.fetch): typeof globalThis.fetch {\n return (async (...args: Parameters<typeof globalThis.fetch>) => {\n const response = await inner(...args);\n const version = sameOrigin(response) ? response.headers.get(\"x-easy-ping-inbox\") : null;\n if (version !== null && version !== lastInboxVersion) {\n const first = lastInboxVersion === null;\n lastInboxVersion = version;\n if (!first && role !== \"follower\") changed();\n }\n return response;\n }) as typeof globalThis.fetch;\n }\n\n function subscribe(listener: (state: NotifyState) => void): () => void {\n listeners.add(listener);\n listener(state);\n\n // One transport regardless of how many components subscribe.\n if (listeners.size === 1) start();\n\n return () => {\n listeners.delete(listener);\n if (listeners.size === 0) stop();\n };\n }\n\n return {\n subscribe,\n refresh,\n loadMore,\n markAsRead,\n markAllRead,\n markSeen,\n instrument,\n getState: () => state,\n getTransport: (): TransportStatus => ({\n role,\n transport: streamDisabled ? \"poll\" : \"sse\",\n connected: streamConnected,\n }),\n };\n}\n\nexport type NotifyClient = ReturnType<typeof createNotifyClient>;\n"]}