@unifiedcommerce/core 0.1.0 → 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 (174) hide show
  1. package/package.json +1 -2
  2. package/src/adapters/console-email.ts +0 -43
  3. package/src/auth/access.ts +0 -187
  4. package/src/auth/auth-schema.ts +0 -139
  5. package/src/auth/middleware.ts +0 -161
  6. package/src/auth/org.ts +0 -41
  7. package/src/auth/permissions.ts +0 -28
  8. package/src/auth/setup.ts +0 -169
  9. package/src/auth/system-actor.ts +0 -19
  10. package/src/auth/types.ts +0 -10
  11. package/src/config/defaults.ts +0 -82
  12. package/src/config/define-config.ts +0 -53
  13. package/src/config/types.ts +0 -299
  14. package/src/generated/plugin-capabilities.d.ts +0 -20
  15. package/src/generated/plugin-manifest.ts +0 -23
  16. package/src/generated/plugin-repositories.d.ts +0 -20
  17. package/src/hooks/checkout-completion.ts +0 -262
  18. package/src/hooks/checkout.ts +0 -677
  19. package/src/hooks/order-emails.ts +0 -62
  20. package/src/index.ts +0 -214
  21. package/src/interfaces/mcp/agent-prompt.ts +0 -174
  22. package/src/interfaces/mcp/context-enrichment.ts +0 -177
  23. package/src/interfaces/mcp/server.ts +0 -617
  24. package/src/interfaces/mcp/transport.ts +0 -68
  25. package/src/interfaces/rest/customer-portal.ts +0 -299
  26. package/src/interfaces/rest/index.ts +0 -74
  27. package/src/interfaces/rest/router.ts +0 -334
  28. package/src/interfaces/rest/routes/admin-jobs.ts +0 -58
  29. package/src/interfaces/rest/routes/audit.ts +0 -50
  30. package/src/interfaces/rest/routes/carts.ts +0 -89
  31. package/src/interfaces/rest/routes/catalog.ts +0 -493
  32. package/src/interfaces/rest/routes/checkout.ts +0 -283
  33. package/src/interfaces/rest/routes/inventory.ts +0 -70
  34. package/src/interfaces/rest/routes/media.ts +0 -86
  35. package/src/interfaces/rest/routes/orders.ts +0 -78
  36. package/src/interfaces/rest/routes/payments.ts +0 -60
  37. package/src/interfaces/rest/routes/pricing.ts +0 -57
  38. package/src/interfaces/rest/routes/promotions.ts +0 -92
  39. package/src/interfaces/rest/routes/search.ts +0 -71
  40. package/src/interfaces/rest/routes/webhooks.ts +0 -46
  41. package/src/interfaces/rest/schemas/admin-jobs.ts +0 -40
  42. package/src/interfaces/rest/schemas/audit.ts +0 -46
  43. package/src/interfaces/rest/schemas/carts.ts +0 -125
  44. package/src/interfaces/rest/schemas/catalog.ts +0 -450
  45. package/src/interfaces/rest/schemas/checkout.ts +0 -66
  46. package/src/interfaces/rest/schemas/customer-portal.ts +0 -195
  47. package/src/interfaces/rest/schemas/inventory.ts +0 -138
  48. package/src/interfaces/rest/schemas/media.ts +0 -75
  49. package/src/interfaces/rest/schemas/orders.ts +0 -104
  50. package/src/interfaces/rest/schemas/pricing.ts +0 -80
  51. package/src/interfaces/rest/schemas/promotions.ts +0 -110
  52. package/src/interfaces/rest/schemas/responses.ts +0 -85
  53. package/src/interfaces/rest/schemas/search.ts +0 -58
  54. package/src/interfaces/rest/schemas/shared.ts +0 -62
  55. package/src/interfaces/rest/schemas/webhooks.ts +0 -68
  56. package/src/interfaces/rest/utils.ts +0 -104
  57. package/src/interfaces/rest/webhook-router.ts +0 -50
  58. package/src/kernel/compensation/executor.ts +0 -61
  59. package/src/kernel/compensation/types.ts +0 -26
  60. package/src/kernel/database/adapter.ts +0 -13
  61. package/src/kernel/database/drizzle-db.ts +0 -56
  62. package/src/kernel/database/migrate.ts +0 -76
  63. package/src/kernel/database/plugin-types.ts +0 -34
  64. package/src/kernel/database/schema.ts +0 -49
  65. package/src/kernel/database/scoped-db.ts +0 -68
  66. package/src/kernel/database/tx-context.ts +0 -46
  67. package/src/kernel/error-mapper.ts +0 -15
  68. package/src/kernel/errors.ts +0 -89
  69. package/src/kernel/factory/repository-factory.ts +0 -242
  70. package/src/kernel/hooks/create-context.ts +0 -43
  71. package/src/kernel/hooks/executor.ts +0 -88
  72. package/src/kernel/hooks/registry.ts +0 -74
  73. package/src/kernel/hooks/types.ts +0 -52
  74. package/src/kernel/http-error.ts +0 -44
  75. package/src/kernel/jobs/adapter.ts +0 -36
  76. package/src/kernel/jobs/drizzle-adapter.ts +0 -58
  77. package/src/kernel/jobs/runner.ts +0 -153
  78. package/src/kernel/jobs/schema.ts +0 -46
  79. package/src/kernel/jobs/types.ts +0 -30
  80. package/src/kernel/local-api.ts +0 -185
  81. package/src/kernel/plugin/manifest.ts +0 -253
  82. package/src/kernel/query/executor.ts +0 -184
  83. package/src/kernel/query/registry.ts +0 -46
  84. package/src/kernel/result.ts +0 -33
  85. package/src/kernel/schema/extra-columns.ts +0 -37
  86. package/src/kernel/service-registry.ts +0 -76
  87. package/src/kernel/service-timing.ts +0 -89
  88. package/src/kernel/state-machine/machine.ts +0 -101
  89. package/src/modules/analytics/drizzle-adapter.ts +0 -426
  90. package/src/modules/analytics/hooks.ts +0 -11
  91. package/src/modules/analytics/models.ts +0 -125
  92. package/src/modules/analytics/repository/index.ts +0 -6
  93. package/src/modules/analytics/service.ts +0 -245
  94. package/src/modules/analytics/types.ts +0 -180
  95. package/src/modules/audit/hooks.ts +0 -78
  96. package/src/modules/audit/schema.ts +0 -33
  97. package/src/modules/audit/service.ts +0 -151
  98. package/src/modules/cart/access.ts +0 -27
  99. package/src/modules/cart/matcher.ts +0 -26
  100. package/src/modules/cart/repository/index.ts +0 -234
  101. package/src/modules/cart/schema.ts +0 -42
  102. package/src/modules/cart/schemas.ts +0 -38
  103. package/src/modules/cart/service.ts +0 -541
  104. package/src/modules/catalog/repository/index.ts +0 -772
  105. package/src/modules/catalog/schema.ts +0 -203
  106. package/src/modules/catalog/schemas.ts +0 -104
  107. package/src/modules/catalog/service.ts +0 -1544
  108. package/src/modules/customers/repository/index.ts +0 -327
  109. package/src/modules/customers/schema.ts +0 -64
  110. package/src/modules/customers/service.ts +0 -171
  111. package/src/modules/fulfillment/repository/index.ts +0 -426
  112. package/src/modules/fulfillment/schema.ts +0 -101
  113. package/src/modules/fulfillment/service.ts +0 -555
  114. package/src/modules/fulfillment/types.ts +0 -59
  115. package/src/modules/inventory/repository/index.ts +0 -509
  116. package/src/modules/inventory/schema.ts +0 -94
  117. package/src/modules/inventory/schemas.ts +0 -38
  118. package/src/modules/inventory/service.ts +0 -490
  119. package/src/modules/media/adapter.ts +0 -17
  120. package/src/modules/media/repository/index.ts +0 -274
  121. package/src/modules/media/schema.ts +0 -41
  122. package/src/modules/media/service.ts +0 -151
  123. package/src/modules/orders/repository/index.ts +0 -287
  124. package/src/modules/orders/schema.ts +0 -66
  125. package/src/modules/orders/service.ts +0 -619
  126. package/src/modules/orders/stale-order-cleanup.ts +0 -76
  127. package/src/modules/organization/service.ts +0 -191
  128. package/src/modules/payments/adapter.ts +0 -47
  129. package/src/modules/payments/repository/index.ts +0 -6
  130. package/src/modules/payments/service.ts +0 -107
  131. package/src/modules/pricing/repository/index.ts +0 -291
  132. package/src/modules/pricing/schema.ts +0 -71
  133. package/src/modules/pricing/schemas.ts +0 -38
  134. package/src/modules/pricing/service.ts +0 -494
  135. package/src/modules/promotions/repository/index.ts +0 -325
  136. package/src/modules/promotions/schema.ts +0 -62
  137. package/src/modules/promotions/schemas.ts +0 -38
  138. package/src/modules/promotions/service.ts +0 -598
  139. package/src/modules/search/adapter.ts +0 -57
  140. package/src/modules/search/hooks.ts +0 -12
  141. package/src/modules/search/repository/index.ts +0 -6
  142. package/src/modules/search/service.ts +0 -315
  143. package/src/modules/shipping/calculator.ts +0 -188
  144. package/src/modules/shipping/repository/index.ts +0 -6
  145. package/src/modules/shipping/service.ts +0 -51
  146. package/src/modules/tax/adapter.ts +0 -60
  147. package/src/modules/tax/repository/index.ts +0 -6
  148. package/src/modules/tax/service.ts +0 -53
  149. package/src/modules/webhooks/hook.ts +0 -34
  150. package/src/modules/webhooks/repository/index.ts +0 -278
  151. package/src/modules/webhooks/schema.ts +0 -56
  152. package/src/modules/webhooks/service.ts +0 -117
  153. package/src/modules/webhooks/signing.ts +0 -6
  154. package/src/modules/webhooks/ssrf-guard.ts +0 -71
  155. package/src/modules/webhooks/tasks.ts +0 -52
  156. package/src/modules/webhooks/worker.ts +0 -134
  157. package/src/runtime/commerce.ts +0 -145
  158. package/src/runtime/kernel.ts +0 -419
  159. package/src/runtime/logger.ts +0 -36
  160. package/src/runtime/server.ts +0 -349
  161. package/src/runtime/shutdown.ts +0 -43
  162. package/src/test-utils/create-pglite-adapter.ts +0 -129
  163. package/src/test-utils/create-plugin-test-app.ts +0 -128
  164. package/src/test-utils/create-repository-test-harness.ts +0 -16
  165. package/src/test-utils/create-test-config.ts +0 -190
  166. package/src/test-utils/create-test-kernel.ts +0 -7
  167. package/src/test-utils/create-test-plugin-context.ts +0 -75
  168. package/src/test-utils/rest-api-test-utils.ts +0 -265
  169. package/src/test-utils/test-actors.ts +0 -62
  170. package/src/test-utils/typed-hooks.ts +0 -54
  171. package/src/types/commerce-types.ts +0 -34
  172. package/src/utils/id.ts +0 -3
  173. package/src/utils/logger.ts +0 -18
  174. package/src/utils/pagination.ts +0 -22
