@pattern-stack/codegen 0.26.1 → 0.27.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 (67) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/{chunk-XNRKZCVH.js → chunk-36NRG2EP.js} +8 -8
  3. package/dist/{chunk-COGHTKXY.js → chunk-7625PLY7.js} +4 -4
  4. package/dist/{chunk-CDLWYZVQ.js → chunk-BHZP6LOV.js} +7 -7
  5. package/dist/{chunk-CZQUOIDY.js → chunk-DKKFTHHI.js} +4 -4
  6. package/dist/{chunk-HLURWFIT.js → chunk-IASPGFFK.js} +5 -5
  7. package/dist/{chunk-CKLM57IE.js → chunk-KS4BZHIA.js} +10 -10
  8. package/dist/{chunk-7XDB4OMR.js → chunk-VCXOPBYY.js} +9 -9
  9. package/dist/{chunk-3VEVGL74.js → chunk-VNBC3VXM.js} +4 -4
  10. package/dist/{chunk-QXYKV4CE.js → chunk-W4JYZSQK.js} +8 -8
  11. package/dist/{chunk-PNCOUFFI.js → chunk-Y6UZMYGX.js} +4 -3
  12. package/dist/chunk-Y6UZMYGX.js.map +1 -0
  13. package/dist/{chunk-6M6LZEP6.js → chunk-YHVZAL6U.js} +5 -5
  14. package/dist/{chunk-QO35B6BN.js → chunk-YIVQ7KLS.js} +4 -4
  15. package/dist/{chunk-ENAR3F5S.js → chunk-Z7YFYK6H.js} +7 -7
  16. package/dist/runtime/base-classes/index.js +17 -17
  17. package/dist/runtime/http/pagination.d.ts +151 -0
  18. package/dist/runtime/http/pagination.js +98 -0
  19. package/dist/runtime/http/pagination.js.map +1 -0
  20. package/dist/runtime/subsystems/analytics/analytics.module.js +2 -2
  21. package/dist/runtime/subsystems/analytics/index.js +4 -4
  22. package/dist/runtime/subsystems/auth/index.js +3 -3
  23. package/dist/runtime/subsystems/bridge/bridge.module.js +12 -12
  24. package/dist/runtime/subsystems/bridge/index.js +18 -18
  25. package/dist/runtime/subsystems/cache/cache.module.js +2 -2
  26. package/dist/runtime/subsystems/cache/index.js +4 -4
  27. package/dist/runtime/subsystems/events/event-bus.drizzle-backend.js +1 -1
  28. package/dist/runtime/subsystems/events/events.module.js +3 -3
  29. package/dist/runtime/subsystems/events/index.js +5 -5
  30. package/dist/runtime/subsystems/index.js +53 -53
  31. package/dist/runtime/subsystems/integration/execute-integration.use-case.js +2 -2
  32. package/dist/runtime/subsystems/integration/index.js +31 -31
  33. package/dist/runtime/subsystems/integration/integration-cursor-store.drizzle-backend.js +3 -3
  34. package/dist/runtime/subsystems/integration/integration-run-recorder.drizzle-backend.js +3 -3
  35. package/dist/runtime/subsystems/integration/integration.module.js +6 -6
  36. package/dist/runtime/subsystems/jobs/index.js +27 -27
  37. package/dist/runtime/subsystems/jobs/job-orchestrator.bullmq-backend.js +4 -4
  38. package/dist/runtime/subsystems/jobs/job-run-service.drizzle-backend.js +2 -2
  39. package/dist/runtime/subsystems/jobs/job-run-service.memory-backend.js +2 -2
  40. package/dist/runtime/subsystems/jobs/job-worker.module.js +8 -8
  41. package/dist/runtime/subsystems/jobs/jobs-domain.module.js +6 -6
  42. package/dist/runtime/subsystems/observability/index.js +3 -3
  43. package/dist/src/cli/index.js +195 -20
  44. package/dist/src/cli/index.js.map +1 -1
  45. package/dist/src/index.js +8 -8
  46. package/package.json +1 -1
  47. package/runtime/http/pagination.ts +233 -0
  48. package/runtime/subsystems/events/event-bus.drizzle-backend.ts +16 -4
  49. package/templates/entity/new/clean-lite-ps/controller.ejs.t +27 -6
  50. package/templates/entity/new/clean-lite-ps/dto/list-query.ejs.t +22 -0
  51. package/templates/entity/new/clean-lite-ps/index.ejs.t +1 -0
  52. package/templates/entity/new/clean-lite-ps/prompt-extension.js +14 -0
  53. package/templates/entity/new/clean-lite-ps/use-cases/list.ejs.t +56 -3
  54. package/templates/entity/new/prompt.js +17 -0
  55. package/dist/chunk-PNCOUFFI.js.map +0 -1
  56. /package/dist/{chunk-XNRKZCVH.js.map → chunk-36NRG2EP.js.map} +0 -0
  57. /package/dist/{chunk-COGHTKXY.js.map → chunk-7625PLY7.js.map} +0 -0
  58. /package/dist/{chunk-CDLWYZVQ.js.map → chunk-BHZP6LOV.js.map} +0 -0
  59. /package/dist/{chunk-CZQUOIDY.js.map → chunk-DKKFTHHI.js.map} +0 -0
  60. /package/dist/{chunk-HLURWFIT.js.map → chunk-IASPGFFK.js.map} +0 -0
  61. /package/dist/{chunk-CKLM57IE.js.map → chunk-KS4BZHIA.js.map} +0 -0
  62. /package/dist/{chunk-7XDB4OMR.js.map → chunk-VCXOPBYY.js.map} +0 -0
  63. /package/dist/{chunk-3VEVGL74.js.map → chunk-VNBC3VXM.js.map} +0 -0
  64. /package/dist/{chunk-QXYKV4CE.js.map → chunk-W4JYZSQK.js.map} +0 -0
  65. /package/dist/{chunk-6M6LZEP6.js.map → chunk-YHVZAL6U.js.map} +0 -0
  66. /package/dist/{chunk-QO35B6BN.js.map → chunk-YIVQ7KLS.js.map} +0 -0
  67. /package/dist/{chunk-ENAR3F5S.js.map → chunk-Z7YFYK6H.js.map} +0 -0
