@unifiedcommerce/core 0.0.4 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (179) hide show
  1. package/dist/auth/auth-schema.d.ts +92 -0
  2. package/dist/auth/auth-schema.d.ts.map +1 -1
  3. package/dist/auth/auth-schema.js +7 -0
  4. package/dist/auth/setup.d.ts.map +1 -1
  5. package/dist/auth/setup.js +3 -1
  6. package/package.json +1 -2
  7. package/src/adapters/console-email.ts +0 -43
  8. package/src/auth/access.ts +0 -187
  9. package/src/auth/auth-schema.ts +0 -131
  10. package/src/auth/middleware.ts +0 -161
  11. package/src/auth/org.ts +0 -41
  12. package/src/auth/permissions.ts +0 -28
  13. package/src/auth/setup.ts +0 -165
  14. package/src/auth/system-actor.ts +0 -19
  15. package/src/auth/types.ts +0 -10
  16. package/src/config/defaults.ts +0 -82
  17. package/src/config/define-config.ts +0 -53
  18. package/src/config/types.ts +0 -299
  19. package/src/generated/plugin-capabilities.d.ts +0 -20
  20. package/src/generated/plugin-manifest.ts +0 -23
  21. package/src/generated/plugin-repositories.d.ts +0 -20
  22. package/src/hooks/checkout-completion.ts +0 -262
  23. package/src/hooks/checkout.ts +0 -677
  24. package/src/hooks/order-emails.ts +0 -62
  25. package/src/index.ts +0 -214
  26. package/src/interfaces/mcp/agent-prompt.ts +0 -174
  27. package/src/interfaces/mcp/context-enrichment.ts +0 -177
  28. package/src/interfaces/mcp/server.ts +0 -617
  29. package/src/interfaces/mcp/transport.ts +0 -68
  30. package/src/interfaces/rest/customer-portal.ts +0 -299
  31. package/src/interfaces/rest/index.ts +0 -74
  32. package/src/interfaces/rest/router.ts +0 -334
  33. package/src/interfaces/rest/routes/admin-jobs.ts +0 -58
  34. package/src/interfaces/rest/routes/audit.ts +0 -50
  35. package/src/interfaces/rest/routes/carts.ts +0 -89
  36. package/src/interfaces/rest/routes/catalog.ts +0 -493
  37. package/src/interfaces/rest/routes/checkout.ts +0 -283
  38. package/src/interfaces/rest/routes/inventory.ts +0 -70
  39. package/src/interfaces/rest/routes/media.ts +0 -86
  40. package/src/interfaces/rest/routes/orders.ts +0 -78
  41. package/src/interfaces/rest/routes/payments.ts +0 -60
  42. package/src/interfaces/rest/routes/pricing.ts +0 -57
  43. package/src/interfaces/rest/routes/promotions.ts +0 -92
  44. package/src/interfaces/rest/routes/search.ts +0 -71
  45. package/src/interfaces/rest/routes/webhooks.ts +0 -46
  46. package/src/interfaces/rest/schemas/admin-jobs.ts +0 -40
  47. package/src/interfaces/rest/schemas/audit.ts +0 -46
  48. package/src/interfaces/rest/schemas/carts.ts +0 -125
  49. package/src/interfaces/rest/schemas/catalog.ts +0 -450
  50. package/src/interfaces/rest/schemas/checkout.ts +0 -66
  51. package/src/interfaces/rest/schemas/customer-portal.ts +0 -195
  52. package/src/interfaces/rest/schemas/inventory.ts +0 -138
  53. package/src/interfaces/rest/schemas/media.ts +0 -75
  54. package/src/interfaces/rest/schemas/orders.ts +0 -104
  55. package/src/interfaces/rest/schemas/pricing.ts +0 -80
  56. package/src/interfaces/rest/schemas/promotions.ts +0 -110
  57. package/src/interfaces/rest/schemas/responses.ts +0 -85
  58. package/src/interfaces/rest/schemas/search.ts +0 -58
  59. package/src/interfaces/rest/schemas/shared.ts +0 -62
  60. package/src/interfaces/rest/schemas/webhooks.ts +0 -68
  61. package/src/interfaces/rest/utils.ts +0 -104
  62. package/src/interfaces/rest/webhook-router.ts +0 -50
  63. package/src/kernel/compensation/executor.ts +0 -61
  64. package/src/kernel/compensation/types.ts +0 -26
  65. package/src/kernel/database/adapter.ts +0 -13
  66. package/src/kernel/database/drizzle-db.ts +0 -56
  67. package/src/kernel/database/migrate.ts +0 -76
  68. package/src/kernel/database/plugin-types.ts +0 -34
  69. package/src/kernel/database/schema.ts +0 -49
  70. package/src/kernel/database/scoped-db.ts +0 -68
  71. package/src/kernel/database/tx-context.ts +0 -46
  72. package/src/kernel/error-mapper.ts +0 -15
  73. package/src/kernel/errors.ts +0 -89
  74. package/src/kernel/factory/repository-factory.ts +0 -242
  75. package/src/kernel/hooks/create-context.ts +0 -43
  76. package/src/kernel/hooks/executor.ts +0 -88
  77. package/src/kernel/hooks/registry.ts +0 -74
  78. package/src/kernel/hooks/types.ts +0 -52
  79. package/src/kernel/http-error.ts +0 -44
  80. package/src/kernel/jobs/adapter.ts +0 -36
  81. package/src/kernel/jobs/drizzle-adapter.ts +0 -58
  82. package/src/kernel/jobs/runner.ts +0 -153
  83. package/src/kernel/jobs/schema.ts +0 -46
  84. package/src/kernel/jobs/types.ts +0 -30
  85. package/src/kernel/local-api.ts +0 -185
  86. package/src/kernel/plugin/manifest.ts +0 -253
  87. package/src/kernel/query/executor.ts +0 -184
  88. package/src/kernel/query/registry.ts +0 -46
  89. package/src/kernel/result.ts +0 -33
  90. package/src/kernel/schema/extra-columns.ts +0 -37
  91. package/src/kernel/service-registry.ts +0 -76
  92. package/src/kernel/service-timing.ts +0 -89
  93. package/src/kernel/state-machine/machine.ts +0 -101
  94. package/src/modules/analytics/drizzle-adapter.ts +0 -426
  95. package/src/modules/analytics/hooks.ts +0 -11
  96. package/src/modules/analytics/models.ts +0 -125
  97. package/src/modules/analytics/repository/index.ts +0 -6
  98. package/src/modules/analytics/service.ts +0 -245
  99. package/src/modules/analytics/types.ts +0 -180
  100. package/src/modules/audit/hooks.ts +0 -78
  101. package/src/modules/audit/schema.ts +0 -33
  102. package/src/modules/audit/service.ts +0 -151
  103. package/src/modules/cart/access.ts +0 -27
  104. package/src/modules/cart/matcher.ts +0 -26
  105. package/src/modules/cart/repository/index.ts +0 -234
  106. package/src/modules/cart/schema.ts +0 -42
  107. package/src/modules/cart/schemas.ts +0 -38
  108. package/src/modules/cart/service.ts +0 -541
  109. package/src/modules/catalog/repository/index.ts +0 -772
  110. package/src/modules/catalog/schema.ts +0 -203
  111. package/src/modules/catalog/schemas.ts +0 -104
  112. package/src/modules/catalog/service.ts +0 -1544
  113. package/src/modules/customers/repository/index.ts +0 -327
  114. package/src/modules/customers/schema.ts +0 -64
  115. package/src/modules/customers/service.ts +0 -171
  116. package/src/modules/fulfillment/repository/index.ts +0 -426
  117. package/src/modules/fulfillment/schema.ts +0 -101
  118. package/src/modules/fulfillment/service.ts +0 -555
  119. package/src/modules/fulfillment/types.ts +0 -59
  120. package/src/modules/inventory/repository/index.ts +0 -509
  121. package/src/modules/inventory/schema.ts +0 -94
  122. package/src/modules/inventory/schemas.ts +0 -38
  123. package/src/modules/inventory/service.ts +0 -490
  124. package/src/modules/media/adapter.ts +0 -17
  125. package/src/modules/media/repository/index.ts +0 -274
  126. package/src/modules/media/schema.ts +0 -41
  127. package/src/modules/media/service.ts +0 -151
  128. package/src/modules/orders/repository/index.ts +0 -287
  129. package/src/modules/orders/schema.ts +0 -66
  130. package/src/modules/orders/service.ts +0 -619
  131. package/src/modules/orders/stale-order-cleanup.ts +0 -76
  132. package/src/modules/organization/service.ts +0 -191
  133. package/src/modules/payments/adapter.ts +0 -47
  134. package/src/modules/payments/repository/index.ts +0 -6
  135. package/src/modules/payments/service.ts +0 -107
  136. package/src/modules/pricing/repository/index.ts +0 -291
  137. package/src/modules/pricing/schema.ts +0 -71
  138. package/src/modules/pricing/schemas.ts +0 -38
  139. package/src/modules/pricing/service.ts +0 -494
  140. package/src/modules/promotions/repository/index.ts +0 -325
  141. package/src/modules/promotions/schema.ts +0 -62
  142. package/src/modules/promotions/schemas.ts +0 -38
  143. package/src/modules/promotions/service.ts +0 -598
  144. package/src/modules/search/adapter.ts +0 -57
  145. package/src/modules/search/hooks.ts +0 -12
  146. package/src/modules/search/repository/index.ts +0 -6
  147. package/src/modules/search/service.ts +0 -315
  148. package/src/modules/shipping/calculator.ts +0 -188
  149. package/src/modules/shipping/repository/index.ts +0 -6
  150. package/src/modules/shipping/service.ts +0 -51
  151. package/src/modules/tax/adapter.ts +0 -60
  152. package/src/modules/tax/repository/index.ts +0 -6
  153. package/src/modules/tax/service.ts +0 -53
  154. package/src/modules/webhooks/hook.ts +0 -34
  155. package/src/modules/webhooks/repository/index.ts +0 -278
  156. package/src/modules/webhooks/schema.ts +0 -56
  157. package/src/modules/webhooks/service.ts +0 -117
  158. package/src/modules/webhooks/signing.ts +0 -6
  159. package/src/modules/webhooks/ssrf-guard.ts +0 -71
  160. package/src/modules/webhooks/tasks.ts +0 -52
  161. package/src/modules/webhooks/worker.ts +0 -134
  162. package/src/runtime/commerce.ts +0 -145
  163. package/src/runtime/kernel.ts +0 -419
  164. package/src/runtime/logger.ts +0 -36
  165. package/src/runtime/server.ts +0 -349
  166. package/src/runtime/shutdown.ts +0 -43
  167. package/src/test-utils/create-pglite-adapter.ts +0 -129
  168. package/src/test-utils/create-plugin-test-app.ts +0 -128
  169. package/src/test-utils/create-repository-test-harness.ts +0 -16
  170. package/src/test-utils/create-test-config.ts +0 -190
  171. package/src/test-utils/create-test-kernel.ts +0 -7
  172. package/src/test-utils/create-test-plugin-context.ts +0 -75
  173. package/src/test-utils/rest-api-test-utils.ts +0 -265
  174. package/src/test-utils/test-actors.ts +0 -62
  175. package/src/test-utils/typed-hooks.ts +0 -54
  176. package/src/types/commerce-types.ts +0 -34
  177. package/src/utils/id.ts +0 -3
  178. package/src/utils/logger.ts +0 -18
  179. package/src/utils/pagination.ts +0 -22