@@ -1,16 +0,0 @@
1
- import type { CommerceConfig } from "../config/types.js";
2
- import { createTestConfig } from "./create-test-config.js";
3
- import { createKernel } from "../runtime/kernel.js";
4
-
5
- export interface RepositoryTestHarness {
6
- config: CommerceConfig;
7
- kernel: ReturnType<typeof createKernel>;
8
- }
9
-
10
- export async function createRepositoryTestHarness(
11
- overrides: Partial<CommerceConfig> = {},
12
- ): Promise<RepositoryTestHarness> {
13
- const config = await createTestConfig(overrides);
14
- const kernel = createKernel(config);
15
- return { config, kernel };
16
- }
@@ -1,190 +0,0 @@
1
- import { defineConfig } from "../config/define-config.js";
2
- import type { CommerceConfig } from "../config/types.js";
3
- import { Ok } from "../kernel/result.js";
4
- import type { StorageAdapter } from "../modules/media/adapter.js";
5
-
6
- function createInMemoryStorageAdapter(): StorageAdapter {
7
- const files = new Map<string, { data: ArrayBuffer; contentType: string }>();
8
- const baseUrl = "http://localhost:3000/test-assets";
9
-
10
- return {
11
- providerId: "test-memory-storage",
12
- async upload(key, data, contentType) {
13
- const body =
14
- data instanceof ArrayBuffer
15
- ? data
16
- : await new Response(data).arrayBuffer();
17
- files.set(key, { data: body, contentType });
18
- return Ok({
19
- key,
20
- url: `${baseUrl}/${key}`,
21
- contentType,
22
- size: body.byteLength,
23
- });
24
- },
25
- async getUrl(key) {
26
- return Ok(`${baseUrl}/${key}`);
27
- },
28
- async getSignedUrl(key, expiresIn) {
29
- return Ok(`${baseUrl}/${key}?expiresIn=${expiresIn}`);
30
- },
31
- async delete(key) {
32
- files.delete(key);
33
- return Ok(undefined);
34
- },
35
- async list(prefix) {
36
- return Ok(
37
- Array.from(files.entries())
38
- .filter(([key]) => key.startsWith(prefix))
39
- .map(([key, file]) => ({
40
- key,
41
- url: `${baseUrl}/${key}`,
42
- contentType: file.contentType,
43
- size: file.data.byteLength,
44
- })),
45
- );
46
- },
47
- };
48
- }
49
-
50
- export async function createTestConfig(
51
- overrides: Partial<CommerceConfig> = {},
52
- ): Promise<CommerceConfig> {
53
- // Auto-provision PGlite when no databaseAdapter is provided
54
- if (!overrides.databaseAdapter) {
55
- const { createPGliteTestAdapter } = await import("./create-pglite-adapter.js");
56
- const { adapter } = await createPGliteTestAdapter();
57
- overrides = { ...overrides, databaseAdapter: adapter };
58
- }
59
-
60
- return defineConfig({
61
- version: "0.0.1-test",
62
- storeName: "Test Store",
63
- database: {
64
- provider: "postgresql",
65
- },
66
- auth: {
67
- requireEmailVerification: false,
68
- apiKeys: { enabled: true, defaultPermissions: ["catalog:read"] },
69
- enableDevKey: true,
70
- devKey: "dev-staff-key",
71
- posPin: { enabled: true },
72
- roles: {
73
- owner: { permissions: ["*:*"] },
74
- admin: { permissions: ["*:*"] },
75
- staff: {
76
- permissions: [
77
- "catalog:create",
78
- "catalog:update",
79
- "catalog:delete",
80
- "catalog:read",
81
- "inventory:adjust",
82
- "orders:create",
83
- "orders:read",
84
- "orders:update",
85
- "cart:create",
86
- "cart:update",
87
- "customers:update:self",
88
- ],
89
- },
90
- ai_agent: {
91
- permissions: [
92
- "catalog:read",
93
- "catalog:create",
94
- "inventory:read",
95
- "inventory:adjust",
96
- "orders:read",
97
- "cart:create",
98
- "cart:update",
99
- "mcp:access",
100
- ],
101
- },
102
- },
103
- customerPermissions: [
104
- "catalog:read",
105
- "cart:create",
106
- "cart:read",
107
- "cart:update",
108
- "orders:create",
109
- "orders:read:own",
110
- "customers:read:self",
111
- "customers:update:self",
112
- ],
113
- },
114
- entities: {
115
- product: {
116
- fields: [
117
- { name: "weight", type: "number" },
118
- { name: "brand", type: "text" },
119
- ],
120
- variants: { enabled: true, optionTypes: ["size", "color"] },
121
- fulfillment: "physical",
122
- },
123
- digitalDownload: {
124
- fields: [{ name: "fileAssetId", type: "text" }],
125
- variants: { enabled: false },
126
- fulfillment: "digital-download",
127
- },
128
- course: {
129
- fields: [{ name: "modules", type: "json" }],
130
- variants: { enabled: false },
131
- fulfillment: "digital-access",
132
- },
133
- },
134
- cart: {
135
- ttlMinutes: 5,
136
- hooks: {},
137
- },
138
- checkout: {
139
- hooks: {
140
- beforeCreate: [],
141
- afterCreate: [],
142
- },
143
- },
144
- orders: {
145
- hooks: {
146
- beforeCreate: [],
147
- afterCreate: [],
148
- beforeStatusChange: [],
149
- afterStatusChange: [],
150
- },
151
- },
152
- inventory: {
153
- hooks: {
154
- afterAdjust: [],
155
- },
156
- },
157
- email: {
158
- async send() {
159
- // no-op for tests
160
- },
161
- },
162
- storage: createInMemoryStorageAdapter(),
163
- ...overrides,
164
- });
165
- }
166
-
167
- /**
168
- * Creates a test config backed by PGlite (in-memory PostgreSQL).
169
- *
170
- * This provides production parity for tests by using real SQL execution
171
- * and PostgreSQL behavior while remaining fast and self-contained.
172
- *
173
- * @param overrides - Optional config overrides
174
- * @returns A promise resolving to an object containing:
175
- * - config: The CommerceConfig to pass to createKernel
176
- * - cleanup: Async function to reset data between tests
177
- */
178
- export async function createPGliteTestConfig(
179
- overrides: Partial<CommerceConfig> = {},
180
- ): Promise<{ config: CommerceConfig; cleanup: () => Promise<void> }> {
181
- const { createPGliteTestAdapter } = await import("./create-pglite-adapter.js");
182
- const { adapter, cleanup } = await createPGliteTestAdapter();
183
-
184
- const config = await createTestConfig({
185
- databaseAdapter: adapter,
186
- ...overrides,
187
- });
188
-
189
- return { config, cleanup };
190
- }
@@ -1,7 +0,0 @@
1
- import type { CommerceConfig } from "../config/types.js";
2
- import { createKernel } from "../runtime/kernel.js";
3
- import { createTestConfig } from "./create-test-config.js";
4
-
5
- export async function createTestKernel(overrides: Partial<CommerceConfig> = {}) {
6
- return createKernel(await createTestConfig(overrides));
7
- }
@@ -1,75 +0,0 @@
1
- import { HookRegistry } from "../kernel/hooks/registry.js";
2
- import { createLogger } from "../utils/logger.js";
3
- import { createTestConfig } from "./create-test-config.js";
4
- import type { CommerceConfig, MCPTool } from "../config/types.js";
5
-
6
- interface PluginContextShape {
7
- hooks: HookRegistry;
8
- config: CommerceConfig;
9
- services: Record<string, unknown>;
10
- routes: { add(method: string, path: string, handler: (...args: unknown[]) => unknown): void };
11
- mcp: { registerTool(tool: MCPTool): void };
12
- analytics: { registerModel(model: unknown): void };
13
- database: {
14
- registerSchema(schema: Record<string, unknown>): void;
15
- query: unknown;
16
- transaction<T>(fn: (tx: unknown) => Promise<T>): Promise<T>;
17
- };
18
- logger: { info(message: string, data?: unknown): void; warn(message: string, data?: unknown): void; error(message: string, data?: unknown): void };
19
- }
20
-
21
- export interface TestPluginContext extends PluginContextShape {
22
- registeredRoutes: Array<{ method: string; path: string; handler: (...args: unknown[]) => unknown }>;
23
- registeredMCPTools: MCPTool[];
24
- registeredAnalyticsModels: unknown[];
25
- registeredSchemas: Array<Record<string, unknown>>;
26
- }
27
-
28
- export async function createTestPluginContext(options?: {
29
- config?: Partial<CommerceConfig>;
30
- services?: Record<string, unknown>;
31
- }): Promise<TestPluginContext> {
32
- const hooks = new HookRegistry();
33
- const config = await createTestConfig(options?.config ?? {});
34
- const services = options?.services ?? {};
35
-
36
- const registeredRoutes: TestPluginContext["registeredRoutes"] = [];
37
- const registeredMCPTools: MCPTool[] = [];
38
- const registeredAnalyticsModels: unknown[] = [];
39
- const registeredSchemas: Array<Record<string, unknown>> = [];
40
-
41
- return {
42
- hooks,
43
- config,
44
- services,
45
- routes: {
46
- add(method, path, handler) {
47
- registeredRoutes.push({ method: method.toUpperCase(), path, handler });
48
- },
49
- },
50
- mcp: {
51
- registerTool(tool) {
52
- registeredMCPTools.push(tool);
53
- },
54
- },
55
- analytics: {
56
- registerModel(model) {
57
- registeredAnalyticsModels.push(model);
58
- },
59
- },
60
- database: {
61
- registerSchema(schema) {
62
- registeredSchemas.push(schema);
63
- },
64
- query: {},
65
- async transaction<T>(fn: (tx: unknown) => Promise<T>): Promise<T> {
66
- return fn({});
67
- },
68
- },
69
- logger: createLogger("test-plugin-context"),
70
- registeredRoutes,
71
- registeredMCPTools,
72
- registeredAnalyticsModels,
73
- registeredSchemas,
74
- };
75
- }
@@ -1,265 +0,0 @@
1
- /**
2
- * Integration test utilities for REST API endpoints.
3
- *
4
- * Provides helper functions to test Hono routes with a real kernel,
5
- * PGlite database, and authentication middleware.
6
- */
7
-
8
- import { Hono } from "hono";
9
- import { createKernel } from "../runtime/kernel.js";
10
- import { createAuth } from "../auth/setup.js";
11
- import { authMiddleware } from "../auth/middleware.js";
12
- import { createRestRoutes } from "../interfaces/rest/index.js";
13
- import { createPGliteTestConfig } from "./create-test-config.js";
14
- import { CommerceValidationError } from "../kernel/errors.js";
15
- import { Ok, Err } from "../kernel/result.js";
16
- import type { CommerceConfig } from "../config/types.js";
17
- import type { Actor } from "../auth/types.js";
18
- import type { AuthInstance } from "../auth/setup.js";
19
-
20
- type ServerEnv = {
21
- Variables: {
22
- auth: AuthInstance;
23
- actor: Actor | null;
24
- };
25
- };
26
-
27
- const mockPaymentAdapter = {
28
- providerId: "test-payments",
29
- async createPaymentIntent(params: { amount: number; currency: string }) {
30
- return Ok({
31
- id: "pi_test_" + Date.now(),
32
- status: "requires_capture",
33
- amount: params.amount,
34
- currency: params.currency,
35
- clientSecret: "secret_test",
36
- });
37
- },
38
- async capturePayment() {
39
- return Ok({ id: "pi_test_" + Date.now(), status: "succeeded", amountCaptured: 1000 });
40
- },
41
- async refundPayment() {
42
- return Ok({ id: "re_test_" + Date.now(), status: "succeeded", amountRefunded: 1000 });
43
- },
44
- async cancelPaymentIntent() {
45
- return Ok(undefined);
46
- },
47
- async verifyWebhook(request: Request) {
48
- // Extract signature from headers
49
- const signature = request.headers.get("stripe-signature") || request.headers.get("webhook-signature");
50
-
51
- // Extract payload from request body
52
- let payload: Record<string, unknown> | undefined;
53
- try {
54
- payload = await request.clone().json();
55
- } catch {
56
- return Err(new CommerceValidationError("Webhook payload is invalid or missing"));
57
- }
58
-
59
- // Simulate signature verification for production-hardened testing
60
- // Reject invalid signatures like "invalid_signature"
61
- if (signature === "invalid_signature") {
62
- return Err(new CommerceValidationError("Invalid webhook signature"));
63
- }
64
-
65
- // Reject requests with no required fields
66
- if (!payload || !payload.type) {
67
- return Err(new CommerceValidationError("Webhook payload is missing required fields"));
68
- }
69
-
70
- return Ok({ id: "evt_test_" + Date.now(), type: String(payload.type), data: (payload.data ?? {}) as unknown });
71
- },
72
- };
73
-
74
- /**
75
- * Creates a test server with PGlite-backed kernel for REST API testing.
76
- */
77
- export async function createTestServer(
78
- overrides: Partial<CommerceConfig> = {},
79
- ): Promise<{
80
- server: Hono<ServerEnv>;
81
- kernel: ReturnType<typeof createKernel>;
82
- auth: AuthInstance;
83
- cleanup: () => Promise<void>;
84
- }> {
85
- const { config, cleanup } = await createPGliteTestConfig({
86
- payments: [mockPaymentAdapter],
87
- ...overrides,
88
- });
89
-
90
- const kernel = createKernel(config);
91
- const auth = createAuth(kernel.database, config);
92
- const app = new Hono<ServerEnv>();
93
-
94
- // Set auth in context (like createServer does)
95
- app.use("*", async (c, next) => {
96
- c.set("auth", auth);
97
- await next();
98
- });
99
-
100
- // Test middleware: allow direct actor injection via x-test-actor header for testing
101
- app.use("*", async (c, next) => {
102
- const testActorHeader = c.req.header("x-test-actor");
103
- if (testActorHeader) {
104
- try {
105
- const actor = JSON.parse(testActorHeader) as Actor;
106
- c.set("actor", actor);
107
- await next();
108
- return;
109
- } catch {
110
- // Invalid JSON, continue to auth middleware
111
- }
112
- }
113
- await next();
114
- });
115
-
116
- // Add auth middleware
117
- app.use("*", authMiddleware(auth, config));
118
-
119
- // Error handling middleware - catch thrown errors and convert to JSON
120
- app.use("*", async (c, next) => {
121
- try {
122
- await next();
123
- } catch (error) {
124
- // Import error handling utilities
125
- const { mapErrorToResponse, mapErrorToStatus } = await import("../interfaces/rest/utils.js");
126
- return c.json(
127
- mapErrorToResponse(error),
128
- mapErrorToStatus(error),
129
- );
130
- }
131
- });
132
-
133
- // Add REST routes
134
- app.route("/api", createRestRoutes(kernel));
135
-
136
- return { server: app, kernel, auth, cleanup };
137
- }
138
-
139
- /**
140
- * Helper to parse JSON response from Hono Response
141
- */
142
- export async function parseJsonResponse<T = unknown>(response: Response): Promise<T> {
143
- return response.json() as Promise<T>;
144
- }
145
-
146
- /**
147
- * Common test actor with staff permissions
148
- */
149
- export const testActor: Actor = {
150
- type: "user",
151
- userId: "00000000-0000-0000-0000-000000000001",
152
- email: "test@example.com",
153
- name: "Test Staff",
154
- vendorId: null,
155
- organizationId: "org_default",
156
- role: "staff",
157
- permissions: [
158
- "catalog:create",
159
- "catalog:update",
160
- "catalog:read",
161
- "inventory:adjust",
162
- "inventory:read",
163
- "orders:create",
164
- "orders:read",
165
- "orders:update",
166
- "cart:create",
167
- "cart:update",
168
- "cart:read",
169
- "customers:update:self",
170
- "webhooks:manage",
171
- "pricing:manage",
172
- "promotions:manage",
173
- "promotions:read",
174
- "audit:read",
175
- "media:write",
176
- ],
177
- };
178
-
179
- /**
180
- * Test actor with read-only permissions
181
- */
182
- export const readonlyActor: Actor = {
183
- type: "user",
184
- userId: "00000000-0000-0000-0000-000000000002",
185
- email: "readonly@example.com",
186
- name: "Read Only User",
187
- vendorId: null,
188
- organizationId: "org_default",
189
- role: "customer",
190
- permissions: ["catalog:read", "cart:read", "orders:read:own"],
191
- };
192
-
193
- /**
194
- * Test actor with no permissions
195
- */
196
- export const noPermActor: Actor = {
197
- type: "user",
198
- userId: "00000000-0000-0000-0000-000000000003",
199
- email: "noperm@example.com",
200
- name: "No Perm",
201
- vendorId: null,
202
- organizationId: "org_default",
203
- role: "customer",
204
- permissions: [],
205
- };
206
-
207
- /**
208
- * Helper to create a mock request with actor context
209
- */
210
- export function createMockRequest(server: Hono<ServerEnv>, options: {
211
- method: string;
212
- url: string;
213
- body?: unknown;
214
- headers?: Record<string, string>;
215
- actor?: Actor;
216
- }) {
217
- const url = new URL(options.url, "http://localhost");
218
-
219
- const headers: Record<string, string> = {
220
- "content-type": "application/json",
221
- ...options.headers,
222
- };
223
-
224
- // Add actor as header for test middleware
225
- if (options.actor) {
226
- headers["x-test-actor"] = JSON.stringify(options.actor);
227
- }
228
-
229
- // Build the request
230
- const requestInit: RequestInit = {
231
- method: options.method,
232
- headers,
233
- };
234
- if (options.body) {
235
- requestInit.body = JSON.stringify(options.body);
236
- }
237
- const request = new Request(url, requestInit);
238
-
239
- return request;
240
- }
241
-
242
- /**
243
- * Helper to make authenticated requests to the test server
244
- */
245
- export async function makeRequest(
246
- server: Hono<ServerEnv>,
247
- options: {
248
- method: string;
249
- url: string;
250
- body?: unknown;
251
- headers?: Record<string, string>;
252
- actor?: Actor;
253
- },
254
- ) {
255
- // Create request with actor header (defaults to testActor)
256
- const request = createMockRequest(server, {
257
- ...options,
258
- actor: options.actor ?? testActor,
259
- });
260
-
261
- // Route the request through Hono
262
- const response = await server.fetch(request);
263
-
264
- return response;
265
- }
@@ -1,62 +0,0 @@
1
- import type { Actor } from "../auth/types.js";
2
-
3
- /** Admin with wildcard permissions. Use for setup operations in beforeAll. */
4
- export const testAdminActor: Actor = {
5
- type: "user",
6
- userId: "test-admin-1",
7
- email: "admin@test.local",
8
- name: "Test Admin",
9
- vendorId: null,
10
- organizationId: "org_default",
11
- role: "admin",
12
- permissions: ["*:*"],
13
- };
14
-
15
- /** Staff with common operational permissions. */
16
- export const testStaffActor: Actor = {
17
- type: "user",
18
- userId: "test-staff-1",
19
- email: "staff@test.local",
20
- name: "Test Staff",
21
- vendorId: null,
22
- organizationId: "org_default",
23
- role: "staff",
24
- permissions: [
25
- "catalog:read", "catalog:create", "catalog:update",
26
- "inventory:adjust", "orders:read", "orders:create", "orders:update",
27
- ],
28
- };
29
-
30
- /** Customer with minimal read/write-own permissions. */
31
- export const testCustomerActor: Actor = {
32
- type: "user",
33
- userId: "test-customer-1",
34
- email: "customer@test.local",
35
- name: "Test Customer",
36
- vendorId: null,
37
- organizationId: "org_default",
38
- role: "customer",
39
- permissions: ["catalog:read", "cart:create", "cart:read", "orders:read:own"],
40
- };
41
-
42
- /** Actor with zero permissions. Use for negative auth/perm tests. */
43
- export const testNoPermActor: Actor = {
44
- type: "user",
45
- userId: "test-noperm-1",
46
- email: "noperm@test.local",
47
- name: "No Permissions",
48
- vendorId: null,
49
- organizationId: "org_default",
50
- role: "customer",
51
- permissions: [],
52
- };
53
-
54
- /**
55
- * Builds request headers with optional test actor injection.
56
- * The x-test-actor header is parsed by createPluginTestApp's middleware.
57
- */
58
- export function jsonHeaders(actor?: Actor): Record<string, string> {
59
- const headers: Record<string, string> = { "Content-Type": "application/json" };
60
- if (actor) headers["x-test-actor"] = JSON.stringify(actor);
61
- return headers;
62
- }
@@ -1,54 +0,0 @@
1
- import type { PluginHookRegistration } from "../kernel/plugin/manifest.js";
2
- import type { HookContext, HookOperation } from "../kernel/hooks/types.js";
3
-
4
- /**
5
- * Creates a typed before-hook registration for plugins.
6
- *
7
- * Narrows the handler signature from the loose `(...args: unknown[]) => unknown`
8
- * on PluginHookRegistration to the actual `{ data, operation, context }` shape,
9
- * providing autocomplete on context.jobs.enqueue(), context.logger.info(), etc.
10
- *
11
- * @example
12
- * ```typescript
13
- * hooks: () => [
14
- * beforeHook<{ customerId: string }>("orders.beforeCreate", async ({ data, context }) => {
15
- * context.logger.info("order_creating", { customerId: data.customerId });
16
- * return data;
17
- * }),
18
- * ],
19
- * ```
20
- */
21
- export function beforeHook<TData>(
22
- key: string,
23
- handler: (args: {
24
- data: TData;
25
- operation: HookOperation;
26
- context: HookContext;
27
- }) => Promise<TData> | TData,
28
- ): PluginHookRegistration {
29
- return { key, handler: handler as PluginHookRegistration["handler"] };
30
- }
31
-
32
- /**
33
- * Creates a typed after-hook registration for plugins.
34
- *
35
- * @example
36
- * ```typescript
37
- * hooks: () => [
38
- * afterHook<{ id: string; grandTotal: number }>("orders.afterCreate", async ({ result, context }) => {
39
- * await context.jobs.enqueue("loyalty:award-points", { orderId: result.id });
40
- * }),
41
- * ],
42
- * ```
43
- */
44
- export function afterHook<TData>(
45
- key: string,
46
- handler: (args: {
47
- data: TData | null;
48
- result: TData;
49
- operation: HookOperation;
50
- context: HookContext;
51
- }) => Promise<void> | void,
52
- ): PluginHookRegistration {
53
- return { key, handler: handler as PluginHookRegistration["handler"] };
54
- }