@unifiedcommerce/core 0.3.4 → 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 (31) hide show
  1. package/dist/interfaces/mcp/tools/webhooks.d.ts +1 -1
  2. package/dist/interfaces/rest/index.d.ts.map +1 -1
  3. package/dist/interfaces/rest/index.js +14 -3
  4. package/dist/interfaces/rest/routes/customers.d.ts +5 -0
  5. package/dist/interfaces/rest/routes/customers.d.ts.map +1 -0
  6. package/dist/interfaces/rest/routes/customers.js +74 -0
  7. package/dist/interfaces/rest/routes/inventory.d.ts.map +1 -1
  8. package/dist/interfaces/rest/routes/inventory.js +14 -1
  9. package/dist/interfaces/rest/schemas/customers.d.ts +422 -0
  10. package/dist/interfaces/rest/schemas/customers.d.ts.map +1 -0
  11. package/dist/interfaces/rest/schemas/customers.js +150 -0
  12. package/dist/interfaces/rest/schemas/inventory.d.ts +92 -0
  13. package/dist/interfaces/rest/schemas/inventory.d.ts.map +1 -1
  14. package/dist/interfaces/rest/schemas/inventory.js +20 -0
  15. package/dist/modules/customers/service.d.ts +2 -0
  16. package/dist/modules/customers/service.d.ts.map +1 -1
  17. package/dist/modules/customers/service.js +15 -0
  18. package/dist/modules/inventory/service.d.ts +4 -0
  19. package/dist/modules/inventory/service.d.ts.map +1 -1
  20. package/dist/modules/inventory/service.js +12 -0
  21. package/dist/runtime/server.d.ts.map +1 -1
  22. package/dist/runtime/server.js +29 -0
  23. package/package.json +2 -2
  24. package/src/interfaces/rest/index.ts +17 -3
  25. package/src/interfaces/rest/routes/customers.ts +94 -0
  26. package/src/interfaces/rest/routes/inventory.ts +17 -0
  27. package/src/interfaces/rest/schemas/customers.ts +155 -0
  28. package/src/interfaces/rest/schemas/inventory.ts +21 -0
  29. package/src/modules/customers/service.ts +25 -0
  30. package/src/modules/inventory/service.ts +17 -0
  31. package/src/runtime/server.ts +30 -0