@@ -0,0 +1,151 @@
1
+ import { z } from 'zod';
2
+
3
+ /**
4
+ * Pagination envelope + ListQuery contract for generated entity list endpoints
5
+ * (pagination-by-default).
6
+ *
7
+ * Every generated `GET /<entities>` returns a {@link Page} envelope instead of
8
+ * a bare `T[]`. The request shape is {@link ListQuery} (page/cursor/pageSize +
9
+ * default sort) merged with arbitrary where-filters at the controller.
10
+ *
11
+ * Mirrors the in-repo precedent: the jobs subsystem's `JobRunPage`
12
+ * (`{ items, nextCursor }`) and its opaque keyset cursor codec
13
+ * (`runtime/subsystems/jobs/job-run-keyset-cursor.ts`). The entity envelope
14
+ * EXTENDS that minimal shape with `page/pageCount/total/pageSize` so a numbered
15
+ * UI (jump-to-page) works while `nextCursor` stays contract-stable for the
16
+ * later keyset upgrade.
17
+ *
18
+ * ENGINE NOTE (v1): the list use-case fetches by OFFSET (page-based). The
19
+ * `nextCursor` is computed from the last row and emitted from day one so the
20
+ * contract never changes, but cursor-REQUEST honoring (keyset seek) is a
21
+ * DEFERRED seam — see the TODO in the generated list use-case / the repository
22
+ * query branch. `ListQuery.cursor` is ACCEPTED (no validation error) even
23
+ * though v1 ignores it for fetching.
24
+ */
25
+
26
+ /** Default page size when `pageSize` is omitted. */
27
+ declare const DEFAULT_PAGE_SIZE = 50;
28
+ /** Hard upper bound on page size to keep a single read bounded. */
29
+ declare const MAX_PAGE_SIZE = 200;
30
+ /** Default page (1-based) when `page` is omitted. */
31
+ declare const DEFAULT_PAGE = 1;
32
+ /** Default sort column. */
33
+ declare const DEFAULT_SORT_BY = "created_at";
34
+ /** Default sort direction. */
35
+ declare const DEFAULT_SORT_ORDER: "desc";
36
+ /** Clamp a caller-supplied `pageSize` into `[1, MAX_PAGE_SIZE]`. */
37
+ declare function clampPageSize(pageSize: number | undefined): number;
38
+ /** Clamp a caller-supplied `page` to a 1-based integer (floor 1). */
39
+ declare function clampPage(page: number | undefined): number;
40
+ /**
41
+ * Zod schema for the universal list query string. All keys optional —
42
+ * pagination works fully UNFILTERED (the default mode). `pageSize` is clamped
43
+ * (default 50, max 200) and `sort_order` defaults to `desc`. Arbitrary where
44
+ * filters are NOT modeled here (they're parsed/passed through at the controller
45
+ * via `.passthrough()`); this schema owns ONLY the pagination + sort knobs so
46
+ * the defaults + clamp land in one place.
47
+ *
48
+ * `cursor` is accepted but v1 ignores it for fetching (offset engine) — the
49
+ * keyset seek is the deferred seam. Passing a `nextCursor` back never errors.
50
+ */
51
+ declare const ListQuerySchema: z.ZodObject<{
52
+ page: z.ZodOptional<z.ZodNumber>;
53
+ cursor: z.ZodOptional<z.ZodString>;
54
+ pageSize: z.ZodOptional<z.ZodNumber>;
55
+ sort_by: z.ZodOptional<z.ZodString>;
56
+ sort_order: z.ZodOptional<z.ZodEnum<["asc", "desc"]>>;
57
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
58
+ page: z.ZodOptional<z.ZodNumber>;
59
+ cursor: z.ZodOptional<z.ZodString>;
60
+ pageSize: z.ZodOptional<z.ZodNumber>;
61
+ sort_by: z.ZodOptional<z.ZodString>;
62
+ sort_order: z.ZodOptional<z.ZodEnum<["asc", "desc"]>>;
63
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
64
+ page: z.ZodOptional<z.ZodNumber>;
65
+ cursor: z.ZodOptional<z.ZodString>;
66
+ pageSize: z.ZodOptional<z.ZodNumber>;
67
+ sort_by: z.ZodOptional<z.ZodString>;
68
+ sort_order: z.ZodOptional<z.ZodEnum<["asc", "desc"]>>;
69
+ }, z.ZodTypeAny, "passthrough">>;
70
+ /** Parsed list query (pre-clamp). Use {@link resolveListQuery} to normalize. */
71
+ type ListQuery = z.infer<typeof ListQuerySchema>;
72
+ /**
73
+ * Normalized pagination options resolved from a raw {@link ListQuery}: clamped
74
+ * `page`/`pageSize`, computed `offset`, and a defaulted sort. The generated
75
+ * list use-case feeds these straight into `service.list({ limit, offset, ... })`.
76
+ */
77
+ interface ResolvedListQuery {
78
+ page: number;
79
+ pageSize: number;
80
+ /** `(page - 1) * pageSize`. */
81
+ offset: number;
82
+ sortBy: string;
83
+ sortOrder: 'asc' | 'desc';
84
+ /** Opaque cursor as passed by the caller (v1: not honored — deferred seam). */
85
+ cursor?: string;
86
+ }
87
+ /**
88
+ * Resolve a raw list query into normalized, clamped pagination options.
89
+ * Defaults: page 1, pageSize 50 (max 200), sort `created_at desc`.
90
+ */
91
+ declare function resolveListQuery(query: ListQuery | undefined): ResolvedListQuery;
92
+ /**
93
+ * One page of a paginated list response. EXTENDS the jobs subsystem's minimal
94
+ * `{ items, nextCursor }` shape with the numbered-UI fields:
95
+ *
96
+ * - `page` — 1-based page number of THIS page.
97
+ * - `pageCount` — total number of pages (`ceil(total / pageSize)`, min 1).
98
+ * - `total` — total matching rows (reflects any where-filter).
99
+ * - `pageSize` — the (clamped) page size used.
100
+ * - `nextCursor` — opaque keyset cursor of the LAST row, or `null` on the
101
+ * last page / empty result. Contract-stable from day one;
102
+ * v1 emits it but fetches by offset.
103
+ */
104
+ interface Page<T> {
105
+ items: T[];
106
+ page: number;
107
+ pageCount: number;
108
+ total: number;
109
+ pageSize: number;
110
+ nextCursor: string | null;
111
+ }
112
+ /** Keyset tuple a {@link Page.nextCursor} encodes. */
113
+ interface PageKeyset {
114
+ /** `created_at` of the last row on this page. */
115
+ createdAt: Date;
116
+ /** `id` (UUID) tie-break of the last row on this page. */
117
+ id: string;
118
+ }
119
+ /**
120
+ * Encode a `(createdAt, id)` keyset into an opaque, base64url cursor. The shape
121
+ * (a JSON tuple) is an implementation detail — never parse it outside this
122
+ * module. Mirrors `encodeKeysetCursor` in the jobs subsystem.
123
+ */
124
+ declare function encodeCursor(keyset: PageKeyset): string;
125
+ /**
126
+ * Decode an opaque cursor back into its `(createdAt, id)` keyset. Returns
127
+ * `null` for a malformed cursor so a caller can treat garbage as "start from
128
+ * the beginning" rather than throw on user-supplied data.
129
+ */
130
+ declare function decodeCursor(cursor: string): PageKeyset | null;
131
+ /**
132
+ * Compute the `nextCursor` for a page of rows: the opaque cursor of the LAST
133
+ * row when more pages remain, else `null`. A row is expected to carry
134
+ * `createdAt: Date` and `id: string` (the default-sort keyset). Rows missing
135
+ * either field yield `null` (cursor not derivable — caller falls back to offset
136
+ * paging, which is the v1 engine anyway).
137
+ *
138
+ * @param rows the items on this page (already fetched, in sort order)
139
+ * @param hasMore whether more rows exist beyond this page
140
+ * (`offset + rows.length < total`)
141
+ */
142
+ declare function computeNextCursor(rows: ReadonlyArray<unknown>, hasMore: boolean): string | null;
143
+ /**
144
+ * Assemble a {@link Page} envelope from a fetched page of rows + the total
145
+ * matching count + the resolved query. Computes `pageCount` and `nextCursor`.
146
+ * The single place the envelope shape is constructed, so the generated list
147
+ * use-case stays a thin call.
148
+ */
149
+ declare function buildPage<T>(items: T[], total: number, resolved: Pick<ResolvedListQuery, 'page' | 'pageSize' | 'offset'>): Page<T>;
150
+
151
+ export { DEFAULT_PAGE, DEFAULT_PAGE_SIZE, DEFAULT_SORT_BY, DEFAULT_SORT_ORDER, type ListQuery, ListQuerySchema, MAX_PAGE_SIZE, type Page, type PageKeyset, type ResolvedListQuery, buildPage, clampPage, clampPageSize, computeNextCursor, decodeCursor, encodeCursor, resolveListQuery };
@@ -0,0 +1,98 @@
1
+ import "../../chunk-2E224ZSN.js";
2
+
3
+ // runtime/http/pagination.ts
4
+ import { z } from "zod";
5
+ var DEFAULT_PAGE_SIZE = 50;
6
+ var MAX_PAGE_SIZE = 200;
7
+ var DEFAULT_PAGE = 1;
8
+ var DEFAULT_SORT_BY = "created_at";
9
+ var DEFAULT_SORT_ORDER = "desc";
10
+ function clampPageSize(pageSize) {
11
+ if (typeof pageSize !== "number" || !Number.isFinite(pageSize)) {
12
+ return DEFAULT_PAGE_SIZE;
13
+ }
14
+ const floored = Math.floor(pageSize);
15
+ if (floored < 1) return 1;
16
+ if (floored > MAX_PAGE_SIZE) return MAX_PAGE_SIZE;
17
+ return floored;
18
+ }
19
+ function clampPage(page) {
20
+ if (typeof page !== "number" || !Number.isFinite(page)) {
21
+ return DEFAULT_PAGE;
22
+ }
23
+ const floored = Math.floor(page);
24
+ return floored < 1 ? 1 : floored;
25
+ }
26
+ var ListQuerySchema = z.object({
27
+ page: z.coerce.number().int().optional(),
28
+ cursor: z.string().optional(),
29
+ pageSize: z.coerce.number().int().optional(),
30
+ sort_by: z.string().optional(),
31
+ sort_order: z.enum(["asc", "desc"]).optional()
32
+ }).passthrough();
33
+ function resolveListQuery(query) {
34
+ const page = clampPage(query?.page);
35
+ const pageSize = clampPageSize(query?.pageSize);
36
+ return {
37
+ page,
38
+ pageSize,
39
+ offset: (page - 1) * pageSize,
40
+ sortBy: query?.sort_by ?? DEFAULT_SORT_BY,
41
+ sortOrder: query?.sort_order ?? DEFAULT_SORT_ORDER,
42
+ cursor: query?.cursor
43
+ };
44
+ }
45
+ function encodeCursor(keyset) {
46
+ const tuple = [keyset.createdAt.toISOString(), keyset.id];
47
+ return Buffer.from(JSON.stringify(tuple), "utf8").toString("base64url");
48
+ }
49
+ function decodeCursor(cursor) {
50
+ try {
51
+ const json = Buffer.from(cursor, "base64url").toString("utf8");
52
+ const parsed = JSON.parse(json);
53
+ if (!Array.isArray(parsed) || parsed.length !== 2) return null;
54
+ const [iso, id] = parsed;
55
+ if (typeof iso !== "string" || typeof id !== "string") return null;
56
+ const createdAt = new Date(iso);
57
+ if (Number.isNaN(createdAt.getTime())) return null;
58
+ return { createdAt, id };
59
+ } catch {
60
+ return null;
61
+ }
62
+ }
63
+ function computeNextCursor(rows, hasMore) {
64
+ if (!hasMore || rows.length === 0) return null;
65
+ const last = rows[rows.length - 1];
66
+ const createdAt = last?.createdAt;
67
+ const id = last?.id;
68
+ if (!(createdAt instanceof Date) || typeof id !== "string") return null;
69
+ return encodeCursor({ createdAt, id });
70
+ }
71
+ function buildPage(items, total, resolved) {
72
+ const pageCount = total === 0 ? 1 : Math.ceil(total / resolved.pageSize);
73
+ const hasMore = resolved.offset + items.length < total;
74
+ return {
75
+ items,
76
+ page: resolved.page,
77
+ pageCount,
78
+ total,
79
+ pageSize: resolved.pageSize,
80
+ nextCursor: computeNextCursor(items, hasMore)
81
+ };
82
+ }
83
+ export {
84
+ DEFAULT_PAGE,
85
+ DEFAULT_PAGE_SIZE,
86
+ DEFAULT_SORT_BY,
87
+ DEFAULT_SORT_ORDER,
88
+ ListQuerySchema,
89
+ MAX_PAGE_SIZE,
90
+ buildPage,
91
+ clampPage,
92
+ clampPageSize,
93
+ computeNextCursor,
94
+ decodeCursor,
95
+ encodeCursor,
96
+ resolveListQuery
97
+ };
98
+ //# sourceMappingURL=pagination.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../runtime/http/pagination.ts"],"sourcesContent":["/**\n * Pagination envelope + ListQuery contract for generated entity list endpoints\n * (pagination-by-default).\n *\n * Every generated `GET /<entities>` returns a {@link Page} envelope instead of\n * a bare `T[]`. The request shape is {@link ListQuery} (page/cursor/pageSize +\n * default sort) merged with arbitrary where-filters at the controller.\n *\n * Mirrors the in-repo precedent: the jobs subsystem's `JobRunPage`\n * (`{ items, nextCursor }`) and its opaque keyset cursor codec\n * (`runtime/subsystems/jobs/job-run-keyset-cursor.ts`). The entity envelope\n * EXTENDS that minimal shape with `page/pageCount/total/pageSize` so a numbered\n * UI (jump-to-page) works while `nextCursor` stays contract-stable for the\n * later keyset upgrade.\n *\n * ENGINE NOTE (v1): the list use-case fetches by OFFSET (page-based). The\n * `nextCursor` is computed from the last row and emitted from day one so the\n * contract never changes, but cursor-REQUEST honoring (keyset seek) is a\n * DEFERRED seam — see the TODO in the generated list use-case / the repository\n * query branch. `ListQuery.cursor` is ACCEPTED (no validation error) even\n * though v1 ignores it for fetching.\n */\n\nimport { z } from 'zod';\n\n// ============================================================================\n// Defaults + clamp\n// ============================================================================\n\n/** Default page size when `pageSize` is omitted. */\nexport const DEFAULT_PAGE_SIZE = 50;\n/** Hard upper bound on page size to keep a single read bounded. */\nexport const MAX_PAGE_SIZE = 200;\n/** Default page (1-based) when `page` is omitted. */\nexport const DEFAULT_PAGE = 1;\n/** Default sort column. */\nexport const DEFAULT_SORT_BY = 'created_at';\n/** Default sort direction. */\nexport const DEFAULT_SORT_ORDER = 'desc' as const;\n\n/** Clamp a caller-supplied `pageSize` into `[1, MAX_PAGE_SIZE]`. */\nexport function clampPageSize(pageSize: number | undefined): number {\n if (typeof pageSize !== 'number' || !Number.isFinite(pageSize)) {\n return DEFAULT_PAGE_SIZE;\n }\n const floored = Math.floor(pageSize);\n if (floored < 1) return 1;\n if (floored > MAX_PAGE_SIZE) return MAX_PAGE_SIZE;\n return floored;\n}\n\n/** Clamp a caller-supplied `page` to a 1-based integer (floor 1). */\nexport function clampPage(page: number | undefined): number {\n if (typeof page !== 'number' || !Number.isFinite(page)) {\n return DEFAULT_PAGE;\n }\n const floored = Math.floor(page);\n return floored < 1 ? 1 : floored;\n}\n\n// ============================================================================\n// ListQuery schema (request)\n// ============================================================================\n\n/**\n * Zod schema for the universal list query string. All keys optional —\n * pagination works fully UNFILTERED (the default mode). `pageSize` is clamped\n * (default 50, max 200) and `sort_order` defaults to `desc`. Arbitrary where\n * filters are NOT modeled here (they're parsed/passed through at the controller\n * via `.passthrough()`); this schema owns ONLY the pagination + sort knobs so\n * the defaults + clamp land in one place.\n *\n * `cursor` is accepted but v1 ignores it for fetching (offset engine) — the\n * keyset seek is the deferred seam. Passing a `nextCursor` back never errors.\n */\nexport const ListQuerySchema = z\n .object({\n page: z.coerce.number().int().optional(),\n cursor: z.string().optional(),\n pageSize: z.coerce.number().int().optional(),\n sort_by: z.string().optional(),\n sort_order: z.enum(['asc', 'desc']).optional(),\n })\n .passthrough();\n\n/** Parsed list query (pre-clamp). Use {@link resolveListQuery} to normalize. */\nexport type ListQuery = z.infer<typeof ListQuerySchema>;\n\n/**\n * Normalized pagination options resolved from a raw {@link ListQuery}: clamped\n * `page`/`pageSize`, computed `offset`, and a defaulted sort. The generated\n * list use-case feeds these straight into `service.list({ limit, offset, ... })`.\n */\nexport interface ResolvedListQuery {\n page: number;\n pageSize: number;\n /** `(page - 1) * pageSize`. */\n offset: number;\n sortBy: string;\n sortOrder: 'asc' | 'desc';\n /** Opaque cursor as passed by the caller (v1: not honored — deferred seam). */\n cursor?: string;\n}\n\n/**\n * Resolve a raw list query into normalized, clamped pagination options.\n * Defaults: page 1, pageSize 50 (max 200), sort `created_at desc`.\n */\nexport function resolveListQuery(query: ListQuery | undefined): ResolvedListQuery {\n const page = clampPage(query?.page);\n const pageSize = clampPageSize(query?.pageSize);\n return {\n page,\n pageSize,\n offset: (page - 1) * pageSize,\n sortBy: query?.sort_by ?? DEFAULT_SORT_BY,\n sortOrder: query?.sort_order ?? DEFAULT_SORT_ORDER,\n cursor: query?.cursor,\n };\n}\n\n// ============================================================================\n// Page envelope (response)\n// ============================================================================\n\n/**\n * One page of a paginated list response. EXTENDS the jobs subsystem's minimal\n * `{ items, nextCursor }` shape with the numbered-UI fields:\n *\n * - `page` — 1-based page number of THIS page.\n * - `pageCount` — total number of pages (`ceil(total / pageSize)`, min 1).\n * - `total` — total matching rows (reflects any where-filter).\n * - `pageSize` — the (clamped) page size used.\n * - `nextCursor` — opaque keyset cursor of the LAST row, or `null` on the\n * last page / empty result. Contract-stable from day one;\n * v1 emits it but fetches by offset.\n */\nexport interface Page<T> {\n items: T[];\n page: number;\n pageCount: number;\n total: number;\n pageSize: number;\n nextCursor: string | null;\n}\n\n// ============================================================================\n// Opaque cursor codec (encodes (createdAt, id))\n// ============================================================================\n\n/** Keyset tuple a {@link Page.nextCursor} encodes. */\nexport interface PageKeyset {\n /** `created_at` of the last row on this page. */\n createdAt: Date;\n /** `id` (UUID) tie-break of the last row on this page. */\n id: string;\n}\n\n/**\n * Encode a `(createdAt, id)` keyset into an opaque, base64url cursor. The shape\n * (a JSON tuple) is an implementation detail — never parse it outside this\n * module. Mirrors `encodeKeysetCursor` in the jobs subsystem.\n */\nexport function encodeCursor(keyset: PageKeyset): string {\n const tuple = [keyset.createdAt.toISOString(), keyset.id];\n return Buffer.from(JSON.stringify(tuple), 'utf8').toString('base64url');\n}\n\n/**\n * Decode an opaque cursor back into its `(createdAt, id)` keyset. Returns\n * `null` for a malformed cursor so a caller can treat garbage as \"start from\n * the beginning\" rather than throw on user-supplied data.\n */\nexport function decodeCursor(cursor: string): PageKeyset | null {\n try {\n const json = Buffer.from(cursor, 'base64url').toString('utf8');\n const parsed = JSON.parse(json) as unknown;\n if (!Array.isArray(parsed) || parsed.length !== 2) return null;\n const [iso, id] = parsed;\n if (typeof iso !== 'string' || typeof id !== 'string') return null;\n const createdAt = new Date(iso);\n if (Number.isNaN(createdAt.getTime())) return null;\n return { createdAt, id };\n } catch {\n return null;\n }\n}\n\n/**\n * Compute the `nextCursor` for a page of rows: the opaque cursor of the LAST\n * row when more pages remain, else `null`. A row is expected to carry\n * `createdAt: Date` and `id: string` (the default-sort keyset). Rows missing\n * either field yield `null` (cursor not derivable — caller falls back to offset\n * paging, which is the v1 engine anyway).\n *\n * @param rows the items on this page (already fetched, in sort order)\n * @param hasMore whether more rows exist beyond this page\n * (`offset + rows.length < total`)\n */\nexport function computeNextCursor(\n rows: ReadonlyArray<unknown>,\n hasMore: boolean,\n): string | null {\n if (!hasMore || rows.length === 0) return null;\n const last = rows[rows.length - 1] as { createdAt?: unknown; id?: unknown };\n const createdAt = last?.createdAt;\n const id = last?.id;\n if (!(createdAt instanceof Date) || typeof id !== 'string') return null;\n return encodeCursor({ createdAt, id });\n}\n\n/**\n * Assemble a {@link Page} envelope from a fetched page of rows + the total\n * matching count + the resolved query. Computes `pageCount` and `nextCursor`.\n * The single place the envelope shape is constructed, so the generated list\n * use-case stays a thin call.\n */\nexport function buildPage<T>(\n items: T[],\n total: number,\n resolved: Pick<ResolvedListQuery, 'page' | 'pageSize' | 'offset'>,\n): Page<T> {\n const pageCount = total === 0 ? 1 : Math.ceil(total / resolved.pageSize);\n const hasMore = resolved.offset + items.length < total;\n return {\n items,\n page: resolved.page,\n pageCount,\n total,\n pageSize: resolved.pageSize,\n nextCursor: computeNextCursor(items as ReadonlyArray<unknown>, hasMore),\n };\n}\n"],"mappings":";;;AAuBA,SAAS,SAAS;AAOX,IAAM,oBAAoB;AAE1B,IAAM,gBAAgB;AAEtB,IAAM,eAAe;AAErB,IAAM,kBAAkB;AAExB,IAAM,qBAAqB;AAG3B,SAAS,cAAc,UAAsC;AAClE,MAAI,OAAO,aAAa,YAAY,CAAC,OAAO,SAAS,QAAQ,GAAG;AAC9D,WAAO;AAAA,EACT;AACA,QAAM,UAAU,KAAK,MAAM,QAAQ;AACnC,MAAI,UAAU,EAAG,QAAO;AACxB,MAAI,UAAU,cAAe,QAAO;AACpC,SAAO;AACT;AAGO,SAAS,UAAU,MAAkC;AAC1D,MAAI,OAAO,SAAS,YAAY,CAAC,OAAO,SAAS,IAAI,GAAG;AACtD,WAAO;AAAA,EACT;AACA,QAAM,UAAU,KAAK,MAAM,IAAI;AAC/B,SAAO,UAAU,IAAI,IAAI;AAC3B;AAiBO,IAAM,kBAAkB,EAC5B,OAAO;AAAA,EACN,MAAM,EAAE,OAAO,OAAO,EAAE,IAAI,EAAE,SAAS;AAAA,EACvC,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,UAAU,EAAE,OAAO,OAAO,EAAE,IAAI,EAAE,SAAS;AAAA,EAC3C,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,EAC7B,YAAY,EAAE,KAAK,CAAC,OAAO,MAAM,CAAC,EAAE,SAAS;AAC/C,CAAC,EACA,YAAY;AAyBR,SAAS,iBAAiB,OAAiD;AAChF,QAAM,OAAO,UAAU,OAAO,IAAI;AAClC,QAAM,WAAW,cAAc,OAAO,QAAQ;AAC9C,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,SAAS,OAAO,KAAK;AAAA,IACrB,QAAQ,OAAO,WAAW;AAAA,IAC1B,WAAW,OAAO,cAAc;AAAA,IAChC,QAAQ,OAAO;AAAA,EACjB;AACF;AA4CO,SAAS,aAAa,QAA4B;AACvD,QAAM,QAAQ,CAAC,OAAO,UAAU,YAAY,GAAG,OAAO,EAAE;AACxD,SAAO,OAAO,KAAK,KAAK,UAAU,KAAK,GAAG,MAAM,EAAE,SAAS,WAAW;AACxE;AAOO,SAAS,aAAa,QAAmC;AAC9D,MAAI;AACF,UAAM,OAAO,OAAO,KAAK,QAAQ,WAAW,EAAE,SAAS,MAAM;AAC7D,UAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,QAAI,CAAC,MAAM,QAAQ,MAAM,KAAK,OAAO,WAAW,EAAG,QAAO;AAC1D,UAAM,CAAC,KAAK,EAAE,IAAI;AAClB,QAAI,OAAO,QAAQ,YAAY,OAAO,OAAO,SAAU,QAAO;AAC9D,UAAM,YAAY,IAAI,KAAK,GAAG;AAC9B,QAAI,OAAO,MAAM,UAAU,QAAQ,CAAC,EAAG,QAAO;AAC9C,WAAO,EAAE,WAAW,GAAG;AAAA,EACzB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAaO,SAAS,kBACd,MACA,SACe;AACf,MAAI,CAAC,WAAW,KAAK,WAAW,EAAG,QAAO;AAC1C,QAAM,OAAO,KAAK,KAAK,SAAS,CAAC;AACjC,QAAM,YAAY,MAAM;AACxB,QAAM,KAAK,MAAM;AACjB,MAAI,EAAE,qBAAqB,SAAS,OAAO,OAAO,SAAU,QAAO;AACnE,SAAO,aAAa,EAAE,WAAW,GAAG,CAAC;AACvC;AAQO,SAAS,UACd,OACA,OACA,UACS;AACT,QAAM,YAAY,UAAU,IAAI,IAAI,KAAK,KAAK,QAAQ,SAAS,QAAQ;AACvE,QAAM,UAAU,SAAS,SAAS,MAAM,SAAS;AACjD,SAAO;AAAA,IACL;AAAA,IACA,MAAM,SAAS;AAAA,IACf;AAAA,IACA;AAAA,IACA,UAAU,SAAS;AAAA,IACnB,YAAY,kBAAkB,OAAiC,OAAO;AAAA,EACxE;AACF;","names":[]}
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  AnalyticsModule
3
- } from "../../../chunk-CZQUOIDY.js";
3
+ } from "../../../chunk-DKKFTHHI.js";
4
+ import "../../../chunk-J37YWU7Y.js";
4
5
  import "../../../chunk-7B3RYX45.js";
