@q32/core 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +60 -0
  3. package/dist/ai.d.ts +26 -0
  4. package/dist/ai.d.ts.map +1 -0
  5. package/dist/ai.js +16 -0
  6. package/dist/ai.js.map +1 -0
  7. package/dist/api.d.ts +25 -0
  8. package/dist/api.d.ts.map +1 -0
  9. package/dist/api.js +66 -0
  10. package/dist/api.js.map +1 -0
  11. package/dist/billing.d.ts +21 -0
  12. package/dist/billing.d.ts.map +1 -0
  13. package/dist/billing.js +18 -0
  14. package/dist/billing.js.map +1 -0
  15. package/dist/cloudflare.d.ts +12 -0
  16. package/dist/cloudflare.d.ts.map +1 -0
  17. package/dist/cloudflare.js +25 -0
  18. package/dist/cloudflare.js.map +1 -0
  19. package/dist/crypto.d.ts +10 -0
  20. package/dist/crypto.d.ts.map +1 -0
  21. package/dist/crypto.js +42 -0
  22. package/dist/crypto.js.map +1 -0
  23. package/dist/d1.d.ts +34 -0
  24. package/dist/d1.d.ts.map +1 -0
  25. package/dist/d1.js +44 -0
  26. package/dist/d1.js.map +1 -0
  27. package/dist/email.d.ts +33 -0
  28. package/dist/email.d.ts.map +1 -0
  29. package/dist/email.js +16 -0
  30. package/dist/email.js.map +1 -0
  31. package/dist/env.d.ts +11 -0
  32. package/dist/env.d.ts.map +1 -0
  33. package/dist/env.js +55 -0
  34. package/dist/env.js.map +1 -0
  35. package/dist/http.d.ts +14 -0
  36. package/dist/http.d.ts.map +1 -0
  37. package/dist/http.js +53 -0
  38. package/dist/http.js.map +1 -0
  39. package/dist/ids.d.ts +7 -0
  40. package/dist/ids.d.ts.map +1 -0
  41. package/dist/ids.js +35 -0
  42. package/dist/ids.js.map +1 -0
  43. package/dist/index.d.ts +22 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +22 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/jobs.d.ts +52 -0
  48. package/dist/jobs.d.ts.map +1 -0
  49. package/dist/jobs.js +195 -0
  50. package/dist/jobs.js.map +1 -0
  51. package/dist/mcp.d.ts +18 -0
  52. package/dist/mcp.d.ts.map +1 -0
  53. package/dist/mcp.js +33 -0
  54. package/dist/mcp.js.map +1 -0
  55. package/dist/oauth.d.ts +17 -0
  56. package/dist/oauth.d.ts.map +1 -0
  57. package/dist/oauth.js +33 -0
  58. package/dist/oauth.js.map +1 -0
  59. package/dist/ops-events.d.ts +25 -0
  60. package/dist/ops-events.d.ts.map +1 -0
  61. package/dist/ops-events.js +43 -0
  62. package/dist/ops-events.js.map +1 -0
  63. package/dist/pg.d.ts +27 -0
  64. package/dist/pg.d.ts.map +1 -0
  65. package/dist/pg.js +51 -0
  66. package/dist/pg.js.map +1 -0
  67. package/dist/r2-json.d.ts +8 -0
  68. package/dist/r2-json.d.ts.map +1 -0
  69. package/dist/r2-json.js +21 -0
  70. package/dist/r2-json.js.map +1 -0
  71. package/dist/rate-limit.d.ts +19 -0
  72. package/dist/rate-limit.d.ts.map +1 -0
  73. package/dist/rate-limit.js +48 -0
  74. package/dist/rate-limit.js.map +1 -0
  75. package/dist/seo.d.ts +23 -0
  76. package/dist/seo.d.ts.map +1 -0
  77. package/dist/seo.js +60 -0
  78. package/dist/seo.js.map +1 -0
  79. package/dist/session.d.ts +11 -0
  80. package/dist/session.d.ts.map +1 -0
  81. package/dist/session.js +55 -0
  82. package/dist/session.js.map +1 -0
  83. package/dist/testing.d.ts +25 -0
  84. package/dist/testing.d.ts.map +1 -0
  85. package/dist/testing.js +94 -0
  86. package/dist/testing.js.map +1 -0
  87. package/dist/time.d.ts +5 -0
  88. package/dist/time.d.ts.map +1 -0
  89. package/dist/time.js +17 -0
  90. package/dist/time.js.map +1 -0
  91. package/docs/evaluation.md +74 -0
  92. package/docs/goal-audit.md +28 -0
  93. package/package.json +129 -0
