@tekki-forge/runtime-api 0.3.2 → 0.4.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 (45) hide show
  1. package/dist/bootstrap.js +7 -0
  2. package/dist/bootstrap.js.map +1 -1
  3. package/dist/computed.js +1 -1
  4. package/dist/computed.js.map +1 -1
  5. package/dist/controllers.d.ts +52 -0
  6. package/dist/controllers.js +69 -1
  7. package/dist/controllers.js.map +1 -1
  8. package/dist/errors.js +4 -0
  9. package/dist/errors.js.map +1 -1
  10. package/dist/events.d.ts +12 -0
  11. package/dist/events.js +26 -0
  12. package/dist/events.js.map +1 -1
  13. package/dist/exports.d.ts +34 -0
  14. package/dist/exports.js +170 -0
  15. package/dist/exports.js.map +1 -0
  16. package/dist/index.d.ts +3 -0
  17. package/dist/index.js +3 -0
  18. package/dist/index.js.map +1 -1
  19. package/dist/notifications.d.ts +2 -0
  20. package/dist/notifications.js +8 -1
  21. package/dist/notifications.js.map +1 -1
  22. package/dist/observability.d.ts +20 -0
  23. package/dist/observability.js +47 -0
  24. package/dist/observability.js.map +1 -0
  25. package/dist/outbox.d.ts +21 -0
  26. package/dist/outbox.js +98 -0
  27. package/dist/outbox.js.map +1 -0
  28. package/dist/plugin.d.ts +2 -0
  29. package/dist/plugin.js.map +1 -1
  30. package/dist/query.d.ts +3 -0
  31. package/dist/query.js +13 -2
  32. package/dist/query.js.map +1 -1
  33. package/dist/search.d.ts +1 -0
  34. package/dist/search.js +14 -3
  35. package/dist/search.js.map +1 -1
  36. package/dist/seed.js +5 -3
  37. package/dist/seed.js.map +1 -1
  38. package/dist/serialize.js +4 -2
  39. package/dist/serialize.js.map +1 -1
  40. package/dist/service.d.ts +33 -1
  41. package/dist/service.js +187 -60
  42. package/dist/service.js.map +1 -1
  43. package/dist/testing.js +6 -1
  44. package/dist/testing.js.map +1 -1
  45. package/package.json +6 -6