5
6
  import "../../../chunk-6I7ULIN6.js";
6
- import "../../../chunk-J37YWU7Y.js";
7
7
  import "../../../chunk-GYGNEQSC.js";
8
8
  import "../../../chunk-2E224ZSN.js";
9
9
  export {
@@ -1,6 +1,9 @@
1
1
  import {
2
2
  AnalyticsModule
3
- } from "../../../chunk-CZQUOIDY.js";
3
+ } from "../../../chunk-DKKFTHHI.js";
4
+ import {
5
+ NoopAnalyticsBackend
6
+ } from "../../../chunk-J37YWU7Y.js";
4
7
  import {
5
8
  CubeAnalyticsBackend
6
9
  } from "../../../chunk-7B3RYX45.js";
@@ -9,9 +12,6 @@ import {
9
12
  CUBE_API_SECRET,
10
13
  CUBE_API_URL
11
14
  } from "../../../chunk-6I7ULIN6.js";
12
- import {
13
- NoopAnalyticsBackend
14
- } from "../../../chunk-J37YWU7Y.js";
15
15
  import "../../../chunk-GYGNEQSC.js";
16
16
  import "../../../chunk-2E224ZSN.js";
17
17
  export {
@@ -1,13 +1,13 @@
1
1
  import "../../../chunk-7C3FOSDI.js";
2
+ import {
3
+ withAuthRetry
4
+ } from "../../../chunk-WL67FZGF.js";
2
5
  import {
3
6
  OAuth2RefreshStrategy
4
7
  } from "../../../chunk-M6QLSLPO.js";
5
8
  import {
6
9
  ConnectionBrokenError
7
10
  } from "../../../chunk-2N4UG4VD.js";
8
- import {
9
- withAuthRetry
10
- } from "../../../chunk-WL67FZGF.js";
11
11
  import {
12
12
  SessionExpiredError,
13
13
  isSessionExpiredError
@@ -1,30 +1,30 @@
1
1
  import {
2
2
  BridgeModule
3
- } from "../../../chunk-XNRKZCVH.js";
3
+ } from "../../../chunk-36NRG2EP.js";
4
4
  import "../../../chunk-5A432NZJ.js";
5
+ import "../../../chunk-E5FJWOMP.js";
5
6
  import "../../../chunk-7OVCARTQ.js";
6
7
  import "../../../chunk-EDKJU5BO.js";
7
- import "../../../chunk-MVKW2BCR.js";
8
- import "../../../chunk-4DOJBQTP.js";
9
- import "../../../chunk-E5FJWOMP.js";
10
8
  import "../../../chunk-R6F6KFIL.js";
9
+ import "../../../chunk-MVKW2BCR.js";
11
10
  import "../../../chunk-6DWFJNIK.js";
11
+ import "../../../chunk-4DOJBQTP.js";
12
12
  import "../../../chunk-BORNCTH3.js";
13
13
  import "../../../chunk-NXXDZ6ZF.js";
14
- import "../../../chunk-ENAR3F5S.js";
15
- import "../../../chunk-7B7MMDOJ.js";
16
- import "../../../chunk-CKLM57IE.js";
17
- import "../../../chunk-3VEVGL74.js";
18
- import "../../../chunk-CDLWYZVQ.js";
19
- import "../../../chunk-L3LZWWSX.js";
14
+ import "../../../chunk-Z7YFYK6H.js";
15
+ import "../../../chunk-KS4BZHIA.js";
16
+ import "../../../chunk-BHZP6LOV.js";
20
17
  import "../../../chunk-DV4RV2DC.js";
21
- import "../../../chunk-I6MVCB5A.js";
22
- import "../../../chunk-RHVN6NA7.js";
18
+ import "../../../chunk-7B7MMDOJ.js";
23
19
  import "../../../chunk-E6PLM6QG.js";
24
20
  import "../../../chunk-VQOAATIG.js";
25
21
  import "../../../chunk-PNZSGAB2.js";
26
22
  import "../../../chunk-SNQ3TOWP.js";
23
+ import "../../../chunk-VNBC3VXM.js";
27
24
  import "../../../chunk-T4BIIU5E.js";
25
+ import "../../../chunk-L3LZWWSX.js";
26
+ import "../../../chunk-I6MVCB5A.js";
27
+ import "../../../chunk-RHVN6NA7.js";
28
28
  import "../../../chunk-Q6LRJ4VI.js";
29
29
  import "../../../chunk-7P5ODGLA.js";
30
30
  import "../../../chunk-ZPL74UQN.js";
@@ -1,29 +1,29 @@
1
1
  import {
2
2
  BridgeModule
3
- } from "../../../chunk-XNRKZCVH.js";
3
+ } from "../../../chunk-36NRG2EP.js";
4
4
  import "../../../chunk-5A432NZJ.js";