@@ -0,0 +1,43 @@
1
+ import { stringifyJsonColumn } from "./d1.js";
2
+ export async function recordOpsEvent(db, input) {
3
+ await db
4
+ .prepare(`INSERT INTO ops_events (event_type, severity, source, fingerprint, payload_json)
5
+ VALUES (?, ?, ?, ?, ?)`)
6
+ .bind(input.eventType, input.severity ?? "info", input.source, input.fingerprint ?? null, stringifyJsonColumn(input.payload ?? {}))
7
+ .run();
8
+ }
9
+ export async function listRecentOpsEvents(db, options = {}) {
10
+ const limit = Math.max(1, Math.min(200, Math.floor(options.limit ?? 25)));
11
+ const eventType = options.eventType?.trim();
12
+ const statement = eventType
13
+ ? db
14
+ .prepare(`SELECT id, event_type, severity, source, fingerprint, payload_json, created_at
15
+ FROM ops_events
16
+ WHERE event_type = ?
17
+ ORDER BY created_at DESC, id DESC
18
+ LIMIT ?`)
19
+ .bind(eventType, limit)
20
+ : db
21
+ .prepare(`SELECT id, event_type, severity, source, fingerprint, payload_json, created_at
22
+ FROM ops_events
23
+ ORDER BY created_at DESC, id DESC
24
+ LIMIT ?`)
25
+ .bind(limit);
26
+ const rows = await statement.all();
27
+ return rows.results ?? [];
28
+ }
29
+ export const D1_OPS_EVENTS_SCHEMA = `
30
+ CREATE TABLE IF NOT EXISTS ops_events (
31
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
32
+ event_type TEXT NOT NULL,
33
+ severity TEXT NOT NULL DEFAULT 'info',
34
+ source TEXT NOT NULL,
35
+ fingerprint TEXT,
36
+ payload_json TEXT NOT NULL DEFAULT '{}',
37
+ created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
38
+ );
39
+ CREATE INDEX IF NOT EXISTS ops_events_created_at_idx ON ops_events(created_at DESC);
40
+ CREATE INDEX IF NOT EXISTS ops_events_type_created_idx ON ops_events(event_type, created_at DESC);
41
+ CREATE INDEX IF NOT EXISTS ops_events_severity_created_idx ON ops_events(severity, created_at DESC);
42
+ `;
43
+ //# sourceMappingURL=ops-events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ops-events.js","sourceRoot":"","sources":["../src/ops-events.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAsB9C,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,EAAkB,EAAE,KAAoB;IAC3E,MAAM,EAAE;SACL,OAAO,CACN;8BACwB,CACzB;SACA,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,QAAQ,IAAI,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,WAAW,IAAI,IAAI,EAAE,mBAAmB,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;SAClI,GAAG,EAAE,CAAC;AACX,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,EAAkB,EAClB,UAAyD,EAAE;IAE3D,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC1E,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC;IAC5C,MAAM,SAAS,GAAG,SAAS;QACzB,CAAC,CAAC,EAAE;aACC,OAAO,CACN;;;;mBAIS,CACV;aACA,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC;QAC3B,CAAC,CAAC,EAAE;aACC,OAAO,CACN;;;mBAGS,CACV;aACA,IAAI,CAAC,KAAK,CAAC,CAAC;IACnB,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,GAAG,EAAe,CAAC;IAChD,OAAO,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC;AAC5B,CAAC;AAED,MAAM,CAAC,MAAM,oBAAoB,GAAG;;;;;;;;;;;;;CAanC,CAAC"}
package/dist/pg.d.ts ADDED
@@ -0,0 +1,27 @@
1
+ export interface PgQueryResult<Row = unknown> {
2
+ rows: Row[];
3
+ rowCount: number | null;
4
+ }
5
+ export interface PgClientLike {
6
+ query<Row = unknown>(text: string, values?: readonly unknown[]): Promise<PgQueryResult<Row>>;
7
+ }
8
+ export interface PgPoolLike extends PgClientLike {
9
+ connect(): Promise<PgClientLike & {
10
+ release(): void;
11
+ }>;
12
+ }
13
+ export type PgMigration = {
14
+ id: string;
15
+ sql: string;
16
+ };
17
+ export type PgMigrationResult = {
18
+ applied: string[];
19
+ skipped: string[];
20
+ };
21
+ export declare function ensurePgMigrationsTable(client: PgClientLike, tableName?: string): Promise<void>;
22
+ export declare function applyPgMigrations(pool: PgPoolLike, migrations: PgMigration[], options?: {
23
+ tableName?: string;
24
+ lockKey?: number;
25
+ }): Promise<PgMigrationResult>;
26
+ export declare function pgJson<T>(value: T): string;
27
+ //# sourceMappingURL=pg.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pg.d.ts","sourceRoot":"","sources":["../src/pg.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa,CAAC,GAAG,GAAG,OAAO;IAC1C,IAAI,EAAE,GAAG,EAAE,CAAC;IACZ,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,GAAG,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC;CAC9F;AAED,MAAM,WAAW,UAAW,SAAQ,YAAY;IAC9C,OAAO,IAAI,OAAO,CAAC,YAAY,GAAG;QAAE,OAAO,IAAI,IAAI,CAAA;KAAE,CAAC,CAAC;CACxD;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;AAEF,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,YAAY,EAAE,SAAS,SAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAQlH;AAED,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,UAAU,EAChB,UAAU,EAAE,WAAW,EAAE,EACzB,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GACrD,OAAO,CAAC,iBAAiB,CAAC,CAiC5B;AAED,wBAAgB,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,MAAM,CAE1C"}
package/dist/pg.js ADDED
@@ -0,0 +1,51 @@
1
+ export async function ensurePgMigrationsTable(client, tableName = "schema_migrations") {
2
+ assertSafeIdentifier(tableName);
3
+ await client.query(`
4
+ CREATE TABLE IF NOT EXISTS ${tableName} (
5
+ id TEXT PRIMARY KEY,
6
+ applied_at TIMESTAMPTZ NOT NULL DEFAULT now()
7
+ )
8
+ `);
9
+ }
10
+ export async function applyPgMigrations(pool, migrations, options = {}) {
11
+ const tableName = options.tableName ?? "schema_migrations";
12
+ assertSafeIdentifier(tableName);
13
+ const client = await pool.connect();
14
+ const applied = [];
15
+ const skipped = [];
16
+ try {
17
+ await client.query("BEGIN");
18
+ if (options.lockKey !== undefined) {
19
+ await client.query("SELECT pg_advisory_xact_lock($1)", [options.lockKey]);
20
+ }
21
+ await ensurePgMigrationsTable(client, tableName);
22
+ for (const migration of migrations) {
23
+ const existing = await client.query(`SELECT id FROM ${tableName} WHERE id = $1 LIMIT 1`, [migration.id]);
24
+ if (existing.rows[0]) {
25
+ skipped.push(migration.id);
26
+ continue;
27
+ }
28
+ await client.query(migration.sql);
29
+ await client.query(`INSERT INTO ${tableName} (id) VALUES ($1)`, [migration.id]);
30
+ applied.push(migration.id);
31
+ }
32
+ await client.query("COMMIT");
33
+ return { applied, skipped };
34
+ }
35
+ catch (error) {
36
+ await client.query("ROLLBACK");
37
+ throw error;
38
+ }
39
+ finally {
40
+ client.release();
41
+ }
42
+ }
43
+ export function pgJson(value) {
44
+ return JSON.stringify(value ?? null);
45
+ }
46
+ function assertSafeIdentifier(value) {
47
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(value)) {
48
+ throw new Error(`Unsafe SQL identifier: ${value}`);
49
+ }
50
+ }
51
+ //# sourceMappingURL=pg.js.map
package/dist/pg.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pg.js","sourceRoot":"","sources":["../src/pg.ts"],"names":[],"mappings":"AAuBA,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,MAAoB,EAAE,SAAS,GAAG,mBAAmB;IACjG,oBAAoB,CAAC,SAAS,CAAC,CAAC;IAChC,MAAM,MAAM,CAAC,KAAK,CAAC;iCACY,SAAS;;;;GAIvC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,IAAgB,EAChB,UAAyB,EACzB,UAAoD,EAAE;IAEtD,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,mBAAmB,CAAC;IAC3D,oBAAoB,CAAC,SAAS,CAAC,CAAC;IAChC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;IACpC,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC5B,IAAI,OAAO,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;YAClC,MAAM,MAAM,CAAC,KAAK,CAAC,kCAAkC,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;QAC5E,CAAC;QACD,MAAM,uBAAuB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QAEjD,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YACnC,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,KAAK,CAAiB,kBAAkB,SAAS,wBAAwB,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC;YACzH,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;gBACrB,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;gBAC3B,SAAS;YACX,CAAC;YACD,MAAM,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YAClC,MAAM,MAAM,CAAC,KAAK,CAAC,eAAe,SAAS,mBAAmB,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC;YAChF,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QAC7B,CAAC;QAED,MAAM,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC7B,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;IAC9B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAC/B,MAAM,KAAK,CAAC;IACd,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,OAAO,EAAE,CAAC;IACnB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,MAAM,CAAI,KAAQ;IAChC,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC;AACvC,CAAC;AAED,SAAS,oBAAoB,CAAC,KAAa;IACzC,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CAAC,0BAA0B,KAAK,EAAE,CAAC,CAAC;IACrD,CAAC;AACH,CAAC"}
@@ -0,0 +1,8 @@
1
+ export type R2JsonPutOptions = {
2
+ contentType?: string;
3
+ customMetadata?: Record<string, string>;
4
+ };
5
+ export declare function putR2Json(bucket: R2Bucket, key: string, value: unknown, options?: R2JsonPutOptions): Promise<R2Object>;
6
+ export declare function getR2Json<T>(bucket: R2Bucket, key: string): Promise<T | null>;
7
+ export declare function digestR2Key(prefix: string, value: string | Uint8Array, extension?: string): Promise<string>;
8
+ //# sourceMappingURL=r2-json.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"r2-json.d.ts","sourceRoot":"","sources":["../src/r2-json.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzC,CAAC;AAEF,wBAAsB,SAAS,CAC7B,MAAM,EAAE,QAAQ,EAChB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,OAAO,EACd,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,QAAQ,CAAC,CAKnB;AAED,wBAAsB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAInF;AAED,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,UAAU,EAAE,SAAS,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAMjH"}
@@ -0,0 +1,21 @@
1
+ import { sha256Hex } from "./ids.js";
2
+ export async function putR2Json(bucket, key, value, options = {}) {
3
+ return bucket.put(key, JSON.stringify(value), {
4
+ httpMetadata: { contentType: options.contentType ?? "application/json; charset=utf-8" },
5
+ customMetadata: options.customMetadata,
6
+ });
7
+ }
8
+ export async function getR2Json(bucket, key) {
9
+ const object = await bucket.get(key);
10
+ if (!object)
11
+ return null;
12
+ return (await object.json());
13
+ }
14
+ export async function digestR2Key(prefix, value, extension = "json") {
15
+ const input = typeof value === "string" ? value : [...value].map((byte) => String.fromCharCode(byte)).join("");
16
+ const digest = await sha256Hex(input);
17
+ const cleanPrefix = prefix.replace(/^\/+|\/+$/g, "");
18
+ const cleanExtension = extension.replace(/^\./, "");
19
+ return `${cleanPrefix}/${digest.slice(0, 2)}/${digest}.${cleanExtension}`;
20
+ }
21
+ //# sourceMappingURL=r2-json.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"r2-json.js","sourceRoot":"","sources":["../src/r2-json.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAOrC,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,MAAgB,EAChB,GAAW,EACX,KAAc,EACd,UAA4B,EAAE;IAE9B,OAAO,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE;QAC5C,YAAY,EAAE,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,iCAAiC,EAAE;QACvF,cAAc,EAAE,OAAO,CAAC,cAAc;KACvC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAI,MAAgB,EAAE,GAAW;IAC9D,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACrC,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,OAAO,CAAC,MAAM,MAAM,CAAC,IAAI,EAAE,CAAM,CAAC;AACpC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,MAAc,EAAE,KAA0B,EAAE,SAAS,GAAG,MAAM;IAC9F,MAAM,KAAK,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC/G,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,KAAK,CAAC,CAAC;IACtC,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;IACrD,MAAM,cAAc,GAAG,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACpD,OAAO,GAAG,WAAW,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,IAAI,cAAc,EAAE,CAAC;AAC5E,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { D1DatabaseLike } from "./d1.js";
2
+ export type RateLimitDecision = {
3
+ allowed: boolean;
4
+ keyHash: string;
5
+ count: number;
6
+ limit: number;
7
+ resetAt: string;
8
+ };
9
+ export type FixedWindowRateLimitInput = {
10
+ namespace: string;
11
+ key: string;
12
+ limit: number;
13
+ windowSeconds: number;
14
+ now?: Date;
15
+ };
16
+ export declare function checkD1FixedWindowRateLimit(db: D1DatabaseLike, input: FixedWindowRateLimitInput): Promise<RateLimitDecision>;
17
+ export declare function deleteExpiredD1RateLimitBuckets(db: D1DatabaseLike, now?: Date): Promise<number>;
18
+ export declare const D1_RATE_LIMITS_SCHEMA = "\nCREATE TABLE IF NOT EXISTS rate_limits (\n namespace TEXT NOT NULL,\n key_hash TEXT NOT NULL,\n bucket TEXT NOT NULL,\n count INTEGER NOT NULL DEFAULT 0,\n reset_at TEXT NOT NULL,\n PRIMARY KEY (namespace, key_hash, bucket)\n);\nCREATE INDEX IF NOT EXISTS rate_limits_reset_at_idx ON rate_limits(reset_at);\n";
19
+ //# sourceMappingURL=rate-limit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAG9C,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ,CAAC;AAEF,wBAAsB,2BAA2B,CAC/C,EAAE,EAAE,cAAc,EAClB,KAAK,EAAE,yBAAyB,GAC/B,OAAO,CAAC,iBAAiB,CAAC,CAqC5B;AAED,wBAAsB,+BAA+B,CAAC,EAAE,EAAE,cAAc,EAAE,GAAG,OAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAG3G;AAED,eAAO,MAAM,qBAAqB,iUAUjC,CAAC"}
@@ -0,0 +1,48 @@
1
+ import { sha256Hex } from "./ids.js";
2
+ export async function checkD1FixedWindowRateLimit(db, input) {
3
+ const now = input.now ?? new Date();
4
+ const windowSeconds = Math.max(1, Math.floor(input.windowSeconds));
5
+ const limit = Math.max(1, Math.floor(input.limit));
6
+ const bucketStartMs = Math.floor(now.getTime() / (windowSeconds * 1000)) * windowSeconds * 1000;
7
+ const bucket = new Date(bucketStartMs).toISOString();
8
+ const resetAt = new Date(bucketStartMs + windowSeconds * 1000).toISOString();
9
+ const keyHash = await sha256Hex(`${input.namespace}:${input.key}`);
10
+ await db
11
+ .prepare(`INSERT INTO rate_limits (namespace, key_hash, bucket, count, reset_at)
12
+ VALUES (?, ?, ?, 1, ?)
13
+ ON CONFLICT(namespace, key_hash, bucket)
14
+ DO UPDATE SET count = count + 1, reset_at = excluded.reset_at`)
15
+ .bind(input.namespace, keyHash, bucket, resetAt)
16
+ .run();
17
+ const row = await db
18
+ .prepare(`SELECT count, reset_at AS resetAt
19
+ FROM rate_limits
20
+ WHERE namespace = ? AND key_hash = ? AND bucket = ?
21
+ LIMIT 1`)
22
+ .bind(input.namespace, keyHash, bucket)
23
+ .first();
24
+ const count = Number(row?.count ?? 1);
25
+ return {
26
+ allowed: count <= limit,
27
+ keyHash,
28
+ count,
29
+ limit,
30
+ resetAt: row?.resetAt ?? resetAt,
31
+ };
32
+ }
33
+ export async function deleteExpiredD1RateLimitBuckets(db, now = new Date()) {
34
+ const result = await db.prepare("DELETE FROM rate_limits WHERE reset_at < ?").bind(now.toISOString()).run();
35
+ return Number(result.meta.changes ?? 0);
36
+ }
37
+ export const D1_RATE_LIMITS_SCHEMA = `
38
+ CREATE TABLE IF NOT EXISTS rate_limits (
39
+ namespace TEXT NOT NULL,
40
+ key_hash TEXT NOT NULL,
41
+ bucket TEXT NOT NULL,
42
+ count INTEGER NOT NULL DEFAULT 0,
43
+ reset_at TEXT NOT NULL,
44
+ PRIMARY KEY (namespace, key_hash, bucket)
45
+ );
46
+ CREATE INDEX IF NOT EXISTS rate_limits_reset_at_idx ON rate_limits(reset_at);
47
+ `;
48
+ //# sourceMappingURL=rate-limit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAkBrC,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAC/C,EAAkB,EAClB,KAAgC;IAEhC,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC;IACpC,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC;IACnE,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;IACnD,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC,GAAG,aAAa,GAAG,IAAI,CAAC;IAChG,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,aAAa,CAAC,CAAC,WAAW,EAAE,CAAC;IACrD,MAAM,OAAO,GAAG,IAAI,IAAI,CAAC,aAAa,GAAG,aAAa,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;IAC7E,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,GAAG,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;IAEnE,MAAM,EAAE;SACL,OAAO,CACN;;;qEAG+D,CAChE;SACA,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC;SAC/C,GAAG,EAAE,CAAC;IAET,MAAM,GAAG,GAAG,MAAM,EAAE;SACjB,OAAO,CACN;;;eAGS,CACV;SACA,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,OAAO,EAAE,MAAM,CAAC;SACtC,KAAK,EAAsC,CAAC;IAE/C,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,EAAE,KAAK,IAAI,CAAC,CAAC,CAAC;IACtC,OAAO;QACL,OAAO,EAAE,KAAK,IAAI,KAAK;QACvB,OAAO;QACP,KAAK;QACL,KAAK;QACL,OAAO,EAAE,GAAG,EAAE,OAAO,IAAI,OAAO;KACjC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,+BAA+B,CAAC,EAAkB,EAAE,GAAG,GAAG,IAAI,IAAI,EAAE;IACxF,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,4CAA4C,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC;IAC5G,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,CAAC,MAAM,qBAAqB,GAAG;;;;;;;;;;CAUpC,CAAC"}
package/dist/seo.d.ts ADDED
@@ -0,0 +1,23 @@
1
+ export type SitemapUrl = {
2
+ loc: string;
3
+ lastmod?: string;
4
+ changefreq?: "always" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" | "never";
5
+ priority?: number;
6
+ };
7
+ export type PageMeta = {
8
+ title: string;
9
+ description?: string;
10
+ canonical?: string;
11
+ image?: string;
12
+ type?: string;
13
+ noindex?: boolean;
14
+ };
15
+ export declare function xmlEscape(value: string): string;
16
+ export declare function renderSitemapXml(urls: SitemapUrl[]): string;
17
+ export declare function renderRobotsTxt(options?: {
18
+ allow?: string[];
19
+ disallow?: string[];
20
+ sitemap?: string | string[];
21
+ }): string;
22
+ export declare function metaTags(meta: PageMeta): Record<string, string>;
23
+ //# sourceMappingURL=seo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seo.d.ts","sourceRoot":"","sources":["../src/seo.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC;IACvF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAO/C;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,MAAM,CAa3D;AAED,wBAAgB,eAAe,CAAC,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;CAAO,GAAG,MAAM,CAQ5H;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAiB/D"}
package/dist/seo.js ADDED
@@ -0,0 +1,60 @@
1
+ export function xmlEscape(value) {
2
+ return value
3
+ .replace(/&/g, "&amp;")
4
+ .replace(/</g, "&lt;")
5
+ .replace(/>/g, "&gt;")
6
+ .replace(/"/g, "&quot;")
7
+ .replace(/'/g, "&apos;");
8
+ }
9
+ export function renderSitemapXml(urls) {
10
+ const body = urls
11
+ .map((url) => {
12
+ const fields = [
13
+ `<loc>${xmlEscape(url.loc)}</loc>`,
14
+ url.lastmod ? `<lastmod>${xmlEscape(url.lastmod)}</lastmod>` : "",
15
+ url.changefreq ? `<changefreq>${url.changefreq}</changefreq>` : "",
16
+ url.priority === undefined ? "" : `<priority>${clampPriority(url.priority).toFixed(1)}</priority>`,
17
+ ].filter(Boolean);
18
+ return ` <url>\n ${fields.join("\n ")}\n </url>`;
19
+ })
20
+ .join("\n");
21
+ return `<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n${body}\n</urlset>\n`;
22
+ }
23
+ export function renderRobotsTxt(options = {}) {
24
+ const lines = ["User-agent: *"];
25
+ for (const path of options.allow ?? [])
26
+ lines.push(`Allow: ${path}`);
27
+ for (const path of options.disallow ?? [])
28
+ lines.push(`Disallow: ${path}`);
29
+ for (const sitemap of Array.isArray(options.sitemap) ? options.sitemap : options.sitemap ? [options.sitemap] : []) {
30
+ lines.push(`Sitemap: ${sitemap}`);
31
+ }
32
+ return `${lines.join("\n")}\n`;
33
+ }
34
+ export function metaTags(meta) {
35
+ const tags = {
36
+ title: meta.title,
37
+ "og:title": meta.title,
38
+ };
39
+ if (meta.description) {
40
+ tags.description = meta.description;
41
+ tags["og:description"] = meta.description;
42
+ }
43
+ if (meta.canonical) {
44
+ tags.canonical = meta.canonical;
45
+ tags["og:url"] = meta.canonical;
46
+ }
47
+ if (meta.image)
48
+ tags["og:image"] = meta.image;
49
+ if (meta.type)
50
+ tags["og:type"] = meta.type;
51
+ if (meta.noindex)
52
+ tags.robots = "noindex,nofollow";
53
+ return tags;
54
+ }
55
+ function clampPriority(priority) {
56
+ if (!Number.isFinite(priority))
57
+ return 0.5;
58
+ return Math.max(0, Math.min(1, priority));
59
+ }
60
+ //# sourceMappingURL=seo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seo.js","sourceRoot":"","sources":["../src/seo.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export type SignedSessionOptions = {
2
+ expiresInSeconds?: number;
3
+ };
4
+ export declare function signSession<T>(payload: T, secret: string, options?: SignedSessionOptions): Promise<string>;
5
+ export declare function verifySession<T>(token: string | null | undefined, secret: string): Promise<T | null>;
6
+ export declare function sessionCookie(name: string, value: string, options?: {
7
+ maxAgeSeconds?: number;
8
+ secure?: boolean;
9
+ }): string;
10
+ export declare function expiredSessionCookie(name: string, secure?: boolean): string;
11
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,oBAAoB,GAAG;IACjC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAQF,wBAAsB,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,MAAM,CAAC,CAWpH;AAED,wBAAsB,aAAa,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAc1G;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE;IAAE,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,MAAM,CAG7H;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,UAAQ,GAAG,MAAM,CAEzE"}
@@ -0,0 +1,55 @@
1
+ import { fromBase64Url, toBase64Url } from "./ids.js";
2
+ import { epochSeconds } from "./time.js";
3
+ export async function signSession(payload, secret, options = {}) {
4
+ if (!secret)
5
+ throw new Error("Session secret is required.");
6
+ const now = epochSeconds();
7
+ const envelope = {
8
+ payload,
9
+ iat: now,
10
+ exp: now + (options.expiresInSeconds ?? 30 * 24 * 60 * 60),
11
+ };
12
+ const encoded = toBase64Url(JSON.stringify(envelope));
13
+ const signature = await hmacSha256(encoded, secret);
14
+ return `${encoded}.${signature}`;
15
+ }
16
+ export async function verifySession(token, secret) {
17
+ if (!token || !secret)
18
+ return null;
19
+ const [encoded, signature] = token.split(".");
20
+ if (!encoded || !signature)
21
+ return null;
22
+ const expected = await hmacSha256(encoded, secret);
23
+ if (!constantTimeEqual(signature, expected))
24
+ return null;
25
+ try {
26
+ const envelope = JSON.parse(new TextDecoder().decode(fromBase64Url(encoded)));
27
+ if (!envelope || typeof envelope.exp !== "number" || envelope.exp < epochSeconds())
28
+ return null;
29
+ return envelope.payload;
30
+ }
31
+ catch {
32
+ return null;
33
+ }
34
+ }
35
+ export function sessionCookie(name, value, options = {}) {
36
+ const secure = options.secure ? "; Secure" : "";
37
+ return `${name}=${value}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${options.maxAgeSeconds ?? 30 * 24 * 60 * 60}${secure}`;
38
+ }
39
+ export function expiredSessionCookie(name, secure = false) {
40
+ return `${name}=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0${secure ? "; Secure" : ""}`;
41
+ }
42
+ async function hmacSha256(input, secret) {
43
+ const key = await crypto.subtle.importKey("raw", new TextEncoder().encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
44
+ const signature = await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(input));
45
+ return toBase64Url(new Uint8Array(signature));
46
+ }
47
+ function constantTimeEqual(a, b) {
48
+ if (a.length !== b.length)
49
+ return false;
50
+ let diff = 0;
51
+ for (let i = 0; i < a.length; i += 1)
52
+ diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
53
+ return diff === 0;
54
+ }
55
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ export type SentQueueMessage<T> = {
2
+ body: T;
3
+ options?: QueueSendOptions;
4
+ };
5
+ export type MemoryQueue<T = unknown> = Pick<Queue<T>, "send" | "sendBatch"> & {
6
+ sent: SentQueueMessage<T>[];
7
+ clear(): void;
8
+ };
9
+ export declare function createMemoryQueue<T = unknown>(): MemoryQueue<T>;
10
+ export type MemoryR2Object = {
11
+ key: string;
12
+ body: Uint8Array;
13
+ httpMetadata?: R2HTTPMetadata;
14
+ customMetadata?: Record<string, string>;
15
+ text(): Promise<string>;
16
+ json<T = unknown>(): Promise<T>;
17
+ arrayBuffer(): Promise<ArrayBuffer>;
18
+ };
19
+ export type MemoryR2Bucket = Pick<R2Bucket, "put" | "get" | "delete"> & {
20
+ objects: Map<string, MemoryR2Object>;
21
+ clear(): void;
22
+ };
23
+ export declare function createMemoryR2Bucket(): MemoryR2Bucket;
24
+ export declare function expectJsonResponse<T = unknown>(response: Response, status?: number): Promise<T>;
25
+ //# sourceMappingURL=testing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI;IAChC,IAAI,EAAE,CAAC,CAAC;IACR,OAAO,CAAC,EAAE,gBAAgB,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,WAAW,CAAC,CAAC,GAAG,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC,GAAG;IAC5E,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5B,KAAK,IAAI,IAAI,CAAC;CACf,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,CAAC,GAAG,OAAO,KAAK,WAAW,CAAC,CAAC,CAAC,CAgB/D;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,UAAU,CAAC;IACjB,YAAY,CAAC,EAAE,cAAc,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IACxB,IAAI,CAAC,CAAC,GAAG,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IAChC,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC;CACrC,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,IAAI,CAAC,QAAQ,EAAE,KAAK,GAAG,KAAK,GAAG,QAAQ,CAAC,GAAG;IACtE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACrC,KAAK,IAAI,IAAI,CAAC;CACf,CAAC;AAEF,wBAAgB,oBAAoB,IAAI,cAAc,CAoBrD;AAED,wBAAsB,kBAAkB,CAAC,CAAC,GAAG,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,SAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAKlG"}
@@ -0,0 +1,94 @@
1
+ export function createMemoryQueue() {
2
+ const sent = [];
3
+ return {
4
+ sent,
5
+ async send(body, options) {
6
+ sent.push({ body, options });
7
+ return queueSendResponse();
8
+ },
9
+ async sendBatch(messages) {
10
+ for (const message of messages)
11
+ sent.push({ body: message.body, options: message });
12
+ return queueSendBatchResponse();
13
+ },
14
+ clear() {
15
+ sent.length = 0;
16
+ },
17
+ };
18
+ }
19
+ export function createMemoryR2Bucket() {
20
+ const objects = new Map();
21
+ return {
22
+ objects,
23
+ async put(key, value, options) {
24
+ const body = await toBytes(value);
25
+ const object = memoryR2Object(key, body, normalizeR2HttpMetadata(options?.httpMetadata), options?.customMetadata);
26
+ objects.set(key, object);
27
+ return object;
28
+ },
29
+ async get(key) {
30
+ return objects.get(key) ?? null;
31
+ },
32
+ async delete(keys) {
33
+ for (const key of Array.isArray(keys) ? keys : [keys])
34
+ objects.delete(key);
35
+ },
36
+ clear() {
37
+ objects.clear();
38
+ },
39
+ };
40
+ }
41
+ export async function expectJsonResponse(response, status = 200) {
42
+ if (response.status !== status)
43
+ throw new Error(`Expected response status ${status}, got ${response.status}.`);
44
+ const contentType = response.headers.get("content-type") ?? "";
45
+ if (!contentType.includes("application/json"))
46
+ throw new Error(`Expected JSON response, got ${contentType || "no content-type"}.`);
47
+ return (await response.json());
48
+ }
49
+ async function toBytes(value) {
50
+ if (value === null)
51
+ return new Uint8Array();
52
+ if (typeof value === "string")
53
+ return new TextEncoder().encode(value);
54
+ if (value instanceof ArrayBuffer)
55
+ return new Uint8Array(value);
56
+ if (ArrayBuffer.isView(value))
57
+ return new Uint8Array(value.buffer.slice(value.byteOffset, value.byteOffset + value.byteLength));
58
+ if (value instanceof Blob)
59
+ return new Uint8Array(await value.arrayBuffer());
60
+ return new Uint8Array(await new Response(value).arrayBuffer());
61
+ }
62
+ function queueSendResponse() {
63
+ return { metadata: { metrics: { backlogCount: 0, backlogBytes: 0 } } };
64
+ }
65
+ function queueSendBatchResponse() {
66
+ return { metadata: { metrics: { backlogCount: 0, backlogBytes: 0 } } };
67
+ }
68
+ function normalizeR2HttpMetadata(value) {
69
+ if (!value)
70
+ return undefined;
71
+ if (!(value instanceof Headers))
72
+ return value;
73
+ const contentType = value.get("content-type") ?? undefined;
74
+ const cacheControl = value.get("cache-control") ?? undefined;
75
+ return { contentType, cacheControl };
76
+ }
77
+ function memoryR2Object(key, body, httpMetadata, customMetadata) {
78
+ return {
79
+ key,
80
+ body,
81
+ httpMetadata,
82
+ customMetadata,
83
+ async text() {
84
+ return new TextDecoder().decode(body);
85
+ },
86
+ async json() {
87
+ return JSON.parse(await this.text());
88
+ },
89
+ async arrayBuffer() {
90
+ return body.buffer.slice(body.byteOffset, body.byteOffset + body.byteLength);
91
+ },
92
+ };
93
+ }
94
+ //# sourceMappingURL=testing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.js","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"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"}
package/dist/time.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ export declare function nowIso(date?: Date): string;
2
+ export declare function epochSeconds(date?: Date): number;
3
+ export declare function addSeconds(date: Date | string, seconds: number): string;
4
+ export declare function isFutureIso(value: string | null | undefined, now?: Date): boolean;
5
+ //# sourceMappingURL=time.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time.d.ts","sourceRoot":"","sources":["../src/time.ts"],"names":[],"mappings":"AAAA,wBAAgB,MAAM,CAAC,IAAI,OAAa,GAAG,MAAM,CAEhD;AAED,wBAAgB,YAAY,CAAC,IAAI,OAAa,GAAG,MAAM,CAEtD;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAGvE;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,GAAG,OAAa,GAAG,OAAO,CAIvF"}
package/dist/time.js ADDED
@@ -0,0 +1,17 @@
1
+ export function nowIso(date = new Date()) {
2
+ return date.toISOString();
3
+ }
4
+ export function epochSeconds(date = new Date()) {
5
+ return Math.floor(date.getTime() / 1000);
6
+ }
7
+ export function addSeconds(date, seconds) {
8
+ const base = typeof date === "string" ? new Date(date) : date;
9
+ return new Date(base.getTime() + seconds * 1000).toISOString();
10
+ }
11
+ export function isFutureIso(value, now = new Date()) {
12
+ if (!value)
13
+ return false;
14
+ const timestamp = Date.parse(value);
15
+ return Number.isFinite(timestamp) && timestamp > now.getTime();
16
+ }
17
+ //# sourceMappingURL=time.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time.js","sourceRoot":"","sources":["../src/time.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,MAAM,CAAC,IAAI,GAAG,IAAI,IAAI,EAAE;IACtC,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAI,GAAG,IAAI,IAAI,EAAE;IAC5C,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAmB,EAAE,OAAe;IAC7D,MAAM,IAAI,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC9D,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,OAAO,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;AACjE,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAgC,EAAE,GAAG,GAAG,IAAI,IAAI,EAAE;IAC5E,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACzB,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACpC,OAAO,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,SAAS,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC;AACjE,CAAC"}