@@ -0,0 +1,170 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { createReadStream, createWriteStream } from "node:fs";
3
+ import { mkdir, mkdtemp, rename, rm, stat } from "node:fs/promises";
4
+ import { join, resolve } from "node:path";
5
+ import { tmpdir } from "node:os";
6
+ import { Readable } from "node:stream";
7
+ import { pipeline } from "node:stream/promises";
8
+ import { BadRequestException, ForbiddenException, HttpException, NotFoundException } from "@nestjs/common";
9
+ import { authorize, effectiveRoles, evaluate } from "@tekki-forge/runtime-permissions";
10
+ import { parseListQuery, QueryError } from "./query.js";
11
+ import { queryableFields } from "./access.js";
12
+ import { relationInclude, toCsv } from "./serialize.js";
13
+ import { teamIdsOf } from "./teams.js";
14
+ /** Exports one person may have waiting or running at once. */
15
+ const EXPORTS_PER_USER = 3;
16
+ /** Chunked CSV generation, durable scheduling, expiring private downloads. */
17
+ export class ExportService {
18
+ service;
19
+ cleanupTimer;
20
+ constructor(service) {
21
+ this.service = service;
22
+ service.events.on("__forge.export", (event) => this.run(event), "CSV export");
23
+ }
24
+ start() {
25
+ if (this.cleanupTimer)
26
+ return;
27
+ this.cleanupTimer = setInterval(() => void this.cleanup().catch(() => undefined), 60_000);
28
+ this.cleanupTimer.unref();
29
+ }
30
+ stop() { if (this.cleanupTimer)
31
+ clearInterval(this.cleanupTimer); this.cleanupTimer = undefined; }
32
+ store() {
33
+ const configured = this.service.shared.get("exportStore");
34
+ if (configured)
35
+ return configured;
36
+ const root = resolve(process.env.FORGE_EXPORT_DIR ?? ".forge-exports");
37
+ const path = (key) => { if (!/^exports\/[0-9a-f-]+\.csv$/.test(key))
38
+ throw new Error("Invalid export key"); return join(root, key); };
39
+ return {
40
+ async put(key, body) { const target = path(key); await mkdir(join(root, "exports"), { recursive: true }); const part = `${target}.${randomUUID()}.part`; try {
41
+ await pipeline(body, createWriteStream(part));
42
+ await rename(part, target);
43
+ }
44
+ finally {
45
+ await rm(part, { force: true });
46
+ } },
47
+ async get(key) { const file = path(key); const metadata = await stat(file); return { body: createReadStream(file), size: metadata.size }; },
48
+ async delete(key) { await rm(path(key), { force: true }); },
49
+ };
50
+ }
51
+ async request(resource, query, ctx) {
52
+ const entity = this.service.entity(resource);
53
+ if (!ctx.principal || !ctx.user)
54
+ throw new ForbiddenException("Sign in to export records");
55
+ if (!entity.features.export || !authorize(entity, "read", ctx.principal).allowed)
56
+ throw new ForbiddenException("Export is not available for this resource");
57
+ if (!this.service.outbox || !this.service.prisma.forgeExport)
58
+ throw new BadRequestException("Run forge generate and migrate the database to enable background exports");
59
+ if (!query || typeof query !== "object" || Array.isArray(query))
60
+ throw new BadRequestException("Export filters must be an object");
61
+ query = Object.fromEntries(Object.entries(query).map(([key, value]) => [key, Array.isArray(value) ? value.join(",") : value]));
62
+ try {
63
+ parseListQuery(entity, { ...query, pagination: undefined, after: undefined, page: 1 }, this.service.queryAccess(entity, ctx));
64
+ }
65
+ catch (e) {
66
+ if (e instanceof QueryError)
67
+ throw new BadRequestException(e.message);
68
+ throw e;
69
+ }
70
+ const open = await this.service.prisma.forgeExport.count({ where: { requestedBy: ctx.principal.id, status: { in: ["pending", "running"] }, expiresAt: { gt: new Date() } } });
71
+ if (open >= EXPORTS_PER_USER)
72
+ throw new HttpException({ statusCode: 429, error: "Too Many Requests", message: `You already have ${open} exports in progress; wait for one to finish` }, 429);
73
+ const id = randomUUID();
74
+ await this.service.prisma.$transaction(async (db) => {
75
+ await db.forgeExport.create({ data: { id, resource, requestedBy: ctx.principal.id, organizationId: ctx.principal.organizationId, principal: JSON.parse(JSON.stringify(ctx.principal)), expiresAt: new Date(Date.now() + 3600_000) } });
76
+ await this.service.outbox.enqueue(db, { id: randomUUID(), type: "__forge.export", entity: entity.name, recordId: id, action: "action", record: { query }, actor: null, organizationId: ctx.principal.organizationId ?? null, occurredAt: new Date().toISOString(), delivery: { context: { principal: ctx.principal, user: ctx.user, session: ctx.session }, record: null } });
77
+ });
78
+ void this.service.outbox.drain();
79
+ return { id, status: "pending", statusUrl: `${this.service.app.api.prefix}/exports/${id}` };
80
+ }
81
+ async currentContext(ctx) {
82
+ const user = await this.service.prisma.user.findUnique({ where: { id: ctx.principal.id } });
83
+ if (!user || user.banned)
84
+ throw new ForbiddenException("Export access has been revoked");
85
+ const org = ctx.principal.organizationId;
86
+ const member = this.service.app.tenancy.enabled && org ? await this.service.prisma.member.findFirst({ where: { userId: user.id, organizationId: org } }) : null;
87
+ if (this.service.app.tenancy.enabled && !member)
88
+ throw new ForbiddenException("Organization membership is required");
89
+ return { ...ctx, user, principal: { id: user.id, organizationId: org, roles: effectiveRoles(user.role, member?.role, this.service.app.tenancy.enabled), teamIds: this.service.app.teams?.enabled ? await teamIdsOf(this.service.prisma, user.id, org ?? null) : [] } };
90
+ }
91
+ async run(event) {
92
+ const db = this.service.prisma;
93
+ const job = await db.forgeExport.findUnique({ where: { id: event.recordId } });
94
+ if (!job || job.status === "ready" || job.expiresAt <= new Date())
95
+ return;
96
+ const temp = await mkdtemp(join(tmpdir(), "forge-export-"));
97
+ try {
98
+ const ctx = await this.currentContext(event.delivery.context);
99
+ const entity = this.service.entity(job.resource);
100
+ const input = { ...event.record.query, pagination: undefined, after: undefined, page: 1, pageSize: 500 };
101
+ const q = parseListQuery(entity, input, this.service.queryAccess(entity, ctx));
102
+ const scope = this.service.readWhere(entity, ctx, q.trashed);
103
+ const queryable = queryableFields(entity, ctx.principal);
104
+ const hidden = new Set(entity.fields.filter((f) => f.access?.read && !queryable.has(f.name) && !evaluate(f.access.read, ctx.principal).allowed).map((f) => f.name));
105
+ await db.forgeExport.update({ where: { id: job.id }, data: { status: "running", error: null, principal: JSON.parse(JSON.stringify(ctx.principal)) } });
106
+ let rows = 0;
107
+ let after;
108
+ const service = this.service;
109
+ async function* csv() {
110
+ yield toCsv(entity, [], hidden);
111
+ for (;;) {
112
+ const records = await service.delegate(entity).findMany({ where: { AND: [...scope, ...q.where, ...(after ? [{ id: { gt: after } }] : [])] }, orderBy: { id: "asc" }, take: 500, include: relationInclude(entity) });
113
+ if (!records.length)
114
+ return;
115
+ rows += records.length;
116
+ if (rows > 1_000_000)
117
+ throw new Error("Export exceeds the one-million-record limit; narrow the filters");
118
+ const chunk = toCsv(entity, await Promise.all(records.map((record) => service.present(entity, record, ctx))), hidden);
119
+ yield chunk.slice(chunk.indexOf("\n") + 1);
120
+ after = records.at(-1).id;
121
+ }
122
+ }
123
+ const file = join(temp, "export.csv");
124
+ await pipeline(Readable.from(csv()), createWriteStream(file, { mode: 0o600 }));
125
+ const metadata = await stat(file);
126
+ const key = `exports/${job.id}.csv`;
127
+ await this.store().put(key, createReadStream(file), { contentType: "text/csv; charset=utf-8", size: metadata.size });
128
+ await db.forgeExport.update({ where: { id: job.id }, data: { status: "ready", storageKey: key, rows } });
129
+ }
130
+ catch (e) {
131
+ await db.forgeExport.update({ where: { id: job.id }, data: { status: "failed", error: "Export could not complete. It will retry while the download remains valid." } });
132
+ throw e;
133
+ }
134
+ finally {
135
+ await rm(temp, { recursive: true, force: true });
136
+ }
137
+ }
138
+ async owned(id, ctx) {
139
+ if (!ctx.principal || !ctx.user)
140
+ throw new ForbiddenException("Sign in to view exports");
141
+ const job = await this.service.prisma.forgeExport.findFirst({ where: { id, requestedBy: ctx.principal.id, organizationId: ctx.principal.organizationId ?? null, expiresAt: { gt: new Date() } } });
142
+ if (!job)
143
+ throw new NotFoundException("Export not found or expired");
144
+ const entity = this.service.entity(job.resource);
145
+ if (!authorize(entity, "read", ctx.principal).allowed)
146
+ throw new ForbiddenException("Export access has been revoked");
147
+ const normalized = (p) => JSON.stringify({ roles: [...p.roles].sort(), teams: [...(p.teamIds ?? [])].sort(), organizationId: p.organizationId ?? null });
148
+ if (normalized(job.principal) !== normalized(ctx.principal))
149
+ throw new ForbiddenException("Your permissions changed; request a new export");
150
+ return job;
151
+ }
152
+ async status(id, ctx) {
153
+ const job = await this.owned(id, ctx);
154
+ return { id: job.id, status: job.status, rows: job.rows, expiresAt: job.expiresAt, error: job.error, ...(job.status === "ready" ? { downloadUrl: `${this.service.app.api.prefix}/exports/${id}/download` } : {}) };
155
+ }
156
+ async download(id, ctx) {
157
+ const job = await this.owned(id, ctx);
158
+ if (job.status !== "ready" || !job.storageKey)
159
+ throw new BadRequestException("Export is not ready");
160
+ return { ...(await this.store().get(job.storageKey)), filename: `${job.resource}.csv` };
161
+ }
162
+ async cleanup() {
163
+ for (const job of await this.service.prisma.forgeExport.findMany({ where: { expiresAt: { lt: new Date() } }, take: 50 })) {
164
+ if (job.storageKey)
165
+ await this.store().delete(job.storageKey);
166
+ await this.service.prisma.forgeExport.delete({ where: { id: job.id } });
167
+ }
168
+ }
169
+ }
170
+ //# sourceMappingURL=exports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exports.js","sourceRoot":"","sources":["../src/exports.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -9,6 +9,9 @@ export * from "./bootstrap.js";
9
9
  export { BadRequestException, ConflictException, ForbiddenException, NotFoundException, UnauthorizedException, } from "@nestjs/common";
