nomkit 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/LICENSE.txt +21 -0
  2. package/dist/_virtual/_rolldown/runtime.js +27 -0
  3. package/dist/adapters/index.d.ts +15 -0
  4. package/dist/adapters/index.js +6 -0
  5. package/dist/cli/commands/push.d.ts +6 -0
  6. package/dist/cli/commands/push.js +143 -0
  7. package/dist/cli/index.d.ts +4 -0
  8. package/dist/cli/index.js +18 -0
  9. package/dist/cli/lib/collection_sync.d.ts +107 -0
  10. package/dist/cli/lib/collection_sync.js +158 -0
  11. package/dist/cli/lib/config_loader.d.ts +15 -0
  12. package/dist/cli/lib/config_loader.js +43 -0
  13. package/dist/cli/lib/hash.d.ts +22 -0
  14. package/dist/cli/lib/hash.js +63 -0
  15. package/dist/cli/lib/migrations.d.ts +6 -0
  16. package/dist/cli/lib/migrations.js +17 -0
  17. package/dist/client/index.d.ts +13 -0
  18. package/dist/client/index.js +34 -0
  19. package/dist/core/nomba_api/banks.d.ts +14 -0
  20. package/dist/core/nomba_api/banks.js +0 -0
  21. package/dist/core/nomba_api/charge-tokenized-card.d.ts +33 -0
  22. package/dist/core/nomba_api/charge-tokenized-card.js +0 -0
  23. package/dist/core/nomba_api/checkout.d.ts +44 -0
  24. package/dist/core/nomba_api/checkout.js +0 -0
  25. package/dist/core/nomba_api/get_checkout.d.ts +57 -0
  26. package/dist/core/nomba_api/get_checkout.js +0 -0
  27. package/dist/core/nomba_api/index.d.ts +313 -0
  28. package/dist/core/nomba_api/index.js +179 -0
  29. package/dist/core/nomba_api/lib/utils.d.ts +235 -0
  30. package/dist/core/nomba_api/lib/utils.js +313 -0
  31. package/dist/core/nomba_api/list-tokenized-cards.d.ts +24 -0
  32. package/dist/core/nomba_api/list-tokenized-cards.js +0 -0
  33. package/dist/core/nomba_api/token-manager/index.d.ts +51 -0
  34. package/dist/core/nomba_api/token-manager/index.js +109 -0
  35. package/dist/core/pg_db/index.d.ts +108 -0
  36. package/dist/core/pg_db/index.js +76 -0
  37. package/dist/core/pg_db/migrations/20260703085901_wealthy_blacklash/migration.sql +120 -0
  38. package/dist/core/pg_db/migrations/20260703085901_wealthy_blacklash/snapshot.json +1616 -0
  39. package/dist/core/pg_db/relations.d.ts +46 -0
  40. package/dist/core/pg_db/relations.js +83 -0
  41. package/dist/core/pg_db/schema.d.ts +1138 -0
  42. package/dist/core/pg_db/schema.js +124 -0
  43. package/dist/endpoints/customers/api.js +51 -0
  44. package/dist/endpoints/entitlements/api.js +42 -0
  45. package/dist/endpoints/routes.d.ts +15 -0
  46. package/dist/endpoints/routes.js +15 -0
  47. package/dist/endpoints/subscriptions/api.js +263 -0
  48. package/dist/endpoints/subscriptions/utils.js +105 -0
  49. package/dist/endpoints/webhooks/invoice/api.js +28 -0
  50. package/dist/endpoints/webhooks/nomba/api.js +76 -0
  51. package/dist/endpoints/webhooks/nomba/utils.js +36 -0
  52. package/dist/index.d.ts +204 -0
  53. package/dist/index.js +175 -0
  54. package/dist/lib/utils.d.ts +21 -0
  55. package/dist/lib/utils.js +41 -0
  56. package/dist/node_modules/.pnpm/@better-fetch_fetch@1.3.1/node_modules/@better-fetch/fetch/dist/index.js +475 -0
  57. package/dist/package.js +4 -0
  58. package/dist/queue/backends/pglite/backend.d.ts +43 -0
  59. package/dist/queue/backends/pglite/backend.js +33 -0
  60. package/dist/queue/backends/pglite/index.d.ts +4 -0
  61. package/dist/queue/backends/pglite/index.js +4 -0
  62. package/dist/queue/backends/pglite/migrations/schema.d.ts +4 -0
  63. package/dist/queue/backends/pglite/migrations/schema.js +37 -0
  64. package/dist/queue/backends/pglite/notification-channel.d.ts +17 -0
  65. package/dist/queue/backends/pglite/notification-channel.js +61 -0
  66. package/dist/queue/backends/pglite/repository.d.ts +38 -0
  67. package/dist/queue/backends/pglite/repository.js +299 -0
  68. package/dist/queue/backends/redis/index.d.ts +7 -0
  69. package/dist/queue/backends/redis/index.js +1 -0
  70. package/dist/queue/client/index.d.ts +12 -0
  71. package/dist/queue/client/index.js +31 -0
  72. package/dist/queue/endpoints/api.d.ts +53 -0
  73. package/dist/queue/endpoints/api.js +45 -0
  74. package/dist/queue/endpoints/routes.d.ts +32 -0
  75. package/dist/queue/endpoints/routes.js +5 -0
  76. package/dist/queue/init.d.ts +27 -0
  77. package/dist/queue/init.js +31 -0
  78. package/dist/queue/lib/billing.d.ts +25 -0
  79. package/dist/queue/lib/billing.js +87 -0
  80. package/dist/queue/lib/utils.d.ts +30 -0
  81. package/dist/queue/lib/utils.js +35 -0
  82. package/package.json +71 -0