5
+ import {
6
+ BridgeOutboxDrainHook
7
+ } from "../../../chunk-E5FJWOMP.js";
5
8
  import {
6
9
  EventFlowService
7
10
  } from "../../../chunk-7OVCARTQ.js";
8
11
  import {
9
12
  BRIDGE_RESERVED_POOLS
10
13
  } from "../../../chunk-EDKJU5BO.js";
11
- import {
12
- DrizzleBridgeDeliveryRepo
13
- } from "../../../chunk-MVKW2BCR.js";
14
- import {
15
- MemoryBridgeDeliveryRepo
16
- } from "../../../chunk-4DOJBQTP.js";
17
- import {
18
- BridgeOutboxDrainHook
19
- } from "../../../chunk-E5FJWOMP.js";
20
14
  import {
21
15
  BRIDGE_DELIVERY_JOB_TYPE,
22
16
  BridgeDeliveryHandler
23
17
  } from "../../../chunk-R6F6KFIL.js";
18
+ import {
19
+ DrizzleBridgeDeliveryRepo
20
+ } from "../../../chunk-MVKW2BCR.js";
24
21
  import {
25
22
  assertTenantId
26
23
  } from "../../../chunk-6DWFJNIK.js";
24
+ import {
25
+ MemoryBridgeDeliveryRepo
26
+ } from "../../../chunk-4DOJBQTP.js";
27
27
  import {
28
28
  bridgeDelivery,
29
29
  bridgeDeliveryStatusEnum
@@ -33,20 +33,20 @@ import {
33
33
  MissingTenantIdError,
34
34
  UniqueConstraintError
35
35
  } from "../../../chunk-NXXDZ6ZF.js";
36
- import "../../../chunk-ENAR3F5S.js";
37
- import "../../../chunk-7B7MMDOJ.js";
38
- import "../../../chunk-CKLM57IE.js";
39
- import "../../../chunk-3VEVGL74.js";
40
- import "../../../chunk-CDLWYZVQ.js";
41
- import "../../../chunk-L3LZWWSX.js";
36
+ import "../../../chunk-Z7YFYK6H.js";
37
+ import "../../../chunk-KS4BZHIA.js";
38
+ import "../../../chunk-BHZP6LOV.js";
42
39
  import "../../../chunk-DV4RV2DC.js";
43
- import "../../../chunk-I6MVCB5A.js";
44
- import "../../../chunk-RHVN6NA7.js";
40
+ import "../../../chunk-7B7MMDOJ.js";
45
41
  import "../../../chunk-E6PLM6QG.js";
46
42
  import "../../../chunk-VQOAATIG.js";
47
43
  import "../../../chunk-PNZSGAB2.js";
48
44
  import "../../../chunk-SNQ3TOWP.js";
45
+ import "../../../chunk-VNBC3VXM.js";
49
46
  import "../../../chunk-T4BIIU5E.js";
47
+ import "../../../chunk-L3LZWWSX.js";
48
+ import "../../../chunk-I6MVCB5A.js";
49
+ import "../../../chunk-RHVN6NA7.js";
50
50
  import "../../../chunk-Q6LRJ4VI.js";
51
51
  import "../../../chunk-7P5ODGLA.js";
52
52
  import "../../../chunk-ZPL74UQN.js";
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  CacheModule
3
- } from "../../../chunk-COGHTKXY.js";
4
- import "../../../chunk-T6C4LFLC.js";
3
+ } from "../../../chunk-7625PLY7.js";
5
4
  import "../../../chunk-IF5I3DAA.js";
