Package not found. Please check the package name and try again.

@unifiedcommerce/core 0.3.3 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/auth/setup.d.ts +25 -66
  2. package/dist/auth/setup.d.ts.map +1 -1
  3. package/dist/auth/setup.js +7 -20
  4. package/dist/interfaces/mcp/tools/webhooks.d.ts +1 -1
  5. package/dist/interfaces/rest/index.d.ts.map +1 -1
  6. package/dist/interfaces/rest/index.js +14 -3
  7. package/dist/interfaces/rest/routes/customers.d.ts +5 -0
  8. package/dist/interfaces/rest/routes/customers.d.ts.map +1 -0
  9. package/dist/interfaces/rest/routes/customers.js +74 -0
  10. package/dist/interfaces/rest/routes/inventory.d.ts.map +1 -1
  11. package/dist/interfaces/rest/routes/inventory.js +14 -1
  12. package/dist/interfaces/rest/schemas/customers.d.ts +422 -0
  13. package/dist/interfaces/rest/schemas/customers.d.ts.map +1 -0
  14. package/dist/interfaces/rest/schemas/customers.js +150 -0
  15. package/dist/interfaces/rest/schemas/inventory.d.ts +92 -0
  16. package/dist/interfaces/rest/schemas/inventory.d.ts.map +1 -1
  17. package/dist/interfaces/rest/schemas/inventory.js +20 -0
  18. package/dist/modules/customers/service.d.ts +2 -0
  19. package/dist/modules/customers/service.d.ts.map +1 -1
  20. package/dist/modules/customers/service.js +15 -0
  21. package/dist/modules/inventory/service.d.ts +4 -0
  22. package/dist/modules/inventory/service.d.ts.map +1 -1
  23. package/dist/modules/inventory/service.js +12 -0
  24. package/dist/runtime/server.d.ts.map +1 -1
  25. package/dist/runtime/server.js +29 -0
  26. package/package.json +2 -2
  27. package/src/auth/setup.ts +30 -79
  28. package/src/interfaces/rest/index.ts +17 -3
  29. package/src/interfaces/rest/routes/customers.ts +94 -0
  30. package/src/interfaces/rest/routes/inventory.ts +17 -0
  31. package/src/interfaces/rest/schemas/customers.ts +155 -0
  32. package/src/interfaces/rest/schemas/inventory.ts +21 -0
  33. package/src/modules/customers/service.ts +25 -0
  34. package/src/modules/inventory/service.ts +17 -0
  35. package/src/runtime/server.ts +30 -0
@@ -9,6 +9,26 @@ export const CreateWarehouseBodySchema = z.object({
9
9
  address: z.record(z.string(), z.unknown()).optional(),
10
10
  }).openapi("CreateWarehouseRequest");
11
11
  // ─── Route Definitions ──────────────────────────────────────────────────────