@@ -1,334 +0,0 @@
1
- /**
2
- * Type-safe route builder for UnifiedCommerce plugins.
3
- *
4
- * Wraps @hono/zod-openapi's createRoute() with sensible defaults:
5
- * - /api prefix prepended automatically
6
- * - Error responses (400, 401, 403, 404, 422) injected
7
- * - Path params auto-detected from {id} patterns
8
- * - POST defaults to 201, others to 200
9
- * - .auth() enforces login, .permission() enforces specific scope
10
- * - Handler receives { input, params, query, actor, services, db, logger }
11
- * - Return value auto-wrapped in { data: result }
12
- * - Errors auto-caught and mapped to { error: { code, message } }
13
- *
14
- * @example
15
- * ```typescript
16
- * import { router, z } from "@unifiedcommerce/core";
17
- *
18
- * const vendors = router("Vendors", "/marketplace/vendors");
19
- *
20
- * vendors.get("/").summary("List").auth().handler(async ({ actor, services }) => {
21
- * return services.vendor.list();
22
- * });
23
- *
24
- * vendors.post("/").summary("Create").permission("marketplace:admin").input(Schema)
25
- * .handler(async ({ input, services }) => services.vendor.create(input));
26
- * ```
27
- */
28
-
29
- import { createRoute, z } from "@hono/zod-openapi";
30
- import { ErrorSchema } from "./schemas/shared.js";
31
- import { mapErrorToResponse, mapErrorToStatus } from "./utils.js";
32
- import type { PluginRouteRegistration } from "../../kernel/plugin/manifest.js";
33
- import { createScopedDb } from "../../kernel/database/scoped-db.js";
34
- import { resolveOrgId } from "../../auth/org.js";
35
-
36
- // ─── Shared OpenAPI Error Responses ──────────────────────────────────────────
37
-
38
- const errorResponses = {
39
- 400: { content: { "application/json": { schema: ErrorSchema } }, description: "Bad request." },
40
- 401: { content: { "application/json": { schema: ErrorSchema } }, description: "Unauthorized." },
41
- 403: { content: { "application/json": { schema: ErrorSchema } }, description: "Forbidden." },
42
- 404: { content: { "application/json": { schema: ErrorSchema } }, description: "Not found." },
43
- 422: { content: { "application/json": { schema: ErrorSchema } }, description: "Validation error." },
44
- } as const;
45
-
46
- function wrapJson(schema: z.ZodType) {
47
- return { content: { "application/json": { schema } }, description: "Success" };
48
- }
49
-
50
- function extractPathParams(path: string): string[] {
51
- return [...path.matchAll(/\{(\w+)\}/g)].map((m) => m[1]!);
52
- }
53
-
54
- // ─── Handler Context ─────────────────────────────────────────────────────────
55
-
56
- export interface RouteHandlerContext {
57
- /** Validated request body (from .input()). Undefined if no .input() was called. */
58
- input: unknown;
59
- /** Validated query parameters (from .query()). Empty object if no .query(). */
60
- query: Record<string, unknown>;
61
- /** Path parameters, auto-extracted from {id} segments. */
62
- params: Record<string, string>;
63
- /** Authenticated actor. Guaranteed non-null if .auth() or .permission() was called. */
64
- actor: { userId: string; role: string; permissions: string[]; vendorId?: string | null; [key: string]: unknown } | null;
65
- /** Resolved organization ID. Derived from actor.organizationId, falls back to DEFAULT_ORG_ID. */
66
- orgId: string;
67
- /** Kernel services (orders, cart, inventory, etc.) */
68
- services: Record<string, unknown>;
69
- /** Drizzle database instance */
70
- db: unknown;
71
- /** Per-request structured logger */
72
- logger: unknown;
73
- /** Request ID */
74
- requestId: string;
75
- /** Raw Hono context (escape hatch) */
76
- raw: unknown;
77
- }
78
-
79
- // ─── Route Chain ─────────────────────────────────────────────────────────────
80
-
81
- class RouteChain {
82
- private _summary = "";
83
- private _description = "";
84
- private _input: z.ZodType | undefined;
85
- private _query: z.ZodType | undefined;
86
- private _params: z.ZodType | undefined;
87
- private _requireAuth = false;
88
- private _requiredPermission: string | undefined;
89
-
90
- constructor(
91
- private method: string,
92
- private fullPath: string,
93
- private tag: string,
94
- private routesList: PluginRouteRegistration[],
95
- private pluginCtx?: { services: Record<string, unknown>; db: unknown },
96
- ) {
97
- const paramNames = extractPathParams(fullPath);
98
- if (paramNames.length > 0) {
99
- this._params = z.object(
100
- Object.fromEntries(paramNames.map((n) => [n, z.uuid()])),
101
- );
102
- }
103
- }
104
-
105
- /** Set the OpenAPI summary for this route. */
106
- summary(text: string) { this._summary = text; return this; }
107
-
108
- /** Set the OpenAPI description for this route. */
109
- description(text: string) { this._description = text; return this; }
110
-
111
- /** Set the request body schema. Only for POST/PATCH/PUT. */
112
- input(schema: z.ZodType) { this._input = schema; return this; }
113
-
114
- /** Set the query parameter schema. */
115
- query(schema: z.ZodType) { this._query = schema; return this; }
116
-
117
- /** Override the auto-detected path parameter schema. */
118
- params(schema: z.ZodType) { this._params = schema; return this; }
119
-
120
- /**
121
- * Require authentication. The handler's `actor` is guaranteed non-null.
122
- * Returns 401 if the request has no authenticated actor.
123
- */
124
- auth() { this._requireAuth = true; return this; }
125
-
126
- /**
127
- * Require a specific permission scope. Implies .auth().
128
- * Returns 401 if not authenticated, 403 if the actor lacks the permission.
129
- *
130
- * Permission scopes should be declared in the plugin's `permissions` manifest.
131
- * The wildcard `*:*` always passes.
132
- *
133
- * @example
134
- * ```typescript
135
- * vendors.post("/").permission("marketplace:admin").handler(...)
136
- * wishlist.post("/").permission("wishlist:write").handler(...)
137
- * ```
138
- */
139
- permission(scope: string) {
140
- this._requireAuth = true;
141
- this._requiredPermission = scope;
142
- return this;
143
- }
144
-
145
- /**
146
- * Register the handler for this route.
147
- *
148
- * The handler receives a context object with typed input, params, query,
149
- * actor, services, and db. Return data is auto-wrapped in { data: result }.
150
- * Thrown errors are auto-caught and mapped to { error: { code, message } }.
151
- */
152
- handler(fn: (ctx: RouteHandlerContext) => Promise<unknown>): void {
153
- const request: Record<string, unknown> = {};
154
- if (this._input) request.body = { ...wrapJson(this._input), required: true };
155
- if (this._query) request.query = this._query;
156
- if (this._params) request.params = this._params;
157
-
158
- const status = this.method === "post" ? 201 : 200;
159
-
160
- const routeConfig = createRoute({
161
- method: this.method as "get",
162
- path: this.fullPath,
163
- tags: [this.tag],
164
- summary: this._summary,
165
- ...(this._description ? { description: this._description } : {}),
166
- ...(Object.keys(request).length > 0 ? { request } : {}),
167
- responses: {
168
- [status]: wrapJson(z.object({ data: z.any() })),
169
- ...errorResponses,
170
- },
171
- });
172
-
173
- const inputSchema = this._input;
174
- const querySchema = this._query;
175
- const pathParams = extractPathParams(this.fullPath);
176
- const successStatus = status;
177
- const requireAuth = this._requireAuth;
178
- const requiredPermission = this._requiredPermission;
179
- const pluginCtx = this.pluginCtx;
180
-
181
- const honoHandler = async (c: unknown) => {
182
- const ctx = c as {
183
- req: {
184
- valid: (target: string) => unknown;
185
- param: (name: string) => string;
186
- };
187
- get: (key: string) => unknown;
188
- json: (data: unknown, status?: number) => unknown;
189
- };
190
-
191
- try {
192
- // ─── Auth + Permission Check ─────────────────────────────────
193
- // .permission() implies .auth() — both are enforced here.
194
- // Order: auth first (401), then permission (403).
195
- const actor = ctx.get("actor") as RouteHandlerContext["actor"];
196
-
197
- if ((requireAuth || requiredPermission) && !actor) {
198
- return ctx.json({ error: { code: "UNAUTHORIZED", message: "Authentication required." } }, 401);
199
- }
200
-
201
- if (requiredPermission) {
202
- const perms = actor?.permissions ?? [];
203
- if (!perms.includes(requiredPermission) && !perms.includes("*:*")) {
204
- return ctx.json({
205
- error: { code: "FORBIDDEN", message: `Permission '${requiredPermission}' is required.` },
206
- }, 403);
207
- }
208
- }
209
-
210
- // ─── Extract context ─────────────────────────────────────────
211
- const params: Record<string, string> = {};
212
- for (const name of pathParams) {
213
- params[name] = ctx.req.param(name);
214
- }
215
-
216
- // Use plugin context if provided (plugin routes), fallback to kernel on Hono context (core routes)
217
- const kernel = pluginCtx ?? (ctx.get("kernel") as {
218
- services?: Record<string, unknown>;
219
- db?: unknown;
220
- } | undefined);
221
-
222
- // Scope the database to the actor's organization.
223
- // Plugin route handlers receive a db that auto-filters queries
224
- // and auto-stamps inserts with the actor's organizationId.
225
- const rawDb = kernel?.db;
226
- const orgId = resolveOrgId(actor);
227
- const scopedDb = rawDb ? createScopedDb(rawDb as Record<string, unknown>, orgId) : rawDb;
228
-
229
- const handlerCtx: RouteHandlerContext = {
230
- input: inputSchema ? ctx.req.valid("json") : undefined,
231
- query: querySchema ? (ctx.req.valid("query") as Record<string, unknown>) : {},
232
- params,
233
- actor,
234
- orgId,
235
- services: kernel?.services ?? {},
236
- db: scopedDb,
237
- logger: ctx.get("logger"),
238
- requestId: (ctx.get("requestId") as string) ?? "",
239
- raw: c,
240
- };
241
-
242
- const result = await fn(handlerCtx);
243
-
244
- if (result === undefined || result === null) {
245
- return ctx.json({ data: null }, successStatus);
246
- }
247
- return ctx.json({ data: result }, successStatus);
248
- } catch (error: unknown) {
249
- try {
250
- return ctx.json(
251
- mapErrorToResponse(error),
252
- mapErrorToStatus(error) as number,
253
- );
254
- } catch {
255
- // Fallback if mapErrorToResponse/mapErrorToStatus themselves throw
256
- return ctx.json(
257
- { error: { code: "INTERNAL_ERROR", message: "An unexpected error occurred." } },
258
- 500,
259
- );
260
- }
261
- }
262
- };
263
-
264
- this.routesList.push({ openapi: routeConfig, handler: honoHandler as (...args: unknown[]) => unknown });
265
- }
266
- }
267
-
268
- // ─── Router ──────────────────────────────────────────────────────────────────
269
-
270
- class RouterImpl {
271
- private _routes: PluginRouteRegistration[] = [];
272
- private _prefix: string;
273
- private _pluginCtx: { services: Record<string, unknown>; db: unknown } | undefined;
274
-
275
- constructor(
276
- private tag: string,
277
- prefix: string,
278
- pluginCtx?: { services?: Record<string, unknown>; database?: { db: unknown } },
279
- ) {
280
- // Normalize: ensure /api prefix, strip trailing slash
281
- let p = prefix.startsWith("/api") ? prefix : `/api${prefix}`;
282
- p = p.replace(/\/+$/, ""); // remove trailing slashes
283
- this._prefix = p;
284
-
285
- // If plugin context is provided, wire services + db for handler access
286
- if (pluginCtx) {
287
- this._pluginCtx = {
288
- services: pluginCtx.services ?? {},
289
- db: pluginCtx.database?.db,
290
- };
291
- }
292
- }
293
-
294
- /** @internal — used by RouteChain to wire plugin context into handlers */
295
- get pluginContext() { return this._pluginCtx; }
296
-
297
- private resolvePath(path: string): string {
298
- // "/" means "the resource root" → just the prefix, no trailing slash
299
- if (path === "/") return this._prefix;
300
- return (this._prefix + path).replace(/\/\/+/g, "/");
301
- }
302
-
303
- get(path: string) { return new RouteChain("get", this.resolvePath(path), this.tag, this._routes, this._pluginCtx); }
304
- post(path: string) { return new RouteChain("post", this.resolvePath(path), this.tag, this._routes, this._pluginCtx); }
305
- patch(path: string) { return new RouteChain("patch", this.resolvePath(path), this.tag, this._routes, this._pluginCtx); }
306
- delete(path: string) { return new RouteChain("delete", this.resolvePath(path), this.tag, this._routes, this._pluginCtx); }
307
- put(path: string) { return new RouteChain("put", this.resolvePath(path), this.tag, this._routes, this._pluginCtx); }
308
-
309
- /** Returns all registered routes as PluginRouteRegistration[] */
310
- routes(): PluginRouteRegistration[] { return this._routes; }
311
- }
312
-
313
- /**
314
- * Create a typed route group for plugin routes.
315
- *
316
- * @param tag — OpenAPI tag for Swagger UI grouping
317
- * @param prefix — Path prefix without /api (e.g., "/marketplace/vendors"). /api is prepended automatically.
318
- * @param ctx — Optional PluginContext. When provided, handler receives { services, db } from the plugin.
319
- *
320
- * @example
321
- * ```typescript
322
- * // In plugin routes callback:
323
- * routes: (ctx) => {
324
- * const r = router("Vendors", "/marketplace/vendors", ctx);
325
- * r.get("/").summary("List").handler(async ({ services }) => {
326
- * return (services as VendorServices).vendor.list();
327
- * });
328
- * return r.routes();
329
- * }
330
- * ```
331
- */
332
- export function router(tag: string, prefix: string, ctx?: { services?: Record<string, unknown>; database?: { db: unknown } }): RouterImpl {
333
- return new RouterImpl(tag, prefix, ctx);
334
- }
@@ -1,58 +0,0 @@
1
- import { OpenAPIHono } from "@hono/zod-openapi";
2
- import { eq, and, desc } from "drizzle-orm";
3
- import type { PgDatabase, PgQueryResultHKT } from "drizzle-orm/pg-core";
4
- import type { Kernel } from "../../../runtime/kernel.js";
5
- import { commerceJobs } from "../../../kernel/jobs/schema.js";
6
- import { listFailedJobsRoute, retryJobRoute } from "../schemas/admin-jobs.js";
7
- import { type AppEnv, requirePerm } from "../utils.js";
8
- import { resolveOrgId } from "../../../auth/org.js";
9
-
10
- type Db = PgDatabase<PgQueryResultHKT, Record<string, unknown>>;
11
-
12
- export function adminJobRoutes(kernel: Kernel) {
13
- const router = new OpenAPIHono<AppEnv>();
14
- const db = kernel.database.db as Db;
15
-
16
- router.use("/jobs/failed", requirePerm("jobs:admin"));
17
-
18
- router.openapi(listFailedJobsRoute, async (c) => {
19
- const actor = c.get("actor");
20
- const orgId = resolveOrgId(actor);
21
- const limit = Math.min(Number(c.req.query("limit") ?? "50"), 100);
22
- const conditions = [eq(commerceJobs.status, "failed")];
23
- // Scope to org unless wildcard admin
24
- if (!actor?.permissions?.includes("*:*")) {
25
- conditions.push(eq(commerceJobs.organizationId, orgId));
26
- }
27
- const failed = await db.select()
28
- .from(commerceJobs)
29
- .where(and(...conditions))
30
- .orderBy(desc(commerceJobs.completedAt))
31
- .limit(limit);
32
- return c.json({ data: failed });
33
- });
34
-
35
- router.use("/jobs/:id/retry", requirePerm("jobs:admin"));
36
-
37
- // @ts-expect-error -- openapi handler union return type
38
- router.openapi(retryJobRoute, async (c) => {
39
- const actor = c.get("actor");
40
- const orgId = resolveOrgId(actor);
41
- const id = c.req.param("id");
42
- const conditions = [eq(commerceJobs.id, id)];
43
- // Scope to org unless wildcard admin
44
- if (!actor?.permissions?.includes("*:*")) {
45
- conditions.push(eq(commerceJobs.organizationId, orgId));
46
- }
47
- const result = await db.update(commerceJobs)
48
- .set({ status: "pending", attempts: 0, error: null, waitUntil: null, updatedAt: new Date() })
49
- .where(and(...conditions))
50
- .returning();
51
- if (result.length === 0) {
52
- return c.json({ error: { code: "NOT_FOUND", message: "Job not found." } }, 404);
53
- }
54
- return c.json({ data: { retried: true } });
55
- });
56
-
57
- return router;
58
- }
@@ -1,50 +0,0 @@
1
- import { OpenAPIHono } from "@hono/zod-openapi";
2
- import type { Kernel } from "../../../runtime/kernel.js";
3
- import { listAuditRoute, listEntityAuditRoute } from "../schemas/audit.js";
4
- import { type AppEnv, requirePerm } from "../utils.js";
5
- import { resolveOrgId } from "../../../auth/org.js";
6
-
7
- export function auditRoutes(kernel: Kernel) {
8
- const router = new OpenAPIHono<AppEnv>();
9
-
10
- /**
11
- * GET /api/audit
12
- * List audit entries with optional filters.
13
- */
14
- router.use("/", requirePerm("audit:read"));
15
-
16
- router.openapi(listAuditRoute, async (c) => {
17
- const actor = c.get("actor");
18
- const orgId = resolveOrgId(actor);
19
- const entries = await kernel.services.audit.list({
20
- organizationId: orgId,
21
- entityType: c.req.query("entityType"),
22
- entityId: c.req.query("entityId"),
23
- event: c.req.query("event"),
24
- actorId: c.req.query("actorId"),
25
- from: c.req.query("from") ? new Date(c.req.query("from")!) : undefined,
26
- to: c.req.query("to") ? new Date(c.req.query("to")!) : undefined,
27
- limit: c.req.query("limit") ? Number(c.req.query("limit")) : 50,
28
- });
29
- return c.json({ data: entries });
30
- });
31
-
32
- /**
33
- * GET /api/audit/:entityType/:entityId
34
- * List audit history for a specific entity.
35
- */
36
- router.use("/:entityType/:entityId", requirePerm("audit:read"));
37
-
38
- router.openapi(listEntityAuditRoute, async (c) => {
39
- const actor = c.get("actor");
40
- const orgId = resolveOrgId(actor);
41
- const entries = await kernel.services.audit.listForEntity({
42
- organizationId: orgId,
43
- entityType: c.req.param("entityType"),
44
- entityId: c.req.param("entityId"),
45
- });
46
- return c.json({ data: entries });
47
- });
48
-
49
- return router;
50
- }
@@ -1,89 +0,0 @@
1
- import { OpenAPIHono } from "@hono/zod-openapi";
2
- import type { Kernel } from "../../../runtime/kernel.js";
3
- import type { CreateCartInput, AddCartItemInput } from "../../../modules/cart/schemas.js";
4
- import { createCartRoute, addCartItemRoute, updateCartItemQuantityRoute, getCartRoute, removeCartItemRoute } from "../schemas/carts.js";
5
- import { type AppEnv, mapErrorToResponse, mapErrorToStatus } from "../utils.js";
6
-
7
- export function cartRoutes(kernel: Kernel) {
8
- const router = new OpenAPIHono<AppEnv>();
9
-
10
- // @ts-expect-error -- openapi() enforces strict response typing but our handler
11
- // returns union responses (201 | 400 | 422). The route definition documents the
12
- // contract; the handler returns dynamic status.
13
- router.openapi(createCartRoute, async (c) => {
14
- const actor = c.get("actor");
15
- const result = await kernel.services.cart.create(c.req.valid("json") as CreateCartInput, actor);
16
- if (!result.ok)
17
- return c.json(
18
- mapErrorToResponse(result.error),
19
- mapErrorToStatus(result.error),
20
- );
21
- return c.json({ data: result.value }, 201);
22
- });
23
-
24
- // @ts-expect-error -- openapi handler union return type
25
- router.openapi(getCartRoute, async (c) => {
26
- const actor = c.get("actor");
27
- const result = await kernel.services.cart.getById(c.req.param("id"), actor);
28
- if (!result.ok)
29
- return c.json(
30
- mapErrorToResponse(result.error),
31
- mapErrorToStatus(result.error),
32
- );
33
- return c.json({ data: result.value });
34
- });
35
-
36
- // @ts-expect-error -- openapi() enforces strict response typing but our handler
37
- // returns union responses (201 | 400 | 422). The route definition documents the
38
- // contract; the handler returns dynamic status.
39
- router.openapi(addCartItemRoute, async (c) => {
40
- const result = await kernel.services.cart.addItem(
41
- { ...c.req.valid("json"), cartId: c.req.param("id") } as AddCartItemInput,
42
- c.get("actor"),
43
- );
44
- if (!result.ok)
45
- return c.json(
46
- mapErrorToResponse(result.error),
47
- mapErrorToStatus(result.error),
48
- );
49
- return c.json({ data: result.value }, 201);
50
- });
51
-
52
- // @ts-expect-error -- openapi() enforces strict response typing but our handler
53
- // returns union responses (200 | 400 | 422). The route definition documents the
54
- // contract; the handler returns dynamic status.
55
- router.openapi(updateCartItemQuantityRoute, async (c) => {
56
- const body = c.req.valid("json");
57
- const result = await kernel.services.cart.updateQuantity(
58
- {
59
- cartId: c.req.param("id"),
60
- itemId: c.req.param("itemId"),
61
- quantity: body.quantity,
62
- },
63
- c.get("actor"),
64
- );
65
- if (!result.ok)
66
- return c.json(
67
- mapErrorToResponse(result.error),
68
- mapErrorToStatus(result.error),
69
- );
70
- return c.json({ data: result.value });
71
- });
72
-
73
- // @ts-expect-error -- openapi handler union return type
74
- router.openapi(removeCartItemRoute, async (c) => {
75
- const result = await kernel.services.cart.removeItem(
76
- c.req.param("id"),
77
- c.req.param("itemId"),
78
- c.get("actor"),
79
- );
80
- if (!result.ok)
81
- return c.json(
82
- mapErrorToResponse(result.error),
83
- mapErrorToStatus(result.error),
84
- );
85
- return c.json({ data: { deleted: true } });
86
- });
87
-
88
- return router;
89
- }