10
10
  export * from "./seed.js";
11
11
  export * from "./events.js";
12
+ export * from "./outbox.js";
13
+ export * from "./observability.js";
14
+ export * from "./exports.js";
12
15
  export * from "./computed.js";
13
16
  export * from "./access.js";
14
17
  export * from "./notifications.js";
package/dist/index.js CHANGED
@@ -9,6 +9,9 @@ export * from "./bootstrap.js";
9
9
  export { BadRequestException, ConflictException, ForbiddenException, NotFoundException, UnauthorizedException, } from "@nestjs/common";
10
10
  export * from "./seed.js";
11
11
  export * from "./events.js";
12
+ export * from "./outbox.js";
13
+ export * from "./observability.js";
14
+ export * from "./exports.js";
12
15
  export * from "./computed.js";
13
16
  export * from "./access.js";
14
17
  export * from "./notifications.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvG,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,gBAAgB,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,oBAAoB,CAAC;AACnC,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvG,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,gBAAgB,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,oBAAoB,CAAC;AACnC,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,oBAAoB,CAAC;AACnC,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC"}
@@ -13,6 +13,8 @@ export interface NotifyInput {
13
13
  event?: string;
14
14
  /** Also email each recipient (templated "notification" email). */
15
15
  email?: boolean;
16
+ /** Stable source key for notifications retried by the transactional outbox. */
17
+ deduplicationKey?: string;
16
18
  }