@@ -0,0 +1,61 @@
1
+ import { BaseNotificationChannel } from "agenda";
2
+ //#region queue/backends/pglite/notification-channel.ts
3
+ const CHANNEL = "agenda_jobs_channel";
4
+ /**
5
+ * Real-time notifications for the PGlite backend using NOTIFY/LISTEN.
6
+ */
7
+ var PgliteNotificationChannel = class extends BaseNotificationChannel {
8
+ db;
9
+ unlisten = null;
10
+ constructor(db) {
11
+ super();
12
+ this.db = db;
13
+ }
14
+ async connect() {
15
+ if (this.state === "connected" || this.state === "connecting") return;
16
+ this.setState("connecting");
17
+ try {
18
+ this.unlisten = await this.db.listen(CHANNEL, (payload) => {
19
+ try {
20
+ const raw = JSON.parse(payload);
21
+ const notification = {
22
+ jobId: raw.jobId,
23
+ jobName: raw.jobName,
24
+ nextRunAt: raw.nextRunAt ? new Date(raw.nextRunAt) : null,
25
+ priority: raw.priority ?? 0,
26
+ timestamp: new Date(raw.timestamp ?? Date.now()),
27
+ source: raw.source
28
+ };
29
+ this.notifyHandlers(notification);
30
+ } catch (err) {
31
+ this.emit("error", err);
32
+ }
33
+ });
34
+ this.setState("connected");
35
+ } catch (err) {
36
+ this.setState("error");
37
+ this.emit("error", err);
38
+ throw err;
39
+ }
40
+ }
41
+ async disconnect() {
42
+ if (this.unlisten) {
43
+ await this.unlisten();
44
+ this.unlisten = null;
45
+ }
46
+ this.setState("disconnected");
47
+ }
48
+ async publish(notification) {
49
+ const payload = JSON.stringify({
50
+ jobId: notification.jobId,
51
+ jobName: notification.jobName,
52
+ nextRunAt: notification.nextRunAt,
53
+ priority: notification.priority,
54
+ timestamp: notification.timestamp,
55
+ source: notification.source
56
+ });
57
+ await this.db.query(`NOTIFY ${CHANNEL}, '${payload.replace(/'/g, "''")}'`);
58
+ }
59
+ };
60
+ //#endregion
61
+ export { PgliteNotificationChannel };
@@ -0,0 +1,38 @@
1
+ import { JobId, JobParameters, JobRepository, JobRepositoryOptions, JobsOverview, JobsQueryOptions, JobsResult, RemoveJobsOptions } from "agenda";
2
+ import { PGlite } from "@electric-sql/pglite";
3
+
4
+ //#region queue/backends/pglite/repository.d.ts
5
+ interface PgliteRepositoryOptions {
6
+ /** Table name to store jobs in.'.
7
+ * @default "agenda_jobs"
8
+ */
9
+ table?: string;
10
+ }
11
+ declare class PgliteRepository implements JobRepository {
12
+ private readonly db;
13
+ private readonly table;
14
+ private ready;
15
+ constructor(db: PGlite, options?: PgliteRepositoryOptions);
16
+ connect(): Promise<void>;
17
+ queryJobs(options?: JobsQueryOptions): Promise<JobsResult>;
18
+ getJobsOverview(): Promise<JobsOverview[]>;
19
+ getDistinctJobNames(): Promise<string[]>;
20
+ getJobById(id: string): Promise<JobParameters | null>;
21
+ getQueueSize(): Promise<number>;
22
+ private buildRemoveWhere;
23
+ removeJobs(options: RemoveJobsOptions): Promise<number>;
24
+ disableJobs(options: RemoveJobsOptions): Promise<number>;
25
+ enableJobs(options: RemoveJobsOptions): Promise<number>;
26
+ purgeAllJobs(): Promise<number>;
27
+ saveJob<DATA = unknown>(job: JobParameters<DATA>, _options?: JobRepositoryOptions): Promise<JobParameters<DATA>>;
28
+ saveJobState(job: JobParameters, _options?: JobRepositoryOptions): Promise<void>;
29
+ lockJob(job: JobParameters, _options?: JobRepositoryOptions): Promise<JobParameters | undefined>;
30
+ unlockJob(job: JobParameters): Promise<void>;
31
+ unlockJobs(jobIds: (JobId | string)[]): Promise<void>;
32
+ getNextJobToRun(jobName: string, nextScanAt: Date, lockDeadline: Date, now?: Date, _options?: JobRepositoryOptions): Promise<JobParameters | undefined>;
33
+ private insert;
34
+ private upsertById;
35
+ private upsertByMatch;
36
+ }
37
+ //#endregion
38
+ export { PgliteRepository, PgliteRepositoryOptions };
@@ -0,0 +1,299 @@
1
+ import { migratePGlite } from "./migrations/schema.js";
2
+ import { computeJobState, toJobId } from "agenda";
3
+ //#region queue/backends/pglite/repository.ts
4
+ function toDate(v) {
5
+ return v == null ? void 0 : new Date(v);
6
+ }
7
+ function rowToJob(row) {
8
+ return {
9
+ _id: toJobId(row.id),
10
+ name: row.name,
11
+ type: row.type ?? "normal",
12
+ priority: row.priority,
13
+ nextRunAt: toDate(row.next_run_at) ?? null,
14
+ lastModifiedBy: row.last_modified_by ?? void 0,
15
+ lockedAt: toDate(row.locked_at),
16
+ lastFinishedAt: toDate(row.last_finished_at),
17
+ lastRunAt: toDate(row.last_run_at),
18
+ repeatInterval: row.repeat_interval ?? void 0,
19
+ repeatTimezone: row.repeat_timezone ?? void 0,
20
+ repeatAt: row.repeat_at ?? void 0,
21
+ data: row.data ?? {},
22
+ unique: row.is_unique ?? void 0,
23
+ uniqueOpts: row.unique_opts ?? void 0,
24
+ failReason: row.fail_reason ?? void 0,
25
+ failCount: row.fail_count,
26
+ failedAt: toDate(row.failed_at),
27
+ disabled: row.disabled,
28
+ progress: row.progress ?? void 0
29
+ };
30
+ }
31
+ function rowToJobWithState(row) {
32
+ const job = rowToJob(row);
33
+ return {
34
+ ...job,
35
+ _id: job._id,
36
+ state: computeJobState(job)
37
+ };
38
+ }
39
+ const SORT_COLUMN_MAP = {
40
+ nextRunAt: "next_run_at",
41
+ lastRunAt: "last_run_at",
42
+ lastFinishedAt: "last_finished_at",
43
+ priority: "priority",
44
+ name: "name",
45
+ data: "data"
46
+ };
47
+ /**
48
+ * Agenda's `unique` field uses dotted paths like 'data.userId' as keys.
49
+ * Build a nested JSON object (rooted at `data`) suitable for a `data @> ...`
50
+ * containment query, e.g. { 'data.userId': 5 } -> { userId: 5 }.
51
+ */
52
+ function uniqueToDataContainment(unique) {
53
+ const root = {};
54
+ for (const [path, value] of Object.entries(unique)) {
55
+ const parts = (path.startsWith("data.") ? path.slice(5) : path).split(".");
56
+ let cursor = root;
57
+ for (let i = 0; i < parts.length - 1; i++) cursor = cursor[parts[i]] ??= {};
58
+ cursor[parts[parts.length - 1]] = value;
59
+ }
60
+ return root;
61
+ }
62
+ var PgliteRepository = class {
63
+ db;
64
+ table;
65
+ ready = null;
66
+ constructor(db, options = {}) {
67
+ this.db = db;
68
+ this.table = options.table ?? "agenda_jobs";
69
+ }
70
+ async connect() {
71
+ if (this.ready) return this.ready;
72
+ this.ready = (async () => {
73
+ await this.db.exec(migratePGlite(this.table));
74
+ })();
75
+ return this.ready;
76
+ }
77
+ async queryJobs(options = {}) {
78
+ const where = [];
79
+ const params = [];
80
+ const add = (clause, value) => {
81
+ params.push(value);
82
+ where.push(clause.replace("?", `$${params.length}`));
83
+ };
84
+ if (options.name) add("name = ?", options.name);
85
+ if (options.names?.length) add("name = ANY(?)", options.names);
86
+ if (options.id) add("id = ?", options.id);
87
+ if (options.ids?.length) add("id = ANY(?)", options.ids);
88
+ if (options.search) add("name ILIKE ?", `%${options.search}%`);
89
+ if (options.data !== void 0) add("data @> ?::jsonb", JSON.stringify(options.data));
90
+ if (options.includeDisabled === false) where.push("disabled = FALSE");
91
+ let sql = `SELECT * FROM "${this.table}"`;
92
+ if (where.length) sql += ` WHERE ${where.join(" AND ")}`;
93
+ const sortField = options.sort ? Object.keys(options.sort)[0] : void 0;
94
+ const sortDir = options.sort && Object.values(options.sort)[0] === -1 ? "DESC" : "ASC";
95
+ const column = sortField && SORT_COLUMN_MAP[sortField] || "next_run_at";
96
+ sql += ` ORDER BY ${column} ${sortDir} NULLS LAST`;
97
+ if (options.limit !== void 0) {
98
+ params.push(options.limit);
99
+ sql += ` LIMIT $${params.length}`;
100
+ }
101
+ if (options.skip !== void 0) {
102
+ params.push(options.skip);
103
+ sql += ` OFFSET $${params.length}`;
104
+ }
105
+ let jobs = (await this.db.query(sql, params)).rows.map(rowToJobWithState);
106
+ if (options.state) jobs = jobs.filter((j) => j.state === options.state);
107
+ const countResult = await this.db.query(`SELECT COUNT(*)::text AS count FROM "${this.table}"${where.length ? ` WHERE ${where.join(" AND ")}` : ""}`, params.slice(0, where.length));
108
+ return {
109
+ jobs,
110
+ total: Number(countResult.rows[0]?.count ?? jobs.length)
111
+ };
112
+ }
113
+ async getJobsOverview() {
114
+ return (await this.db.query(`
115
+ SELECT
116
+ name,
117
+ COUNT(*)::text AS total,
118
+ COUNT(*) FILTER (WHERE disabled = TRUE)::text AS paused,
119
+ COUNT(*) FILTER (WHERE locked_at IS NOT NULL AND last_finished_at IS NULL)::text AS running,
120
+ COUNT(*) FILTER (WHERE repeat_interval IS NOT NULL)::text AS repeating,
121
+ COUNT(*) FILTER (WHERE fail_count > 0 AND last_finished_at IS NOT NULL AND failed_at IS NOT NULL)::text AS failed,
122
+ COUNT(*) FILTER (WHERE next_run_at IS NULL AND last_finished_at IS NOT NULL AND fail_count = 0)::text AS completed,
123
+ COUNT(*) FILTER (WHERE next_run_at IS NOT NULL AND locked_at IS NULL AND disabled = FALSE)::text AS scheduled
124
+ FROM "${this.table}"
125
+ GROUP BY name
126
+ ORDER BY name
127
+ `)).rows.map((r) => ({
128
+ name: r.name,
129
+ total: Number(r.total),
130
+ running: Number(r.running),
131
+ scheduled: Number(r.scheduled),
132
+ queued: Number(r.scheduled),
133
+ completed: Number(r.completed),
134
+ failed: Number(r.failed),
135
+ repeating: Number(r.repeating),
136
+ paused: Number(r.paused)
137
+ }));
138
+ }
139
+ async getDistinctJobNames() {
140
+ return (await this.db.query(`SELECT DISTINCT name FROM "${this.table}" ORDER BY name`)).rows.map((r) => r.name);
141
+ }
142
+ async getJobById(id) {
143
+ const result = await this.db.query(`SELECT * FROM "${this.table}" WHERE id = $1`, [id]);
144
+ return result.rows[0] ? rowToJob(result.rows[0]) : null;
145
+ }
146
+ async getQueueSize() {
147
+ const result = await this.db.query(`SELECT COUNT(*)::text AS count FROM "${this.table}" WHERE next_run_at <= now() AND disabled = FALSE AND locked_at IS NULL`);
148
+ return Number(result.rows[0]?.count ?? 0);
149
+ }
150
+ buildRemoveWhere(options) {
151
+ const where = [];
152
+ const params = [];
153
+ const add = (clause, value) => {
154
+ params.push(value);
155
+ where.push(clause.replace("?", `$${params.length}`));
156
+ };
157
+ if (options.id) add("id = ?", String(options.id));
158
+ if (options.ids?.length) add("id = ANY(?)", options.ids.map(String));
159
+ if (options.name) add("name = ?", options.name);
160
+ if (options.names?.length) add("name = ANY(?)", options.names);
161
+ if (options.notNames?.length) add("NOT (name = ANY(?))", options.notNames);
162
+ if (options.data !== void 0) add("data @> ?::jsonb", JSON.stringify(options.data));
163
+ return {
164
+ clause: where.join(" AND "),
165
+ params
166
+ };
167
+ }
168
+ async removeJobs(options) {
169
+ const { clause, params } = this.buildRemoveWhere(options);
170
+ if (!clause) return 0;
171
+ return (await this.db.query(`DELETE FROM "${this.table}" WHERE ${clause}`, params)).affectedRows ?? 0;
172
+ }
173
+ async disableJobs(options) {
174
+ const { clause, params } = this.buildRemoveWhere(options);
175
+ if (!clause) return 0;
176
+ return (await this.db.query(`UPDATE "${this.table}" SET disabled = TRUE, updated_at = now() WHERE ${clause}`, params)).affectedRows ?? 0;
177
+ }
178
+ async enableJobs(options) {
179
+ const { clause, params } = this.buildRemoveWhere(options);
180
+ if (!clause) return 0;
181
+ return (await this.db.query(`UPDATE "${this.table}" SET disabled = FALSE, updated_at = now() WHERE ${clause}`, params)).affectedRows ?? 0;
182
+ }
183
+ async purgeAllJobs() {
184
+ return (await this.db.query(`DELETE FROM "${this.table}"`)).affectedRows ?? 0;
185
+ }
186
+ async saveJob(job, _options) {
187
+ const values = {
188
+ name: job.name,
189
+ type: job.type ?? "normal",
190
+ priority: job.priority ?? 0,
191
+ next_run_at: job.nextRunAt ?? null,
192
+ last_modified_by: job.lastModifiedBy ?? null,
193
+ locked_at: job.lockedAt ?? null,
194
+ last_finished_at: job.lastFinishedAt ?? null,
195
+ last_run_at: job.lastRunAt ?? null,
196
+ repeat_interval: job.repeatInterval != null ? String(job.repeatInterval) : null,
197
+ repeat_timezone: job.repeatTimezone ?? null,
198
+ repeat_at: job.repeatAt ?? null,
199
+ data: JSON.stringify(job.data ?? {}),
200
+ is_unique: job.unique != null ? JSON.stringify(job.unique) : null,
201
+ unique_opts: job.uniqueOpts != null ? JSON.stringify(job.uniqueOpts) : null,
202
+ fail_reason: job.failReason ?? null,
203
+ fail_count: job.failCount ?? 0,
204
+ failed_at: job.failedAt ?? null,
205
+ disabled: job.disabled ?? false,
206
+ progress: job.progress ?? null
207
+ };
208
+ if (job._id) return rowToJob(await this.upsertById(String(job._id), values));
209
+ if (job.type === "single") return rowToJob(await this.upsertByMatch("name = $1 AND type = $2", [job.name, "single"], values));
210
+ if (job.unique != null) {
211
+ const insertOnly = job.uniqueOpts?.insertOnly;
212
+ return rowToJob(await this.upsertByMatch("name = $1 AND data @> $2::jsonb", [job.name, JSON.stringify(uniqueToDataContainment(job.unique))], values, { insertOnly: !!insertOnly }));
213
+ }
214
+ return rowToJob(await this.insert(values));
215
+ }
216
+ async saveJobState(job, _options) {
217
+ if (!job._id) throw new Error("saveJobState requires job._id");
218
+ await this.db.query(`UPDATE "${this.table}" SET
219
+ locked_at = $1, last_finished_at = $2, last_run_at = $3, next_run_at = $4,
220
+ progress = $5, fail_reason = $6, fail_count = $7, failed_at = $8, updated_at = now()
221
+ WHERE id = $9`, [
222
+ job.lockedAt ?? null,
223
+ job.lastFinishedAt ?? null,
224
+ job.lastRunAt ?? null,
225
+ job.nextRunAt ?? null,
226
+ job.progress ?? null,
227
+ job.failReason ?? null,
228
+ job.failCount ?? 0,
229
+ job.failedAt ?? null,
230
+ String(job._id)
231
+ ]);
232
+ }
233
+ async lockJob(job, _options) {
234
+ if (!job._id) return void 0;
235
+ const result = await this.db.query(`UPDATE "${this.table}"
236
+ SET locked_at = now(), updated_at = now()
237
+ WHERE id = $1 AND locked_at IS NULL
238
+ RETURNING *`, [String(job._id)]);
239
+ return result.rows[0] ? rowToJob(result.rows[0]) : void 0;
240
+ }
241
+ async unlockJob(job) {
242
+ if (!job._id) return;
243
+ await this.db.query(`UPDATE "${this.table}" SET locked_at = NULL, updated_at = now() WHERE id = $1`, [String(job._id)]);
244
+ }
245
+ async unlockJobs(jobIds) {
246
+ if (!jobIds.length) return;
247
+ await this.db.query(`UPDATE "${this.table}" SET locked_at = NULL, updated_at = now() WHERE id = ANY($1)`, [jobIds.map(String)]);
248
+ }
249
+ async getNextJobToRun(jobName, nextScanAt, lockDeadline, now = /* @__PURE__ */ new Date(), _options) {
250
+ const result = await this.db.query(`WITH candidate AS (
251
+ SELECT id FROM "${this.table}"
252
+ WHERE name = $1
253
+ AND disabled = FALSE
254
+ AND next_run_at <= $2
255
+ AND (locked_at IS NULL OR locked_at <= $3)
256
+ ORDER BY priority DESC, next_run_at ASC
257
+ FOR UPDATE SKIP LOCKED
258
+ LIMIT 1
259
+ )
260
+ UPDATE "${this.table}" t
261
+ SET locked_at = $4, updated_at = now()
262
+ FROM candidate
263
+ WHERE t.id = candidate.id
264
+ RETURNING t.*`, [
265
+ jobName,
266
+ nextScanAt,
267
+ lockDeadline,
268
+ now
269
+ ]);
270
+ return result.rows[0] ? rowToJob(result.rows[0]) : void 0;
271
+ }
272
+ async insert(values) {
273
+ const cols = Object.keys(values);
274
+ const placeholders = cols.map((_, i) => `$${i + 1}`);
275
+ return (await this.db.query(`INSERT INTO "${this.table}" (${cols.join(", ")})
276
+ VALUES (${placeholders.join(", ")})
277
+ RETURNING *`, Object.values(values))).rows[0];
278
+ }
279
+ async upsertById(id, values) {
280
+ const setClause = Object.keys(values).map((c, i) => `${c} = $${i + 2}`).join(", ");
281
+ const result = await this.db.query(`UPDATE "${this.table}" SET ${setClause}, updated_at = now() WHERE id = $1 RETURNING *`, [id, ...Object.values(values)]);
282
+ if (result.rows[0]) return result.rows[0];
283
+ return this.insert({
284
+ id,
285
+ ...values
286
+ });
287
+ }
288
+ async upsertByMatch(matchClause, matchParams, values, opts = {}) {
289
+ const existing = await this.db.query(`SELECT * FROM "${this.table}" WHERE ${matchClause} LIMIT 1`, matchParams);
290
+ if (existing.rows[0]) {
291
+ if (opts.insertOnly) return existing.rows[0];
292
+ const setClause = Object.keys(values).map((c, i) => `${c} = $${i + 2}`).join(", ");
293
+ return (await this.db.query(`UPDATE "${this.table}" SET ${setClause}, updated_at = now() WHERE id = $1 RETURNING *`, [existing.rows[0].id, ...Object.values(values)])).rows[0];
294
+ }
295
+ return this.insert(values);
296
+ }
297
+ };
298
+ //#endregion
299
+ export { PgliteRepository };
@@ -0,0 +1,7 @@
1
+ export * from "@agendajs/redis-backend";
2
+
3
+ //#region queue/backends/redis/index.d.ts
4
+
5
+ import * as import__agendajs_redis_backend from "@agendajs/redis-backend";
6
+ //#endregion
7
+ export { import__agendajs_redis_backend as index_d_exports };
@@ -0,0 +1 @@
1
+ export * from "@agendajs/redis-backend";
@@ -0,0 +1,12 @@
1
+ import { routes } from "../endpoints/routes.js";
2
+ import { InferEndpoint } from "../../lib/utils.js";
3
+ import { Endpoint } from "better-call";
4
+
5
+ //#region queue/client/index.d.ts
6
+ interface QueueClientOptions {
7
+ baseURL?: string;
8
+ }
9
+ declare function createQueueClient<TServer extends Record<string, any> = typeof routes>(options?: QueueClientOptions): InferRouteAPI<TServer>;
10
+ type InferRouteAPI<T> = { [K in keyof T]: T[K] extends Endpoint<any, any, any, any, any, any, any, any> ? (request: InferEndpoint<T[K]>["request"]) => Promise<InferEndpoint<T[K]>["response"]> : T[K] extends Record<string, any> ? InferRouteAPI<T[K]> : never };
11
+ //#endregion
12
+ export { InferRouteAPI, QueueClientOptions, createQueueClient };
@@ -0,0 +1,31 @@
1
+ import { createFetch } from "../../node_modules/.pnpm/@better-fetch_fetch@1.3.1/node_modules/@better-fetch/fetch/dist/index.js";
2
+ //#region queue/client/index.ts
3
+ function createQueueClient(options) {
4
+ const baseUrl = (options?.baseURL ?? "/queue").replace(/\/+$/, "");
5
+ let queueUrl;
6
+ if (baseUrl === "") queueUrl = "/queue";
7
+ else if (baseUrl.endsWith("/queue")) queueUrl = baseUrl;
8
+ else queueUrl = `${baseUrl}/queue`;
9
+ const supportsCredentials = typeof globalThis.Request !== "undefined" && "credentials" in Request.prototype;
10
+ const $fetch = createFetch({
11
+ baseURL: queueUrl,
12
+ throw: true,
13
+ ...supportsCredentials ? { credentials: "include" } : {}
14
+ });
15
+ const createProxy = (path = []) => new Proxy(() => {}, {
16
+ get(_, prop) {
17
+ if (typeof prop !== "string") return;
18
+ if (prop === "then" || prop === "catch" || prop === "finally") return;
19
+ return createProxy([...path, prop]);
20
+ },
21
+ async apply(_, __, args) {
22
+ return $fetch("/" + path.map((segment) => segment.replace(/[A-Z]/g, (char) => `-${char.toLowerCase()}`)).join("/"), {
23
+ method: "POST",
24
+ body: args[0] ?? {}
25
+ });
26
+ }
27
+ });
28
+ return createProxy();
29
+ }
30
+ //#endregion
31
+ export { createQueueClient };
@@ -0,0 +1,53 @@
1
+ import z from "zod";
2
+
3
+ //#region queue/endpoints/api.d.ts
4
+ declare const CreateBillingSchema: z.ZodObject<{
5
+ id: z.ZodString;
6
+ customerId: z.ZodString;
7
+ productId: z.ZodString;
8
+ currentPeriodStart: z.ZodString;
9
+ currentPeriodEnd: z.ZodString;
10
+ interval: z.ZodString;
11
+ billingDetails: z.ZodObject<{
12
+ tokenKey: z.ZodString;
13
+ }, z.core.$strip>;
14
+ retryPolicy: z.ZodTuple<[z.ZodLiteral<"5s">, z.ZodLiteral<"6s">, z.ZodLiteral<"10s">], null>;
15
+ retryCount: z.ZodNumber;
16
+ skipImmediate: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
17
+ }, z.core.$strip>;
18
+ type CreateBillingInfo = z.infer<typeof CreateBillingSchema>;
19
+ declare const createJob: import("better-call").Endpoint<"/create-job", "POST", {
20
+ id: string;
21
+ customerId: string;
22
+ productId: string;
23
+ currentPeriodStart: string;
24
+ currentPeriodEnd: string;
25
+ interval: string;
26
+ billingDetails: {
27
+ tokenKey: string;
28
+ };
29
+ retryPolicy: ["5s", "6s", "10s"];
30
+ retryCount: number;
31
+ skipImmediate?: boolean | undefined;
32
+ }, Record<string, any> | undefined, [], {
33
+ status: string;
34
+ }, {
35
+ scope: "http";
36
+ }, undefined>;
37
+ declare const DeleteBillingSchema: z.ZodObject<{
38
+ id: z.ZodString;
39
+ customerId: z.ZodString;
40
+ productId: z.ZodString;
41
+ }, z.core.$strip>;
42
+ type DeleteBillingInfo = z.infer<typeof DeleteBillingSchema>;
43
+ declare const deleteJob: import("better-call").Endpoint<"/delete-job", "POST", {
44
+ id: string;
45
+ customerId: string;
46
+ productId: string;
47
+ }, Record<string, any> | undefined, [], {
48
+ status: string;
49
+ }, {
50
+ scope: "http";
51
+ }, undefined>;
52
+ //#endregion
53
+ export { CreateBillingInfo, DeleteBillingInfo, createJob, deleteJob };
@@ -0,0 +1,45 @@
1
+ import { __exportAll } from "../../_virtual/_rolldown/runtime.js";
2
+ import { defineQueueMethod } from "../lib/utils.js";
3
+ import z from "zod";
4
+ //#region queue/endpoints/api.ts
5
+ var api_exports = /* @__PURE__ */ __exportAll({
6
+ createJob: () => createJob,
7
+ deleteJob: () => deleteJob
8
+ });
9
+ const createJob = defineQueueMethod({
10
+ input: z.object({
11
+ id: z.string("Subscription identifier is required").min(1).max(70),
12
+ customerId: z.string().min(1).max(70),
13
+ productId: z.string("Product identifier is required").min(1).max(70),
14
+ currentPeriodStart: z.string().min(1).max(40),
15
+ currentPeriodEnd: z.string().min(1).max(40),
16
+ interval: z.string({ error: "product interval is required" }).min(1).max(20),
17
+ billingDetails: z.object({ tokenKey: z.string() }),
18
+ retryPolicy: z.tuple([
19
+ z.literal("5s"),
20
+ z.literal("6s"),
21
+ z.literal("10s")
22
+ ]),
23
+ retryCount: z.number(),
24
+ skipImmediate: z.boolean().default(false).optional()
25
+ }),
26
+ route: { path: "/create-job" }
27
+ }, async (ctx) => {
28
+ const input = ctx.input;
29
+ await ctx.queue.billing.subscribe(input);
30
+ return { status: "queued" };
31
+ });
32
+ const deleteJob = defineQueueMethod({
33
+ input: z.object({
34
+ id: z.string(),
35
+ customerId: z.string().min(1).max(70),
36
+ productId: z.string("Product identifier is required").min(1).max(70)
37
+ }),
38
+ route: { path: "/delete-job" }
39
+ }, async (ctx) => {
40
+ const input = ctx.input;
41
+ await ctx.queue.billing.unsubscribe(input);
42
+ return { status: "deleted" };
43
+ });
44
+ //#endregion
45
+ export { api_exports, createJob, deleteJob };
@@ -0,0 +1,32 @@
1
+ //#region queue/endpoints/routes.d.ts
2
+ declare const routes: {
3
+ readonly createJob: import("better-call").Endpoint<"/create-job", "POST", {
4
+ id: string;
5
+ customerId: string;
6
+ productId: string;
7
+ currentPeriodStart: string;
8
+ currentPeriodEnd: string;
9
+ interval: string;
10
+ billingDetails: {
11
+ tokenKey: string;
12
+ };
13
+ retryPolicy: ["5s", "6s", "10s"];
14
+ retryCount: number;
15
+ skipImmediate?: boolean | undefined;
16
+ }, Record<string, any> | undefined, [], {
17
+ status: string;
18
+ }, {
19
+ scope: "http";
20
+ }, undefined>;
21
+ readonly deleteJob: import("better-call").Endpoint<"/delete-job", "POST", {
22
+ id: string;
23
+ customerId: string;
24
+ productId: string;
25
+ }, Record<string, any> | undefined, [], {
26
+ status: string;
27
+ }, {
28
+ scope: "http";
29
+ }, undefined>;
30
+ };
31
+ //#endregion
32
+ export { routes };
@@ -0,0 +1,5 @@
1
+ import { api_exports } from "./api.js";
2
+ //#region queue/endpoints/routes.ts
3
+ const routes = { ...api_exports };
4
+ //#endregion
5
+ export { routes };
@@ -0,0 +1,27 @@
1
+ import { InferRouteAPI } from "./client/index.js";
2
+ import { PGliteBackend } from "./backends/pglite/backend.js";
3
+ import { index_d_exports } from "./backends/redis/index.js";
4
+ import { Billing } from "./lib/billing.js";
5
+ import { nomkitWebhooks } from "../endpoints/routes.js";
6
+ import { Agenda } from "agenda";
7
+
8
+ //#region queue/init.d.ts
9
+ type Backends = PGliteBackend | index_d_exports.RedisBackend;
10
+ interface QueueContext {
11
+ billing: Billing;
12
+ agenda: Agenda;
13
+ nomkit: InferRouteAPI<typeof nomkitWebhooks>;
14
+ }
15
+ interface CreateQueueOptions {
16
+ backend: Backends;
17
+ nomkitConfig: {
18
+ baseURL: string;
19
+ };
20
+ }
21
+ declare function createQueue(args: CreateQueueOptions): {
22
+ init(): Promise<void>;
23
+ terminate(): Promise<void>;
24
+ handler: (request: Request) => Promise<Response>;
25
+ };
26
+ //#endregion
27
+ export { Backends, CreateQueueOptions, QueueContext, createQueue };
@@ -0,0 +1,31 @@
1
+ import { routes } from "./endpoints/routes.js";
2
+ import { Billing } from "./lib/billing.js";
3
+ import { createNomKitClient } from "../client/index.js";
4
+ import { createRouter } from "better-call";
5
+ import { Agenda } from "agenda";
6
+ //#region queue/init.ts
7
+ function createQueue(args) {
8
+ if (!args) throw new Error("Missing required createQueue arguments");
9
+ if (!args?.nomkitConfig) throw new Error("Missing required nomkit arguments");
10
+ if (!args?.nomkitConfig?.baseURL) throw new Error("Missing required nomkit baseURL");
11
+ const agenda = new Agenda({ backend: args.backend });
12
+ const nomkitAPI = createNomKitClient({ baseURL: args.nomkitConfig.baseURL });
13
+ return {
14
+ async init() {
15
+ return agenda.start();
16
+ },
17
+ async terminate() {
18
+ return agenda.stop(true);
19
+ },
20
+ handler: createRouter(routes, { routerContext: {
21
+ agenda,
22
+ billing: new Billing({
23
+ agenda,
24
+ nomkit: nomkitAPI
25
+ }),
26
+ nomkit: nomkitAPI
27
+ } }).handler
28
+ };
29
+ }
30
+ //#endregion
31
+ export { createQueue };
@@ -0,0 +1,25 @@
1
+ import { CreateBillingInfo, DeleteBillingInfo } from "../endpoints/api.js";
2
+ import { QueueContext } from "../init.js";
3
+
4
+ //#region queue/lib/billing.d.ts
5
+ type Result<T, E = Error> = {
6
+ ok: true;
7
+ value: T;
8
+ error?: never;
9
+ } | {
10
+ ok: false;
11
+ value?: never;
12
+ error: E;
13
+ };
14
+ declare class Billing {
15
+ private hasInit;
16
+ private agenda;
17
+ private nomkit;
18
+ private readonly ID;
19
+ private wf;
20
+ constructor(args: Omit<QueueContext, "billing">);
21
+ subscribe(args: CreateBillingInfo): Promise<void>;
22
+ unsubscribe(args: DeleteBillingInfo): Promise<void>;
23
+ }
24
+ //#endregion
25
+ export { Billing, Result };