5
+ import "../../../chunk-T6C4LFLC.js";
6
6
  import "../../../chunk-FASRXRX5.js";
7
7
  import "../../../chunk-L6FTY45T.js";
8
8
  import "../../../chunk-GYGNEQSC.js";
@@ -1,13 +1,13 @@
1
1
  import "../../../chunk-IWAOY6KC.js";
2
2
  import {
3
3
  CacheModule
4
- } from "../../../chunk-COGHTKXY.js";
5
- import {
6
- DrizzleCacheService
7
- } from "../../../chunk-T6C4LFLC.js";
4
+ } from "../../../chunk-7625PLY7.js";
8
5
  import {
9
6
  MemoryCacheService
10
7
  } from "../../../chunk-IF5I3DAA.js";
8
+ import {
9
+ DrizzleCacheService
10
+ } from "../../../chunk-T6C4LFLC.js";
11
11
  import {
12
12
  cacheEntries
13
13
  } from "../../../chunk-FASRXRX5.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  DrizzleEventBus
3
- } from "../../../chunk-PNCOUFFI.js";
3
+ } from "../../../chunk-Y6UZMYGX.js";
4
4
  import "../../../chunk-UQ5EHOH2.js";
5
5
  import "../../../chunk-Q6LRJ4VI.js";