12
+ export const listInventoryLevelsRoute = createRoute({
13
+ method: "get",
14
+ path: "/levels",
15
+ tags: ["Inventory"],
16
+ summary: "List inventory levels",
17
+ description: "Lists all inventory levels, optionally filtered by warehouse or entity.",
18
+ request: {
19
+ query: z.object({
20
+ warehouseId: z.string().uuid().optional().openapi({ example: "uuid" }),
21
+ entityId: z.string().uuid().optional().openapi({ example: "uuid" }),
22
+ }),
23
+ },
24
+ responses: {
25
+ 200: {
26
+ content: { "application/json": { schema: z.object({ data: z.array(z.record(z.string(), z.unknown())) }) } },
27
+ description: "Inventory levels",
28
+ },
29
+ ...errorResponses,
30
+ },
31
+ });
12
32
  export const inventoryCheckRoute = createRoute({
13
33
  method: "get",
14
34
  path: "/check",
@@ -10,8 +10,10 @@ export declare class CustomerService {
10
10
  private readonly repo;
11
11
  constructor(deps: CustomerServiceDeps);
12
12
  private getOrCreateByUserId;
13
+ list(actor?: Actor | null, ctx?: TxContext): Promise<Result<Customer[]>>;
13
14
  getById(id: string, actor?: Actor | null, ctx?: TxContext): Promise<Result<Customer>>;
14
15
  getByUserId(userId: string, actor?: Actor | null, ctx?: TxContext): Promise<Result<Customer>>;
16
+ update(id: string, updates: Partial<Omit<Customer, "id" | "userId" | "createdAt" | "updatedAt">>, actor?: Actor | null, ctx?: TxContext): Promise<Result<Customer>>;
15
17
  updateByUserId(userId: string, updates: Partial<Omit<Customer, "id" | "userId" | "createdAt" | "updatedAt">>, actor?: Actor | null, ctx?: TxContext): Promise<Result<Customer>>;
16
18
  getAddresses(userId: string, actor?: Actor | null, ctx?: TxContext): Promise<Result<CustomerAddress[]>>;
17
19
  addAddress(userId: string, input: Omit<CustomerAddressInsert, "id" | "customerId">, actor?: Actor | null, ctx?: TxContext): Promise<Result<CustomerAddress>>;
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/modules/customers/service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAEjD,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qCAAqC,CAAC;AACrE,OAAO,KAAK,EACV,mBAAmB,EACnB,QAAQ,EACR,eAAe,EACf,qBAAqB,EACtB,MAAM,uBAAuB,CAAC;AAE/B,UAAU,mBAAmB;IAC3B,UAAU,EAAE,mBAAmB,CAAC;CACjC;AAED,qBAAa,eAAe;IAGd,OAAO,CAAC,IAAI;IAFxB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAsB;gBAEvB,IAAI,EAAE,mBAAmB;YAI/B,mBAAmB;IAsB3B,OAAO,CACX,EAAE,EAAE,MAAM,EACV,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,EACpB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAOtB,WAAW,CACf,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,EACpB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAMtB,cAAc,CAClB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,CACd,IAAI,CAAC,QAAQ,EAAE,IAAI,GAAG,QAAQ,GAAG,WAAW,GAAG,WAAW,CAAC,CAC5D,EACD,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,EACpB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAYtB,YAAY,CAChB,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,EACpB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC,CAAC;IAU/B,UAAU,CACd,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,IAAI,CAAC,qBAAqB,EAAE,IAAI,GAAG,YAAY,CAAC,EACvD,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,EACpB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;IA4B7B,aAAa,CACjB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,EACpB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAiBlB,oBAAoB,CACxB,SAAS,EAAE,MAAM,EACjB,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,EACpB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CAYhD"}
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/modules/customers/service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAEjD,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qCAAqC,CAAC;AACrE,OAAO,KAAK,EACV,mBAAmB,EACnB,QAAQ,EACR,eAAe,EACf,qBAAqB,EACtB,MAAM,uBAAuB,CAAC;AAE/B,UAAU,mBAAmB;IAC3B,UAAU,EAAE,mBAAmB,CAAC;CACjC;AAED,qBAAa,eAAe;IAGd,OAAO,CAAC,IAAI;IAFxB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAsB;gBAEvB,IAAI,EAAE,mBAAmB;YAI/B,mBAAmB;IAsB3B,IAAI,CACR,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,EACpB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;IAMxB,OAAO,CACX,EAAE,EAAE,MAAM,EACV,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,EACpB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAOtB,WAAW,CACf,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,EACpB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAMtB,MAAM,CACV,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,OAAO,CACd,IAAI,CAAC,QAAQ,EAAE,IAAI,GAAG,QAAQ,GAAG,WAAW,GAAG,WAAW,CAAC,CAC5D,EACD,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,EACpB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAStB,cAAc,CAClB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,CACd,IAAI,CAAC,QAAQ,EAAE,IAAI,GAAG,QAAQ,GAAG,WAAW,GAAG,WAAW,CAAC,CAC5D,EACD,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,EACpB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAYtB,YAAY,CAChB,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,EACpB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC,CAAC;IAU/B,UAAU,CACd,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,IAAI,CAAC,qBAAqB,EAAE,IAAI,GAAG,YAAY,CAAC,EACvD,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,EACpB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;IA4B7B,aAAa,CACjB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,EACpB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAiBlB,oBAAoB,CACxB,SAAS,EAAE,MAAM,EACjB,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,EACpB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CAYhD"}
@@ -20,6 +20,11 @@ export class CustomerService {
20
20
  }, ctx);
21
21
  return customer;
22
22
  }
23
+ async list(actor, ctx) {
24
+ const orgId = resolveOrgId(actor ?? ctx?.actor ?? null);
25
+ const customers = await this.repo.findAll(orgId, ctx);
26
+ return Ok(customers);
27
+ }
23
28
  async getById(id, actor, ctx) {
24
29
  const orgId = resolveOrgId(actor ?? ctx?.actor ?? null);
25
30
  const customer = await this.repo.findById(orgId, id, ctx);
@@ -32,6 +37,16 @@ export class CustomerService {
32
37
  const customer = await this.getOrCreateByUserId(orgId, userId, ctx);
33
38
  return Ok(customer);
34
39
  }
40
+ async update(id, updates, actor, ctx) {
41
+ const orgId = resolveOrgId(actor ?? ctx?.actor ?? null);
42
+ const existing = await this.repo.findById(orgId, id, ctx);
43
+ if (!existing)
44
+ return Err(new CommerceNotFoundError("Customer not found."));
45
+ const updated = await this.repo.update(id, updates, ctx);
46
+ if (!updated)
47
+ return Err(new CommerceNotFoundError("Customer not found."));
48
+ return Ok(updated);
49
+ }
35
50
  async updateByUserId(userId, updates, actor, ctx) {
36
51
  const orgId = resolveOrgId(actor ?? ctx?.actor ?? null);
37
52
  const customer = await this.getOrCreateByUserId(orgId, userId, ctx);
@@ -22,6 +22,10 @@ export declare class InventoryService {
22
22
  createWarehouse(input: Partial<Warehouse>, actor?: Actor | null, ctx?: TxContext): Promise<Result<Warehouse>>;
23
23
  listWarehouses(actor?: Actor | null, ctx?: TxContext): Promise<Result<Warehouse[]>>;
24
24
  getAvailable(entityId: string, variantId?: string, ctx?: TxContext): Promise<Result<number>>;
25
+ listLevels(params?: {
26
+ warehouseId?: string;
27
+ entityId?: string;
28
+ }, actor?: Actor | null, ctx?: TxContext): Promise<Result<InventoryLevel[]>>;
25
29
  checkMultiple(entityIds: string[], ctx?: TxContext): Promise<Result<Record<string, number>>>;
26
30
  getLevelsByEntityId(entityId: string, ctx?: TxContext): Promise<Result<InventoryLevel[]>>;
27
31
  reserve(input: InventoryReserveInput, actor?: Actor | null, ctx?: TxContext): Promise<Result<void>>;
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/modules/inventory/service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/modules/inventory/service.ts"],"names":[],"mappings":"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"}
@@ -56,6 +56,18 @@ export class InventoryService {
56
56
  const available = await this.repo.getAvailableQuantity(entityId, variantId, ctx);
57
57
  return Ok(available);
58
58
  }
59
+ async listLevels(params, actor, ctx) {
60
+ if (params?.entityId) {
61
+ const levels = await this.repo.findLevelsByEntityId(params.entityId, ctx);
62
+ return Ok(levels);
63
+ }
64
+ if (params?.warehouseId) {
65
+ const levels = await this.repo.findLevelsByWarehouseId(params.warehouseId, ctx);
66
+ return Ok(levels);
67
+ }
68
+ const levels = await this.repo.findAllLevels(ctx);
69
+ return Ok(levels);
70
+ }
59
71
  async checkMultiple(entityIds, ctx) {
60
72
  const data = await this.repo.getAvailableQuantities(entityIds, ctx);
61
73
  return Ok(data);
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/runtime/server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAKhD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAOzD,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AAExD,OAAO,EAAkB,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEtE,KAAK,SAAS,GAAG;IACf,SAAS,EAAE;QACT,IAAI,EAAE,YAAY,CAAC;QACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH,CAAC;AAEF;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,cAAc;;;;;GA8TxD"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/runtime/server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAKhD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAOzD,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AAExD,OAAO,EAAkB,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEtE,KAAK,SAAS,GAAG;IACf,SAAS,EAAE;QACT,IAAI,EAAE,YAAY,CAAC;QACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH,CAAC;AAEF;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,cAAc;;;;;GA4VxD"}
@@ -207,6 +207,35 @@ export async function createServer(config) {
207
207
  version: config.version ?? "0.0.1",
208
208
  description: "Headless commerce engine REST API. Includes core and plugin endpoints.",
209
209
  },
210
+ tags: [
211
+ // ── Storefront ──
212
+ { name: "Catalog", description: "Products, categories, brands, variants, and option types" },
213
+ { name: "Search", description: "Full-text search and typeahead suggestions" },
214
+ { name: "Pricing", description: "Base prices, price modifiers, and customer group pricing" },
215
+ { name: "Carts", description: "Shopping cart lifecycle — create, add items, update quantities" },
216
+ { name: "Checkout", description: "Convert a cart into a paid order" },
217
+ { name: "Promotions", description: "Discount codes, validation, and usage tracking" },
218
+ // ── Admin ──
219
+ { name: "Orders", description: "Order management — list, detail, status transitions, fulfillments" },
220
+ { name: "Customers", description: "Customer profiles, addresses, groups, and order history" },
221
+ { name: "Inventory", description: "Stock levels, warehouse management, adjustments, and reservations" },
222
+ { name: "Media", description: "File uploads, entity attachments, and signed URLs" },
223
+ { name: "Payments", description: "Payment provider webhooks and event processing" },
224
+ // ── Operations ──
225
+ { name: "Webhooks", description: "Outbound webhook endpoint registration and management" },
226
+ { name: "Audit", description: "Immutable audit log — who changed what and when" },
227
+ { name: "Admin Jobs", description: "Background job queue — view failed jobs and retry" },
228
+ ],
229
+ });
230
+ // Serve enriched spec with x-tagGroups vendor extension (Scalar/Redocly sidebar grouping)
231
+ const tagGroups = [
232
+ { name: "Storefront", tags: ["Catalog", "Search", "Pricing", "Carts", "Checkout", "Promotions"] },
233
+ { name: "Admin", tags: ["Orders", "Customers", "Inventory", "Media", "Payments"] },
234
+ { name: "Operations", tags: ["Webhooks", "Audit", "Admin Jobs"] },
235
+ ];
236
+ app.get("/api/doc-ext", (c) => {
237
+ const spec = app.getOpenAPIDocument({ openapi: "3.0.0", info: { title: "UnifiedCommerce API", version: config.version ?? "0.0.1" } });
238
+ return c.json({ ...spec, "x-tagGroups": tagGroups });
210
239
  });
211
240
  }
212
241
  else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unifiedcommerce/core",
3
- "version": "0.3.3",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -44,9 +44,9 @@
44
44
  },