@@ -0,0 +1,155 @@
1
+ import { z, createRoute } from "@hono/zod-openapi";
2
+ import { errorResponses } from "./shared.js";
3
+
4
+ export const listCustomersRoute = createRoute({
5
+ method: "get",
6
+ path: "/",
7
+ tags: ["Customers"],
8
+ summary: "List customers",
9
+ request: {
10
+ query: z.object({
11
+ page: z.string().optional().openapi({ example: "1" }),
12
+ limit: z.string().optional().openapi({ example: "20" }),
13
+ }),
14
+ },
15
+ responses: {
16
+ 200: {
17
+ content: {
18
+ "application/json": {
19
+ schema: z.object({
20
+ data: z.array(z.record(z.string(), z.unknown())),
21
+ meta: z.object({
22
+ pagination: z.object({
23
+ page: z.number(),
24
+ limit: z.number(),
25
+ total: z.number(),
26
+ totalPages: z.number(),
27
+ }),
28
+ }),
29
+ }),
30
+ },
31
+ },
32
+ description: "Customer list",
33
+ },
34
+ },
35
+ });
36
+
37
+ export const getCustomerRoute = createRoute({
38
+ method: "get",
39
+ path: "/{id}",
40
+ tags: ["Customers"],
41
+ summary: "Get customer by ID",
42
+ request: {
43
+ params: z.object({
44
+ id: z.string().uuid().openapi({ example: "b482a588-..." }),
45
+ }),
46
+ },
47
+ responses: {
48
+ 200: {
49
+ content: {
50
+ "application/json": {
51
+ schema: z.object({ data: z.record(z.string(), z.unknown()) }),
52
+ },
53
+ },
54
+ description: "Customer detail",
55
+ },
56
+ ...errorResponses,
57
+ },
58
+ });
59
+
60
+ export const updateCustomerRoute = createRoute({
61
+ method: "patch",
62
+ path: "/{id}",
63
+ tags: ["Customers"],
64
+ summary: "Update a customer",
65
+ request: {
66
+ params: z.object({
67
+ id: z.string().uuid(),
68
+ }),
69
+ body: {
70
+ content: {
71
+ "application/json": {
72
+ schema: z.object({
73
+ firstName: z.string().optional(),
74
+ lastName: z.string().optional(),
75
+ email: z.string().email().optional(),
76
+ phone: z.string().optional(),
77
+ metadata: z.record(z.string(), z.unknown()).optional(),
78
+ }).openapi("UpdateCustomerRequest"),
79
+ },
80
+ },
81
+ },
82
+ },
83
+ responses: {
84
+ 200: {
85
+ content: {
86
+ "application/json": {
87
+ schema: z.object({ data: z.record(z.string(), z.unknown()) }),
88
+ },
89
+ },
90
+ description: "Customer updated",
91
+ },
92
+ ...errorResponses,
93
+ },
94
+ });
95
+
96
+ export const getCustomerOrdersRoute = createRoute({
97
+ method: "get",
98
+ path: "/{id}/orders",
99
+ tags: ["Customers"],
100
+ summary: "List orders for a customer",
101
+ request: {
102
+ params: z.object({
103
+ id: z.string().uuid(),
104
+ }),
105
+ query: z.object({
106
+ status: z.string().optional(),
107
+ page: z.string().optional().openapi({ example: "1" }),
108
+ limit: z.string().optional().openapi({ example: "20" }),
109
+ }),
110
+ },
111
+ responses: {
112
+ 200: {
113
+ content: {
114
+ "application/json": {
115
+ schema: z.object({
116
+ data: z.array(z.record(z.string(), z.unknown())),
117
+ meta: z.object({
118
+ pagination: z.object({
119
+ page: z.number(),
120
+ limit: z.number(),
121
+ total: z.number(),
122
+ totalPages: z.number(),
123
+ }),
124
+ }),
125
+ }),
126
+ },
127
+ },
128
+ description: "Customer orders",
129
+ },
130
+ ...errorResponses,
131
+ },
132
+ });
133
+
134
+ export const getCustomerAddressesRoute = createRoute({
135
+ method: "get",
136
+ path: "/{id}/addresses",
137
+ tags: ["Customers"],
138
+ summary: "List addresses for a customer",
139
+ request: {
140
+ params: z.object({
141
+ id: z.string().uuid(),
142
+ }),
143
+ },
144
+ responses: {
145
+ 200: {
146
+ content: {
147
+ "application/json": {
148
+ schema: z.object({ data: z.array(z.record(z.string(), z.unknown())) }),
149
+ },
150
+ },
151
+ description: "Customer addresses",
152
+ },
153
+ ...errorResponses,
154
+ },
155
+ });
@@ -14,6 +14,27 @@ export const CreateWarehouseBodySchema = z.object({
14
14
 
15
15
  // ─── Route Definitions ──────────────────────────────────────────────────────
16
16
 
17
+ export const listInventoryLevelsRoute = createRoute({
18
+ method: "get",
19
+ path: "/levels",
20
+ tags: ["Inventory"],
21
+ summary: "List inventory levels",
22
+ description: "Lists all inventory levels, optionally filtered by warehouse or entity.",
23
+ request: {
24
+ query: z.object({
25
+ warehouseId: z.string().uuid().optional().openapi({ example: "uuid" }),
26
+ entityId: z.string().uuid().optional().openapi({ example: "uuid" }),
27
+ }),
28
+ },
29
+ responses: {
30
+ 200: {
31
+ content: { "application/json": { schema: z.object({ data: z.array(z.record(z.string(), z.unknown())) }) } },
32
+ description: "Inventory levels",
33
+ },
34
+ ...errorResponses,
35
+ },
36
+ });
37
+
17
38
  export const inventoryCheckRoute = createRoute({
18
39
  method: "get",
19
40
  path: "/check",
@@ -43,6 +43,15 @@ export class CustomerService {
43
43
  return customer;
44
44
  }
45
45
 
46
+ async list(
47
+ actor?: Actor | null,
48
+ ctx?: TxContext,
49
+ ): Promise<Result<Customer[]>> {
50
+ const orgId = resolveOrgId(actor ?? ctx?.actor ?? null);
51
+ const customers = await this.repo.findAll(orgId, ctx);
52
+ return Ok(customers);
53
+ }
54
+
46
55
  async getById(
47
56
  id: string,
48
57
  actor?: Actor | null,
@@ -64,6 +73,22 @@ export class CustomerService {
64
73
  return Ok(customer);
65
74
  }
66
75
 
76
+ async update(
77
+ id: string,
78
+ updates: Partial<
79
+ Omit<Customer, "id" | "userId" | "createdAt" | "updatedAt">
80
+ >,
81
+ actor?: Actor | null,
82
+ ctx?: TxContext,
83
+ ): Promise<Result<Customer>> {
84
+ const orgId = resolveOrgId(actor ?? ctx?.actor ?? null);
85
+ const existing = await this.repo.findById(orgId, id, ctx);
86
+ if (!existing) return Err(new CommerceNotFoundError("Customer not found."));
87
+ const updated = await this.repo.update(id, updates, ctx);
88
+ if (!updated) return Err(new CommerceNotFoundError("Customer not found."));
89
+ return Ok(updated);
90
+ }
91
+
67
92
  async updateByUserId(
68
93
  userId: string,
69
94
  updates: Partial<
@@ -110,6 +110,23 @@ export class InventoryService {
110
110
  return Ok(available);
111
111
  }
112
112
 
113
+ async listLevels(
114
+ params?: { warehouseId?: string; entityId?: string },
115
+ actor?: Actor | null,
116
+ ctx?: TxContext,
117
+ ): Promise<Result<InventoryLevel[]>> {
118
+ if (params?.entityId) {
119
+ const levels = await this.repo.findLevelsByEntityId(params.entityId, ctx);
120
+ return Ok(levels);
121
+ }
122
+ if (params?.warehouseId) {
123
+ const levels = await this.repo.findLevelsByWarehouseId(params.warehouseId, ctx);
124
+ return Ok(levels);
125
+ }
126
+ const levels = await this.repo.findAllLevels(ctx);
127
+ return Ok(levels);
128
+ }
129
+
113
130
  async checkMultiple(
114
131
  entityIds: string[],
115
132
  ctx?: TxContext,
@@ -267,6 +267,36 @@ export async function createServer(config: CommerceConfig) {
267
267
  version: config.version ?? "0.0.1",
268
268
  description: "Headless commerce engine REST API. Includes core and plugin endpoints.",
269
269
  },
270
+ tags: [
271
+ // ── Storefront ──
272
+ { name: "Catalog", description: "Products, categories, brands, variants, and option types" },
273
+ { name: "Search", description: "Full-text search and typeahead suggestions" },
274
+ { name: "Pricing", description: "Base prices, price modifiers, and customer group pricing" },
275
+ { name: "Carts", description: "Shopping cart lifecycle — create, add items, update quantities" },
276
+ { name: "Checkout", description: "Convert a cart into a paid order" },
277
+ { name: "Promotions", description: "Discount codes, validation, and usage tracking" },
278
+ // ── Admin ──
279
+ { name: "Orders", description: "Order management — list, detail, status transitions, fulfillments" },
280
+ { name: "Customers", description: "Customer profiles, addresses, groups, and order history" },
281
+ { name: "Inventory", description: "Stock levels, warehouse management, adjustments, and reservations" },
282
+ { name: "Media", description: "File uploads, entity attachments, and signed URLs" },
283
+ { name: "Payments", description: "Payment provider webhooks and event processing" },
284
+ // ── Operations ──
285
+ { name: "Webhooks", description: "Outbound webhook endpoint registration and management" },
286
+ { name: "Audit", description: "Immutable audit log — who changed what and when" },
287
+ { name: "Admin Jobs", description: "Background job queue — view failed jobs and retry" },
288
+ ],
289
+ });
290
+
291
+ // Serve enriched spec with x-tagGroups vendor extension (Scalar/Redocly sidebar grouping)
292
+ const tagGroups = [
293
+ { name: "Storefront", tags: ["Catalog", "Search", "Pricing", "Carts", "Checkout", "Promotions"] },
294
+ { name: "Admin", tags: ["Orders", "Customers", "Inventory", "Media", "Payments"] },
295
+ { name: "Operations", tags: ["Webhooks", "Audit", "Admin Jobs"] },
296
+ ];
297
+ app.get("/api/doc-ext", (c) => {
298
+ const spec = app.getOpenAPIDocument({ openapi: "3.0.0", info: { title: "UnifiedCommerce API", version: config.version ?? "0.0.1" } });
299
+ return c.json({ ...spec, "x-tagGroups": tagGroups });
270
300
  });
271
301
  } else {
272
302
  app.get("/api/doc", (c) => c.json({ error: { code: "NOT_FOUND", message: "Not found." } }, 404));