6
6
  import "../../../chunk-H5NH7KPE.js";
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  EventSchedulerLifecycle,
3
3
  EventsModule
4
- } from "../../../chunk-6M6LZEP6.js";
4
+ } from "../../../chunk-YHVZAL6U.js";
5
5
  import "../../../chunk-PBENHIN2.js";
6
6
  import "../../../chunk-LQZESSM3.js";
7
7
  import "../../../chunk-MU54DZCC.js";
8
+ import "../../../chunk-GOO5ZMYO.js";
8
9
  import "../../../chunk-DUUCU77W.js";
9
10
  import "../../../chunk-DUBZOXJC.js";
10
- import "../../../chunk-PNCOUFFI.js";
11
- import "../../../chunk-GOO5ZMYO.js";
11
+ import "../../../chunk-Y6UZMYGX.js";
12
12
  import "../../../chunk-UQ5EHOH2.js";
13
13
  import "../../../chunk-Q6LRJ4VI.js";
14
14
  import "../../../chunk-H5NH7KPE.js";
@@ -2,12 +2,15 @@ import "../../../chunk-SYVZ4MD2.js";
2
2
  import {
3
3
  EventSchedulerLifecycle,
4
4
  EventsModule
5
- } from "../../../chunk-6M6LZEP6.js";
5
+ } from "../../../chunk-YHVZAL6U.js";
6
6
  import {
7
7
  TypedEventBus
8
8
  } from "../../../chunk-PBENHIN2.js";
9
9
  import "../../../chunk-LQZESSM3.js";
10
10
  import "../../../chunk-MU54DZCC.js";
11
+ import {
12
+ MemoryEventBus
13
+ } from "../../../chunk-GOO5ZMYO.js";
11
14
  import {
12
15
  EventScheduler,
13
16
  SCHEDULE_FLOOR_MS,
@@ -25,10 +28,7 @@ import {
25
28
  } from "../../../chunk-DUBZOXJC.js";
26
29
  import {
27
30
  DrizzleEventBus
28
- } from "../../../chunk-PNCOUFFI.js";
29
- import {
30
- MemoryEventBus
31
- } from "../../../chunk-GOO5ZMYO.js";
31
+ } from "../../../chunk-Y6UZMYGX.js";
32
32
  import "../../../chunk-UQ5EHOH2.js";
33
33
  import "../../../chunk-Q6LRJ4VI.js";
34
34
  import {