17
19
  export interface MailSender {
18
20
  send(input: {
@@ -1,4 +1,5 @@
1
1
  import { EventEmitter } from "node:events";
2
+ import { createHash } from "node:crypto";
2
3
  import { allowsRecord, authorize, effectiveRoles } from "@tekki-forge/runtime-permissions";
3
4
  import { Logger, UnauthorizedException } from "@nestjs/common";
4
5
  import { teamIdsOf } from "./teams.js";
@@ -32,8 +33,13 @@ export class NotificationService {
32
33
  return 0;
33
34
  const created = [];
34
35
  for (const userId of ids) {
36
+ const hash = input.deduplicationKey ? createHash("sha256").update(`${input.deduplicationKey}:${userId}`).digest("hex").slice(0, 32) : undefined;
37
+ const id = hash ? `${hash.slice(0, 8)}-${hash.slice(8, 12)}-${hash.slice(12, 16)}-${hash.slice(16, 20)}-${hash.slice(20)}` : undefined;
38
+ if (id && await this.prisma.notification.findUnique({ where: { id } }))
39
+ continue;
35
40
  created.push(await this.prisma.notification.create({
36
41
  data: {
42
+ ...(id ? { id } : {}),
37
43
  userId,
38
44
  title: input.title.slice(0, 500),
39
45
  body: input.body?.slice(0, 5000) ?? null,
@@ -134,7 +140,7 @@ export class NotificationService {
134
140
  if (!rules.length || !event.record)
135
141
  return;
136
142
  const entity = this.app.entities.find((e) => e.name === event.entity);
137
- for (const rule of rules) {
143
+ for (const [ruleIndex, rule] of rules.entries()) {
138
144
  if (rule.changed && !event.changes?.[rule.changed] && !event.changes?.[entity.fields.find((f) => f.name === rule.changed)?.apiName ?? ""])
139
145
  continue;
140
146
  const candidates = (await this.recipients(rule, entity, event.record, event.organizationId)).filter((id) => id !== event.actor?.id);
@@ -158,6 +164,7 @@ export class NotificationService {
158
164
  recordId: event.recordId,
159
165
  event: event.type,
160
166
  email: rule.email,
167
+ deduplicationKey: `${event.id}:${ruleIndex}`,
161
168
  });
162
169
  }
163
170
  }
@@ -1 +1 @@
1
- {"version":3,"file":"notifications.js","sourceRoot":"","sources":["../src/notifications.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"notifications.js","sourceRoot":"","sources":["../src/notifications.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,cAAc,EAAkB,MAAM,kCAAkC,CAAC;AAC3G,OAAO,EAAE,MAAM,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAG/D,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAsBvC,2FAA2F;AAC3F,MAAM,OAAO,mBAAmB;IAOX;IACA;IACA;IACA;IATF,MAAM,GAAG,IAAI,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAC1C,KAAK,GAAG,IAAI,YAAY,EAAE,CAAC;IAC5C,4EAA4E;IACpE,SAAS,CAA0C;IAE3D,YACmB,GAAU,EACV,MAAW,EACX,IAAiB,EACjB,WAAmH;QAHnH,QAAG,GAAH,GAAG,CAAO;QACV,WAAM,GAAN,MAAM,CAAK;QACX,SAAI,GAAJ,IAAI,CAAa;QACjB,gBAAW,GAAX,WAAW,CAAwG;QAEpI,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IAChC,CAAC;IAED,SAAS,CAAC,OAA+C;QACvD,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC;IAC3B,CAAC;IAED,uFAAuF;IACvF,OAAO,CAAC,MAAc,EAAE,OAAY;QAClC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,OAAiB,EAAE,KAAkB;QAChD,MAAM,GAAG,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAClD,IAAI,CAAC,GAAG,CAAC,MAAM;YAAE,OAAO,CAAC,CAAC;QAC1B,MAAM,OAAO,GAAU,EAAE,CAAC;QAC1B,KAAK,MAAM,MAAM,IAAI,GAAG,EAAE,CAAC;YACzB,MAAM,IAAI,GAAG,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC,gBAAgB,IAAI,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAChJ,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;YACvI,IAAI,EAAE,IAAI,MAAM,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC;gBAAE,SAAS;YACjF,OAAO,CAAC,IAAI,CACV,MAAM,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC;gBACpC,IAAI,EAAE;oBACJ,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBACrB,MAAM;oBACN,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;oBAChC,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,IAAI;oBACxC,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,IAAI;oBACxB,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,IAAI;oBAC1B,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,IAAI;oBAC5B,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,IAAI;oBAChC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,KAAK,CAAC,cAAc,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBACtF;aACF,CAAC,CACH,CAAC;QACJ,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACxB,MAAM,OAAO,GAAG,EAAE,IAAI,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC;YAC1D,IAAI,IAAI,CAAC,SAAS;gBAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;;gBACjD,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC1C,CAAC;QACD,IAAI,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YAC7B,MAAM,KAAK,GAAU,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;YAC9G,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;gBACtB,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC,KAAK,YAAY,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YACpO,CAAC;QACH,CAAC;QACD,OAAO,OAAO,CAAC,MAAM,CAAC;IACxB,CAAC;IAED,iFAAiF;IAEzE,IAAI,CAAC,IAAY,EAAE,MAAgB,EAAE,MAAW,EAAE,KAAa;QACrE,OAAO,IAAI,CAAC,OAAO,CAAC,+BAA+B,EAAE,CAAC,CAAC,EAAE,GAAW,EAAE,EAAE;YACtE,IAAI,GAAG,KAAK,OAAO;gBAAE,OAAO,KAAK,CAAC;YAClC,IAAI,GAAG,KAAK,QAAQ;gBAAE,OAAO,MAAM,CAAC,KAAK,CAAC;YAC1C,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC;YACpD,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;YACtB,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,SAAS;gBAAE,OAAO,EAAE,CAAC;YAC7C,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;gBAAE,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACpE,IAAI,OAAO,CAAC,KAAK,QAAQ;gBAAE,OAAO,MAAM,CAAE,CAAS,CAAC,KAAK,IAAK,CAAS,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;YACpF,IAAI,CAAC,EAAE,UAAU;gBAAE,OAAO,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,EAAE,KAAK,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC;YACtF,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;QACnB,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,YAAY,CAAC,MAAc,EAAE,cAA6B,EAAE,MAAe;QACvF,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;QAC1H,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QACtC,IAAI,UAAU,GAAkB,IAAI,CAAC;QACrC,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,cAAc,EAAE,CAAC;YAC/C,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,cAAc,EAAE,EAAE,CAAC,CAAC;YACpF,IAAI,CAAC,CAAC,IAAI,MAAM;gBAAE,OAAO,IAAI,CAAC;YAC9B,UAAU,GAAG,CAAC,EAAE,IAAI,IAAI,IAAI,CAAC;QAC/B,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC3G,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,cAAc,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;IACjJ,CAAC;IAEO,KAAK,CAAC,UAAU,CAAC,IAAwB,EAAE,MAAgB,EAAE,MAAW,EAAE,KAAoB;QACpG,MAAM,GAAG,GAAa,EAAE,CAAC;QACzB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC;YACxB,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;gBAC5C,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,UAAU,CAAE,CAAC,QAAS,CAAC,UAAU,CAAC;gBACzF,IAAI,MAAM,CAAC,EAAE,CAAC;oBAAE,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;YACvC,CAAC;iBAAM,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;gBAC9B,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,KAAK,CAAE,CAAC;gBACzD,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;gBAC5B,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;oBAAE,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;qBAChC,IAAI,OAAO,CAAC,KAAK,QAAQ;oBAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC9C,CAAC;iBAAM,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBAC7B,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;oBAC7B,MAAM,OAAO,GAAU,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,EAAE,cAAc,EAAE,KAAK,IAAI,UAAU,EAAE,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;oBACnJ,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;wBACxB,IAAI,cAAc,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;4BAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;oBAC9E,CAAC;gBACH,CAAC;qBAAM,CAAC;oBACN,MAAM,KAAK,GAAU,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;oBAClI,KAAK,MAAM,CAAC,IAAI,KAAK;wBAAE,IAAI,cAAc,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;4BAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;gBAClG,CAAC;YACH,CAAC;QACH,CAAC;QACD,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3B,CAAC;IAED,6EAA6E;IAC7E,KAAK,CAAC,MAAM,CAAC,KAAiB;QAC5B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QACjJ,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM;YAAE,OAAO;QAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,MAAM,CAAE,CAAC;QACvE,KAAK,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;YAChD,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,OAAO,CAAC,EAAE,OAAO,IAAI,EAAE,CAAC;gBAAE,SAAS;YACpJ,MAAM,UAAU,GAAG,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,KAAK,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YACpI,sDAAsD;YACtD,MAAM,OAAO,GAAa,EAAE,CAAC;YAC7B,KAAK,MAAM,MAAM,IAAI,UAAU,EAAE,CAAC;gBAChC,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,cAAc,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;gBAChG,IAAI,CAAC,SAAS;oBAAE,SAAS;gBACzB,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;gBACtD,IAAI,MAAM,YAAY,CAAC,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;oBAAE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACjI,CAAC;YACD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,IAAI,IAAI,SAAS,CAAC;YAC7C,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE;gBACzB,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC;gBACzD,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACjF,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC;gBACvD,cAAc,EAAE,KAAK,CAAC,cAAc;gBACpC,MAAM,EAAE,MAAM,CAAC,WAAW;gBAC1B,QAAQ,EAAE,KAAK,CAAC,QAAQ;gBACxB,KAAK,EAAE,KAAK,CAAC,IAAI;gBACjB,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,gBAAgB,EAAE,GAAG,KAAK,CAAC,EAAE,IAAI,SAAS,EAAE;aAC7C,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,iFAAiF;IAEzE,KAAK,CAAC,GAAwB;QACpC,IAAI,CAAC,GAAG,CAAC,SAAS;YAAE,MAAM,IAAI,qBAAqB,CAAC,qBAAqB,CAAC,CAAC;QAC3E,OAAO;YACL,MAAM,EAAE,GAAG,CAAC,SAAS,CAAC,EAAE;YACxB,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,cAAc,EAAE,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC1H,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,GAAwB,EAAE,KAAU;QAC7C,MAAM,KAAK,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC,MAAM,KAAK,MAAM,IAAI,KAAK,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;QACnH,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAClD,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAC1E,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YAC9C,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC;YACzC,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC;YAC/E,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;SAC1H,CAAC,CAAC;QACH,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC;IAC5G,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,GAAwB,EAAE,IAAS;QAChD,MAAM,KAAK,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAU,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;QACjM,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QACrG,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC5B,CAAC;IAED,sFAAsF;IACtF,SAAS,CAAC,MAAc,EAAE,IAA4B;QACpD,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC5B,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC5C,CAAC;CACF"}
@@ -0,0 +1,20 @@
1
+ import type { Request, Response, NextFunction } from "express";
2
+ export declare class RequestMetrics {
3
+ private readonly routes;
4
+ middleware(log: boolean): (req: Request, res: Response, next: NextFunction) => void;
5
+ snapshot(): {
6
+ [k: string]: {
7
+ meanMs: number;
8
+ requests: number;
9
+ errors: number;
10
+ durationMs: number;
11
+ maxMs: number;
12
+ };
13
+ };
14
+ }
15
+ export declare function readiness(checks: Record<string, () => Promise<unknown>>, timeoutMs?: number): Promise<{
16
+ status: string;
17
+ checks: {
18
+ [k: string]: boolean;
19
+ };
20
+ }>;
@@ -0,0 +1,47 @@
1
+ import { randomUUID } from "node:crypto";
2
+ export class RequestMetrics {
3
+ routes = new Map();
4
+ middleware(log) {
5
+ return (req, res, next) => {
6
+ const supplied = req.get("x-request-id");
7
+ const requestId = supplied && /^[A-Za-z0-9_-]{1,80}$/.test(supplied) ? supplied : randomUUID();
8
+ res.setHeader("x-request-id", requestId);
9
+ const start = performance.now();
10
+ res.once("finish", () => {
11
+ const durationMs = performance.now() - start;
12
+ // Route templates, never query strings or record identifiers, bound label cardinality.
13
+ const route = req.route?.path ? String(req.route.path) : "unmatched";
14
+ const key = `${req.method} ${route}`;
15
+ const stats = this.routes.get(key) ?? { requests: 0, errors: 0, durationMs: 0, maxMs: 0 };
16
+ stats.requests++;
17
+ stats.errors += res.statusCode >= 500 ? 1 : 0;
18
+ stats.durationMs += durationMs;
19
+ stats.maxMs = Math.max(stats.maxMs, durationMs);
20
+ this.routes.set(key, stats);
21
+ if (log)
22
+ console.log(JSON.stringify({ type: "request", requestId, method: req.method, route, status: res.statusCode, durationMs: Math.round(durationMs * 100) / 100 }));
23
+ });
24
+ next();
25
+ };
26
+ }
27
+ snapshot() {
28
+ return Object.fromEntries([...this.routes].map(([route, s]) => [route, { ...s, meanMs: s.requests ? s.durationMs / s.requests : 0 }]));
29
+ }
30
+ }
31
+ export async function readiness(checks, timeoutMs = 2000) {
32
+ const results = await Promise.all(Object.entries(checks).map(async ([name, check]) => {
33
+ let timer;
34
+ try {
35
+ await Promise.race([Promise.resolve().then(check), new Promise((_, reject) => { timer = setTimeout(() => reject(new Error("Readiness timeout")), timeoutMs); })]);
36
+ return [name, true];
37
+ }
38
+ catch {
39
+ return [name, false];
40
+ }
41
+ finally {
42
+ clearTimeout(timer);
43
+ }
44
+ }));
45
+ return { status: results.every(([, ok]) => ok) ? "ready" : "unavailable", checks: Object.fromEntries(results) };
46
+ }
47
+ //# sourceMappingURL=observability.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observability.js","sourceRoot":"","sources":["../src/observability.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import type { EventBus, ForgeEvent } from "./events.js";
2
+ /** Transactional event storage with leased, at-least-once delivery and subscriber checkpoints. */
3
+ export declare class EventOutbox {
4
+ private readonly prisma;
5
+ private readonly events;
6
+ private readonly logger;
7
+ private timer?;
8
+ private running?;
9
+ private rerun;
10
+ private readonly inFlight;
11
+ private exportsRunning;
12
+ constructor(prisma: any, events: EventBus);
13
+ enqueue(db: any, event: ForgeEvent): Promise<void>;
14
+ start(): void;
15
+ stop(): Promise<void>;
16
+ drain(): Promise<void>;
17
+ private dispatch;
18
+ /** Deliver one freshly committed domain event without waiting behind a large background export. */
19
+ deliver(id: string): Promise<void>;
20
+ private deliverRow;
21
+ }
package/dist/outbox.js ADDED
@@ -0,0 +1,98 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { Logger } from "@nestjs/common";
3
+ /** Transactional event storage with leased, at-least-once delivery and subscriber checkpoints. */
4
+ export class EventOutbox {
5
+ prisma;
6
+ events;
7
+ logger = new Logger("ForgeOutbox");
8
+ timer;
9
+ running;
10
+ rerun = false;
11
+ inFlight = new Set();
12
+ exportsRunning = 0;
13
+ constructor(prisma, events) {
14
+ this.prisma = prisma;
15
+ this.events = events;
16
+ }
17
+ async enqueue(db, event) {
18
+ await db.forgeOutbox.create({ data: { id: event.id, payload: JSON.parse(JSON.stringify(event)) } });
19
+ }
20
+ start() {
21
+ if (this.timer)
22
+ return;
23
+ this.timer = setInterval(() => void this.drain(), 2000);
24
+ this.timer.unref();
25
+ void this.drain();
26
+ }
27
+ async stop() {
28
+ if (this.timer)
29
+ clearInterval(this.timer);
30
+ this.timer = undefined;
31
+ await this.running;
32
+ await Promise.all(this.inFlight);
33
+ }
34
+ drain() {
35
+ if (this.running) {
36
+ this.rerun = true;
37
+ return this.running;
38
+ }
39
+ this.running = (async () => { do {
40
+ this.rerun = false;
41
+ await this.dispatch();
42
+ } while (this.rerun); })().catch((e) => this.logger.error(`Dispatch failed: ${e.message}`)).finally(() => { this.running = undefined; });
43
+ return this.running;
44
+ }
45
+ async dispatch() {
46
+ await this.prisma.forgeOutbox.deleteMany({ where: { processedAt: { lt: new Date(Date.now() - 7 * 86400_000) } } });
47
+ const now = new Date();
48
+ const rows = await this.prisma.forgeOutbox.findMany({ where: { processedAt: null, availableAt: { lte: now }, OR: [{ lockedUntil: null }, { lockedUntil: { lt: now } }] }, orderBy: { createdAt: "asc" }, take: 50 });
49
+ for (const row of rows) {
50
+ // A large export runs beside the queue, never in it: hooks, notifications and webhooks keep flowing.
51
+ if (String(row.payload?.type ?? "").startsWith("__forge.export")) {
52
+ if (this.exportsRunning >= 2)
53
+ continue;
54
+ this.exportsRunning++;
55
+ void this.deliver(row.id).finally(() => { this.exportsRunning--; });
56
+ }
57
+ else
58
+ await this.deliverRow(row);
59
+ }
60
+ }
61
+ /** Deliver one freshly committed domain event without waiting behind a large background export. */
62
+ deliver(id) {
63
+ const work = (async () => {
64
+ const row = await this.prisma.forgeOutbox.findUnique({ where: { id } });
65
+ if (row && !row.processedAt && row.availableAt <= new Date())
66
+ await this.deliverRow(row);
67
+ })().catch((e) => this.logger.error(`Delivery failed: ${e.message}`));
68
+ this.inFlight.add(work);
69
+ void work.then(() => this.inFlight.delete(work));
70
+ return work;
71
+ }
72
+ async deliverRow(row) {
73
+ const token = randomUUID();
74
+ const claimed = await this.prisma.forgeOutbox.updateMany({ where: { id: row.id, processedAt: null, OR: [{ lockedUntil: null }, { lockedUntil: { lt: new Date() } }] }, data: { lockedUntil: new Date(Date.now() + 60_000), lockToken: token, attempts: { increment: 1 } } });
75
+ if (!claimed.count)
76
+ return;
77
+ // Renew long-running subscribers so another instance cannot take over a live delivery.
78
+ const renew = setInterval(() => { void this.prisma.forgeOutbox.updateMany({ where: { id: row.id, lockToken: token }, data: { lockedUntil: new Date(Date.now() + 60_000) } }).catch(() => undefined); }, 20_000);
79
+ renew.unref();
80
+ try {
81
+ await this.events.deliver(row.payload, row.completed, async (name) => {
82
+ await this.prisma.forgeOutbox.updateMany({ where: { id: row.id, lockToken: token }, data: { completed: { push: name } } });
83
+ });
84
+ // Delivered: the internal snapshot (request context, full record) is no longer needed and is dropped.
85
+ const { delivery: _delivery, ...payload } = row.payload;
86
+ await this.prisma.forgeOutbox.updateMany({ where: { id: row.id, lockToken: token }, data: { processedAt: new Date(), lockedUntil: null, lockToken: null, lastError: null, payload: JSON.parse(JSON.stringify(payload)) } });
87
+ }
88
+ catch (e) {
89
+ const delay = Math.min(3_600_000, 1000 * 2 ** Math.min(row.attempts, 12));
90
+ await this.prisma.forgeOutbox.updateMany({ where: { id: row.id, lockToken: token }, data: { availableAt: new Date(Date.now() + delay), lockedUntil: null, lockToken: null, lastError: (e instanceof Error ? e.message : String(e)).slice(0, 2000) } });
91
+ this.logger.warn(`Event ${row.id} will retry: ${e instanceof Error ? e.message : String(e)}`);
92
+ }
93
+ finally {
94
+ clearInterval(renew);
95
+ }
96
+ }
97
+ }
98
+ //# sourceMappingURL=outbox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outbox.js","sourceRoot":"","sources":["../src/outbox.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAGxC,kGAAkG;AAClG,MAAM,OAAO,WAAW;IAOO;IAA8B;IAN1C,MAAM,GAAG,IAAI,MAAM,CAAC,aAAa,CAAC,CAAC;IAC5C,KAAK,CAAkC;IACvC,OAAO,CAAiB;IACxB,KAAK,GAAG,KAAK,CAAC;IACL,QAAQ,GAAG,IAAI,GAAG,EAAiB,CAAC;IAC7C,cAAc,GAAG,CAAC,CAAC;IAC3B,YAA6B,MAAW,EAAmB,MAAgB;QAA9C,WAAM,GAAN,MAAM,CAAK;QAAmB,WAAM,GAAN,MAAM,CAAU;IAAG,CAAC;IAE/E,KAAK,CAAC,OAAO,CAAC,EAAO,EAAE,KAAiB;QACtC,MAAM,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACtG,CAAC;IAED,KAAK;QACH,IAAI,IAAI,CAAC,KAAK;YAAE,OAAO;QACvB,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,CAAC;QACxD,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACnB,KAAK,IAAI,CAAC,KAAK,EAAE,CAAC;IACpB,CAAC;IAED,KAAK,CAAC,IAAI;QACR,IAAI,IAAI,CAAC,KAAK;YAAE,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1C,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;QACvB,MAAM,IAAI,CAAC,OAAO,CAAC;QACnB,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACnC,CAAC;IAED,KAAK;QACH,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAAC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;YAAC,OAAO,IAAI,CAAC,OAAO,CAAC;QAAC,CAAC;QAC7D,IAAI,CAAC,OAAO,GAAG,CAAC,KAAK,IAAI,EAAE,GAAG,GAAG,CAAC;YAAC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;YAAC,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QAAC,CAAC,QAAQ,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9N,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAEO,KAAK,CAAC,QAAQ;QACpB,MAAM,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,SAAS,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QACnH,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QACrN,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,qGAAqG;YACrG,IAAI,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;gBACjE,IAAI,IAAI,CAAC,cAAc,IAAI,CAAC;oBAAE,SAAS;gBACvC,IAAI,CAAC,cAAc,EAAE,CAAC;gBACtB,KAAK,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;YACtE,CAAC;;gBAAM,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;IAED,mGAAmG;IACnG,OAAO,CAAC,EAAU;QAChB,MAAM,IAAI,GAAG,CAAC,KAAK,IAAI,EAAE;YACvB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;YACxE,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,WAAW,IAAI,IAAI,IAAI,EAAE;gBAAE,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QAC3F,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC7E,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACxB,KAAK,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;QACjD,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,KAAK,CAAC,UAAU,CAAC,GAAQ;QAC/B,MAAM,KAAK,GAAG,UAAU,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,EAAE,IAAI,IAAI,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,WAAW,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QAC7Q,IAAI,CAAC,OAAO,CAAC,KAAK;YAAE,OAAO;QAC3B,uFAAuF;QACvF,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,WAAW,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QAChN,KAAK,CAAC,KAAK,EAAE,CAAC;QACd,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,OAAqB,EAAE,GAAG,CAAC,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;gBACjF,MAAM,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;YAC7H,CAAC,CAAC,CAAC;YACH,sGAAsG;YACtG,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,OAAO,EAAE,GAAG,GAAG,CAAC,OAAqB,CAAC;YACtE,MAAM,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,WAAW,EAAE,IAAI,IAAI,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC9N,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC;YAC1E,MAAM,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,WAAW,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;YACvP,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,GAAG,CAAC,EAAE,gBAAgB,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAChG,CAAC;gBAAS,CAAC;YACT,aAAa,CAAC,KAAK,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;CACF"}
package/dist/plugin.d.ts CHANGED
@@ -21,6 +21,8 @@ export interface ForgeApiPlugin {
21
21
  name: string;
22
22
  /** Runs once at startup, before routes are registered. Throw to abort startup with a clear message. */
23
23
  setup?(ctx: PluginContext): void | Promise<void>;
24
+ /** Dependency readiness; throw when unavailable. Called by /ready with a bounded timeout. */
25
+ health?(ctx: PluginContext): void | Promise<unknown>;
24
26
  /** Nest controllers; they can inject FORGE_SERVICE and use `requestContext(service, req)`. */
25
27
  controllers?: Type[];
26
28
  /** Extra Nest modules. */
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAuCA,MAAM,UAAU,mBAAmB,CAA8B,OAAuC;IACtG,OAAO,CAAC,OAAW,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,OAAO,IAAI,EAAE,CAAM,CAAC,CAAC;AACxD,CAAC"}
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAyCA,MAAM,UAAU,mBAAmB,CAA8B,OAAuC;IACtG,OAAO,CAAC,OAAW,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,OAAO,IAAI,EAAE,CAAM,CAAC,CAAC;AACxD,CAAC"}
package/dist/query.d.ts CHANGED
@@ -5,6 +5,9 @@ export declare class QueryError extends Error {
5
5
  export interface ListQuery {
6
6
  page: number;
7
7
  pageSize: number;
8
+ pagination?: "cursor";
9
+ after?: string;
10
+ count?: false;
8
11
  orderBy: Record<string, "asc" | "desc">[];
9
12
  /** Filters and search, excluding authorization/tenancy/soft-delete conditions. */
10
13
  where: Record<string, unknown>[];
package/dist/query.js CHANGED
@@ -5,7 +5,7 @@ export class QueryError extends Error {
5
5
  }
6
6
  }
7
7
  // `view` and `month` are UI state (list/board/calendar/map) that may travel with list URLs and saved views.
8
- const RESERVED = new Set(["page", "pageSize", "sort", "search", "trashed", "view", "month"]);
8
+ const RESERVED = new Set(["page", "pageSize", "sort", "search", "trashed", "view", "month", "pagination", "after", "count"]);
9
9
  const RANGE_OPS = new Set(["gte", "lte", "gt", "lt"]);
10
10
  const RANGE_KINDS = new Set(["integer", "decimal", "money", "percentage", "date", "datetime"]);
11
11
  export const MAX_PAGE_SIZE = 500;
@@ -187,6 +187,17 @@ export function parseListQuery(entity, query, access) {
187
187
  const values = value.split(",").map((v) => coerceFilterValue(f, f.relation ? me(v.trim()) : v.trim()));
188
188
  where.push(values.length === 1 ? { [column]: values[0] } : { [column]: { in: values.filter((v) => v !== null) } });
189
189
  }
190
- return { page, pageSize, orderBy: parseSort(entity, first(query.sort), access), where, trashed };
190
+ if (query.pagination !== undefined && query.pagination !== "cursor")
191
+ throw new QueryError('pagination must be "cursor"');
192
+ const cursor = query.pagination === "cursor";
193
+ if (query.after && !cursor)
194
+ throw new QueryError("after requires cursor pagination");
195
+ if (cursor && query.sort && !["id", "-id"].includes(String(query.sort)))
196
+ throw new QueryError("cursor pagination supports sort=id or sort=-id");
197
+ if (cursor && page !== 1)
198
+ throw new QueryError("use after instead of page with cursor pagination");
199
+ if (query.after && (typeof query.after !== "string" || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(query.after)))
200
+ throw new QueryError("after must be a record UUID");
201
+ return { page, pageSize, orderBy: parseSort(entity, cursor ? (first(query.sort) ?? "id") : first(query.sort), access), where, trashed, ...(cursor ? { pagination: "cursor", ...(query.after ? { after: String(query.after) } : {}) } : {}), ...(query.count === "false" || query.count === false ? { count: false } : {}) };
191
202
  }
192
203
  //# sourceMappingURL=query.js.map