45
45
  "dependencies": {
46
46
  "@better-auth/drizzle-adapter": "^1.3.8",
47
- "@hono/swagger-ui": "^0.6.1",
48
47
  "@hono/zod-openapi": "^1.2.2",
49
48
  "@modelcontextprotocol/sdk": "^1.27.1",
49
+ "@scalar/hono-api-reference": "^0.10.5",
50
50
  "better-auth": "^1.3.8",
51
51
  "drizzle-orm": "^0.45.1",
52
52
  "drizzle-zod": "^0.8.3",
package/src/auth/setup.ts CHANGED
@@ -10,80 +10,39 @@ import * as authSchema from "./auth-schema.js";
10
10
  type BetterAuthDbProvider = "pg" | "mysql" | "sqlite";
11
11
 
12
12
  function resolveAuthDbProvider(provider: string): BetterAuthDbProvider {
13
- if (
14
- provider === "postgres" ||
15
- provider === "postgresql" ||
16
- provider === "pg"
17
- ) {
18
- return "pg";
19
- }
20
- if (provider === "mysql") {
21
- return "mysql";
22
- }
23
- if (provider === "sqlite") {
24
- return "sqlite";
25
- }
26
- throw new Error(
27
- `Unsupported auth database provider "${provider}". Expected one of: postgres, mysql, sqlite.`,
28
- );
13
+ if (provider === "postgres" || provider === "postgresql" || provider === "pg") return "pg";
14
+ if (provider === "mysql") return "mysql";
15
+ if (provider === "sqlite") return "sqlite";
16
+ throw new Error(`Unsupported auth database provider "${provider}".`);
29
17
  }
30
18
 
31
19
  interface AuthEmailPayload {
32
- user: {
33
- email: string;
34
- name: string | null;
35
- };
20
+ user: { email: string; name: string | null };
36
21
  url: string;
37
22
  }
38
23
 
39
- /** Member shape returned by Better Auth's organization plugin */
40
- export interface OrgMember {
41
- id: string;
42
- userId: string;
43
- organizationId: string;
44
- role: string;
45
- createdAt: string;
46
- }
47
-
48
- export interface AuthInstance {
49
- handler(request: Request): Promise<Response>;
50
- api: {
51
- getSession(input: { headers: Headers }): Promise<unknown>;
52
-
53
- // Organization plugin methods
54
- getActiveMemberRole?: (input: { headers: Headers }) => Promise<{ role: string } | null>;
55
- getFullOrganization?: (input: { query: { organizationId: string }; headers: Headers }) => Promise<{
56
- id: string;
57
- name: string;
58
- members: OrgMember[];
59
- } | null>;
60
- listMembers?: (input: { query: { organizationId: string } }) => Promise<OrgMember[]>;
61
-
62
- // API key plugin methods
63
- verifyApiKey?: (input: {
64
- body: { key: string; permissions?: Record<string, string[]> };
65
- }) => Promise<{
66
- valid: boolean;
67
- error: { message: string; code: string } | null;
68
- key: Record<string, unknown> | null;
69
- }>;
70
- createApiKey?: (input: {
71
- body: {
72
- configId?: string;
73
- name?: string;
74
- permissions?: Record<string, string[]>;
75
- userId?: string;
76
- organizationId?: string;
77
- };
78
- headers?: Headers;
79
- }) => Promise<{ key: string; id: string }>;
24
+ /**
25
+ * The auth type is inferred from `typeof betterAuth(...)` with all plugins enabled.
26
+ * This gives us the full API surface without manual interface maintenance.
27
+ *
28
+ * We use a type-level-only reference (never executed) to capture the complete type.
29
+ */
30
+ type FullBetterAuth = ReturnType<typeof betterAuth<{
31
+ plugins: [
32
+ ReturnType<typeof organization>,
33
+ ReturnType<typeof bearer>,
34
+ ReturnType<typeof jwt>,
35
+ ReturnType<typeof twoFactor>,
36
+ ReturnType<typeof apiKey>,
37
+ ReturnType<typeof phoneNumber>,
38
+ ];
39
+ }>>;
80
40
 
81
- /** Allow access to other Better Auth API methods added by plugins */
82
- [key: string]: unknown;
83
- };
84
- options?: Record<string, unknown>;
85
- $context?: Promise<unknown>;
86
- }
41
+ /**
42
+ * AuthInstance — inferred from Better Auth with all plugins.
43
+ * No manual type maintenance needed.
44
+ */
45
+ export type AuthInstance = FullBetterAuth;
87
46
 
88
47
  export function createAuth(
89
48
  db: DatabaseAdapter,
@@ -98,8 +57,6 @@ export function createAuth(
98
57
  | ReturnType<typeof bearer>
99
58
  > = [
100
59
  organization({
101
- // Better Auth's Role includes `authorize` and `statements` fields that
102
- // our RoleDefinition doesn't have. Double-cast is required — upstream type gap.
103
60
  roles: (config.auth?.roles ?? {}) as unknown as Record<string, Role | undefined>,
104
61
  }),
105
62
  bearer(),
@@ -110,7 +67,6 @@ export function createAuth(
110
67
  plugins.push(twoFactor({ issuer: config.storeName ?? "UnifiedCommerce" }));
111
68
  }
112
69
 
113
- // Configure API key plugin — one config per defined scope, or a single default config.
114
70
  const scopes = config.auth?.apiKeyScopes;
115
71
  if (scopes && Object.keys(scopes).length > 0) {
116
72
  const apiKeyConfigs = Object.entries(scopes).map(([scopeId, scope]) => ({
@@ -143,12 +99,8 @@ export function createAuth(
143
99
  }));
144
100
  }
145
101
 
146
- // API key support can be attached via external plugin package in newer better-auth versions.
147
-
148
102
  try {
149
103
  const auth = betterAuth({
150
- // Better Auth's drizzle adapter expects a plain object, not PgDatabase.
151
- // Double-cast required — PgDatabase has no index signature.
152
104
  database: drizzleAdapter(db.db as unknown as Record<string, unknown>, {
153
105
  provider: resolveAuthDbProvider(db.provider),
154
106
  schema: authSchema,
@@ -180,7 +132,7 @@ export function createAuth(
180
132
  updateAge: 60 * 60 * 24,
181
133
  cookieCache: {
182
134
  enabled: true,
183
- maxAge: 60 * 5, // 5 minute cookie cache for performance
135
+ maxAge: 60 * 5,
184
136
  },
185
137
  },
186
138
  advanced: {
@@ -195,10 +147,9 @@ export function createAuth(
195
147
  },
196
148
  },
197
149
  });
198
- // Better Auth's plugin-extended return type is structurally incompatible with
199
- // our simplified AuthInstance interface (upstream generic union vs our narrowed shape).
200
- // Double-cast is the accepted pattern same approach used by PayloadCMS.
201
- // @see https://github.com/better-auth/better-auth/discussions
150
+
151
+ // The runtime return matches FullBetterAuth structurally plugins may differ
152
+ // at runtime (conditional) but the API surface is a superset.
202
153
  return auth as unknown as AuthInstance;
203
154
  } catch (error) {
204
155
  const message =
@@ -1,5 +1,5 @@
1
1
  import { OpenAPIHono } from "@hono/zod-openapi";
2
- import { swaggerUI } from "@hono/swagger-ui";
2
+ import { Scalar } from "@scalar/hono-api-reference";
3
3
  import { sql } from "drizzle-orm";
4
4
  import type { Kernel } from "../../runtime/kernel.js";
5
5
  import type { AppEnv } from "./utils.js";
@@ -16,6 +16,7 @@ import { promotionRoutes } from "./routes/promotions.js";
16
16
  import { searchRoutes } from "./routes/search.js";
17
17
  import { auditRoutes } from "./routes/audit.js";
18
18
  import { adminJobRoutes } from "./routes/admin-jobs.js";
19
+ import { customerRoutes } from "./routes/customers.js";
19
20
 
20
21
  export function createRestRoutes(kernel: Kernel) {
21
22
  const router = new OpenAPIHono<AppEnv>({
@@ -61,13 +62,26 @@ export function createRestRoutes(kernel: Kernel) {
61
62
  router.route("/pricing", pricingRoutes(kernel));
62
63
  router.route("/promotions", promotionRoutes(kernel));
63
64
  router.route("/search", searchRoutes(kernel));
65
+ router.route("/customers", customerRoutes(kernel));
64
66
  router.route("/audit", auditRoutes(kernel));
65
67
  router.route("/admin", adminJobRoutes(kernel));
66
68
 
67
- // Swagger UI — disabled in production unless config.exposeOpenApiSpec is true
69
+ // API Reference (Scalar) — disabled in production unless config.exposeOpenApiSpec is true
68
70
  const exposeSpec = kernel.config.exposeOpenApiSpec ?? (process.env.NODE_ENV !== "production");
69
71
  if (exposeSpec) {
70
- router.get("/reference", swaggerUI({ url: "/api/doc" }));
72
+ router.get(
73
+ "/reference",
74
+ Scalar({
75
+ url: "/api/doc-ext",
76
+ theme: "kepler",
77
+ layout: "modern",
78
+ darkMode: true,
79
+ hideModels: true,
80
+ tagsSorter: "alpha",
81
+ defaultHttpClient: { targetKey: "js", clientKey: "fetch" },
82
+ metaData: { title: "UnifiedCommerce API Reference" },
83
+ }),
84
+ );
71
85
  }
72
86
 
73
87
  return router;
@@ -0,0 +1,94 @@
1
+ import { OpenAPIHono } from "@hono/zod-openapi";
2
+ import type { Kernel } from "../../../runtime/kernel.js";
3
+ import {
4
+ listCustomersRoute,
5
+ getCustomerRoute,
6
+ updateCustomerRoute,
7
+ getCustomerOrdersRoute,
8
+ getCustomerAddressesRoute,
9
+ } from "../schemas/customers.js";
10
+ import { type AppEnv, mapErrorToResponse, mapErrorToStatus, parsePagination } from "../utils.js";
11
+
12
+ export function customerRoutes(kernel: Kernel) {
13
+ const router = new OpenAPIHono<AppEnv>();
14
+
15
+ // @ts-expect-error -- openapi handler union return type
16
+ router.openapi(listCustomersRoute, async (c) => {
17
+ const actor = c.get("actor");
18
+ const { page, limit } = parsePagination(c.req.query());
19
+
20
+ const result = await kernel.services.customers.list(actor);
21
+ if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
22
+
23
+ const all = result.value;
24
+ const total = all.length;
25
+ const start = (page - 1) * limit;
26
+ const paged = all.slice(start, start + limit);
27
+
28
+ return c.json({
29
+ data: paged,
30
+ meta: {
31
+ pagination: { page, limit, total, totalPages: Math.ceil(total / limit) },
32
+ },
33
+ });
34
+ });
35
+
36
+ // @ts-expect-error -- openapi handler union return type
37
+ router.openapi(getCustomerRoute, async (c) => {
38
+ const { id } = c.req.valid("param");
39
+ const actor = c.get("actor");
40
+ const result = await kernel.services.customers.getById(id, actor);
41
+ if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
42
+ return c.json({ data: result.value });
43
+ });
44
+
45
+ // @ts-expect-error -- openapi handler union return type
46
+ router.openapi(updateCustomerRoute, async (c) => {
47
+ const { id } = c.req.valid("param");
48
+ const body = c.req.valid("json");
49
+ const actor = c.get("actor");
50
+ // Strip undefined values — exactOptionalPropertyTypes requires omission, not undefined
51
+ const updates: Record<string, unknown> = {};
52
+ for (const [k, v] of Object.entries(body)) {
53
+ if (v !== undefined) updates[k] = v;
54
+ }
55
+ const result = await kernel.services.customers.update(id, updates, actor);
56
+ if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
57
+ return c.json({ data: result.value });
58
+ });
59
+
60
+ // @ts-expect-error -- openapi handler union return type
61
+ router.openapi(getCustomerOrdersRoute, async (c) => {
62
+ const { id } = c.req.valid("param");
63
+ const actor = c.get("actor");
64
+ const { page, limit } = parsePagination(c.req.query());
65
+ const status = c.req.query("status") || undefined;
66
+
67
+ const result = await kernel.services.orders.listByCustomer(
68
+ id,
69
+ { page, limit, ...(status ? { status } : {}) },
70
+ actor,
71
+ );
72
+ if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
73
+ return c.json({ data: result.value.items, meta: { pagination: result.value.pagination } });
74
+ });
75
+
76
+ // @ts-expect-error -- openapi handler union return type
77
+ router.openapi(getCustomerAddressesRoute, async (c) => {
78
+ const { id } = c.req.valid("param");
79
+ const actor = c.get("actor");
80
+
81
+ // Get customer first, then use their userId for address lookup
82
+ const customerResult = await kernel.services.customers.getById(id, actor);
83
+ if (!customerResult.ok) return c.json(mapErrorToResponse(customerResult.error), mapErrorToStatus(customerResult.error));
84
+
85
+ const addressResult = await kernel.services.customers.getAddresses(
86
+ customerResult.value.userId,
87
+ actor,
88
+ );
89
+ if (!addressResult.ok) return c.json(mapErrorToResponse(addressResult.error), mapErrorToStatus(addressResult.error));
90
+ return c.json({ data: addressResult.value });
91
+ });
92
+
93
+ return router;
94
+ }
@@ -7,12 +7,29 @@ import {
7
7
  createWarehouseRoute,
8
8
  inventoryCheckRoute,
9
9
  listWarehousesRoute,
10
+ listInventoryLevelsRoute,
10
11
  } from "../schemas/inventory.js";
11
12
  import { type AppEnv, mapErrorToResponse, mapErrorToStatus } from "../utils.js";
12
13
 
13
14
  export function inventoryRoutes(kernel: Kernel) {
14
15
  const router = new OpenAPIHono<AppEnv>();
15
16
 
17
+ // @ts-expect-error -- openapi handler union return type
18
+ router.openapi(listInventoryLevelsRoute, async (c) => {
19
+ const actor = c.get("actor");
20
+ const warehouseId = c.req.query("warehouseId");
21
+ const entityId = c.req.query("entityId");
22
+ const result = await kernel.services.inventory.listLevels(
23
+ {
24
+ ...(warehouseId ? { warehouseId } : {}),
25
+ ...(entityId ? { entityId } : {}),
26
+ },
27
+ actor,
28
+ );
29
+ if (!result.ok) return c.json(mapErrorToResponse(result.error), mapErrorToStatus(result.error));
30
+ return c.json({ data: result.value });
31
+ });
32
+
16
33
  // @ts-expect-error -- openapi handler union return type
17
34
  router.openapi(inventoryCheckRoute, async (c) => {
18
35
  const entityIds = (c.req.query("entityIds") ?? "")