@porulle/core 0.1.0 → 0.6.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 (166) hide show
  1. package/LICENSE +21 -0
  2. package/dist/auth/middleware.d.ts.map +1 -1
  3. package/dist/auth/middleware.js +17 -1
  4. package/dist/config/define-config.d.ts.map +1 -1
  5. package/dist/config/define-config.js +6 -0
  6. package/dist/config/types.d.ts +12 -0
  7. package/dist/config/types.d.ts.map +1 -1
  8. package/dist/hooks/checkout.d.ts +3 -3
  9. package/dist/hooks/checkout.js +4 -4
  10. package/dist/index.d.ts +9 -1
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +6 -1
  13. package/dist/interfaces/rest/audit-middleware.d.ts +46 -0
  14. package/dist/interfaces/rest/audit-middleware.d.ts.map +1 -0
  15. package/dist/interfaces/rest/audit-middleware.js +82 -0
  16. package/dist/interfaces/rest/parse-json.d.ts +39 -0
  17. package/dist/interfaces/rest/parse-json.d.ts.map +1 -0
  18. package/dist/interfaces/rest/parse-json.js +45 -0
  19. package/dist/interfaces/rest/routes/catalog.d.ts.map +1 -1
  20. package/dist/interfaces/rest/routes/catalog.js +17 -2
  21. package/dist/interfaces/rest/routes/customers.d.ts.map +1 -1
  22. package/dist/interfaces/rest/routes/customers.js +59 -4
  23. package/dist/interfaces/rest/routes/inventory.d.ts.map +1 -1
  24. package/dist/interfaces/rest/routes/inventory.js +4 -2
  25. package/dist/interfaces/rest/routes/media.d.ts.map +1 -1
  26. package/dist/interfaces/rest/routes/media.js +6 -0
  27. package/dist/interfaces/rest/routes/orders.d.ts.map +1 -1
  28. package/dist/interfaces/rest/routes/orders.js +23 -1
  29. package/dist/interfaces/rest/routes/promotions.d.ts.map +1 -1
  30. package/dist/interfaces/rest/routes/promotions.js +21 -1
  31. package/dist/interfaces/rest/schemas/carts.d.ts +175 -0
  32. package/dist/interfaces/rest/schemas/carts.d.ts.map +1 -1
  33. package/dist/interfaces/rest/schemas/catalog.d.ts +1410 -0
  34. package/dist/interfaces/rest/schemas/catalog.d.ts.map +1 -1
  35. package/dist/interfaces/rest/schemas/catalog.js +28 -1
  36. package/dist/interfaces/rest/schemas/checkout.d.ts +35 -0
  37. package/dist/interfaces/rest/schemas/checkout.d.ts.map +1 -1
  38. package/dist/interfaces/rest/schemas/customer-portal.d.ts +280 -0
  39. package/dist/interfaces/rest/schemas/customer-portal.d.ts.map +1 -1
  40. package/dist/interfaces/rest/schemas/customers.d.ts +863 -0
  41. package/dist/interfaces/rest/schemas/customers.d.ts.map +1 -1
  42. package/dist/interfaces/rest/schemas/customers.js +88 -0
  43. package/dist/interfaces/rest/schemas/inventory.d.ts +182 -1
  44. package/dist/interfaces/rest/schemas/inventory.d.ts.map +1 -1
  45. package/dist/interfaces/rest/schemas/media.d.ts +105 -0
  46. package/dist/interfaces/rest/schemas/media.d.ts.map +1 -1
  47. package/dist/interfaces/rest/schemas/orders.d.ts +136 -0
  48. package/dist/interfaces/rest/schemas/orders.d.ts.map +1 -1
  49. package/dist/interfaces/rest/schemas/orders.js +20 -0
  50. package/dist/interfaces/rest/schemas/pricing.d.ts +70 -0
  51. package/dist/interfaces/rest/schemas/pricing.d.ts.map +1 -1
  52. package/dist/interfaces/rest/schemas/promotions.d.ts +298 -0
  53. package/dist/interfaces/rest/schemas/promotions.d.ts.map +1 -1
  54. package/dist/interfaces/rest/schemas/promotions.js +25 -1
  55. package/dist/interfaces/rest/schemas/shared.d.ts +47 -0
  56. package/dist/interfaces/rest/schemas/shared.d.ts.map +1 -1
  57. package/dist/interfaces/rest/schemas/shared.js +8 -0
  58. package/dist/interfaces/rest/schemas/webhooks.d.ts +70 -0
  59. package/dist/interfaces/rest/schemas/webhooks.d.ts.map +1 -1
  60. package/dist/kernel/database/adapter.d.ts +6 -0
  61. package/dist/kernel/database/adapter.d.ts.map +1 -1
  62. package/dist/kernel/database/adapter.js +62 -1
  63. package/dist/kernel/database/migrate.d.ts +22 -2
  64. package/dist/kernel/database/migrate.d.ts.map +1 -1
  65. package/dist/kernel/database/migrate.js +42 -2
  66. package/dist/modules/analytics/hooks.d.ts +5 -5
  67. package/dist/modules/analytics/hooks.js +5 -5
  68. package/dist/modules/analytics/repository/index.d.ts.map +1 -1
  69. package/dist/modules/catalog/category-service.d.ts +6 -1
  70. package/dist/modules/catalog/category-service.d.ts.map +1 -1
  71. package/dist/modules/catalog/category-service.js +35 -5
  72. package/dist/modules/catalog/schema.d.ts +17 -0
  73. package/dist/modules/catalog/schema.d.ts.map +1 -1
  74. package/dist/modules/catalog/schema.js +2 -0
  75. package/dist/modules/catalog/service.d.ts +11 -2
  76. package/dist/modules/catalog/service.d.ts.map +1 -1
  77. package/dist/modules/catalog/service.js +8 -2
  78. package/dist/modules/customers/repository/index.d.ts +8 -1
  79. package/dist/modules/customers/repository/index.d.ts.map +1 -1
  80. package/dist/modules/customers/repository/index.js +42 -2
  81. package/dist/modules/customers/schema.d.ts +162 -0
  82. package/dist/modules/customers/schema.d.ts.map +1 -1
  83. package/dist/modules/customers/schema.js +18 -0
  84. package/dist/modules/customers/service.d.ts +32 -2
  85. package/dist/modules/customers/service.d.ts.map +1 -1
  86. package/dist/modules/customers/service.js +119 -3
  87. package/dist/modules/inventory/schemas.d.ts +7 -1
  88. package/dist/modules/inventory/schemas.d.ts.map +1 -1
  89. package/dist/modules/inventory/schemas.js +6 -1
  90. package/dist/modules/inventory/service.d.ts +18 -0
  91. package/dist/modules/inventory/service.d.ts.map +1 -1
  92. package/dist/modules/inventory/service.js +58 -21
  93. package/dist/modules/media/noop-adapter.d.ts +11 -0
  94. package/dist/modules/media/noop-adapter.d.ts.map +1 -0
  95. package/dist/modules/media/noop-adapter.js +31 -0
  96. package/dist/modules/orders/repository/index.d.ts +19 -0
  97. package/dist/modules/orders/repository/index.d.ts.map +1 -1
  98. package/dist/modules/orders/repository/index.js +42 -1
  99. package/dist/modules/orders/service.d.ts +31 -0
  100. package/dist/modules/orders/service.d.ts.map +1 -1
  101. package/dist/modules/orders/service.js +52 -2
  102. package/dist/modules/payments/repository/index.d.ts.map +1 -1
  103. package/dist/modules/promotions/schemas.d.ts +46 -0
  104. package/dist/modules/promotions/schemas.d.ts.map +1 -1
  105. package/dist/modules/promotions/schemas.js +19 -8
  106. package/dist/modules/promotions/service.d.ts +8 -2
  107. package/dist/modules/promotions/service.d.ts.map +1 -1
  108. package/dist/modules/promotions/service.js +74 -9
  109. package/dist/modules/search/repository/index.d.ts.map +1 -1
  110. package/dist/modules/shipping/repository/index.d.ts.map +1 -1
  111. package/dist/modules/tax/repository/index.d.ts.map +1 -1
  112. package/dist/runtime/client-ip.d.ts +31 -0
  113. package/dist/runtime/client-ip.d.ts.map +1 -0
  114. package/dist/runtime/client-ip.js +31 -0
  115. package/dist/runtime/server.d.ts +4 -0
  116. package/dist/runtime/server.d.ts.map +1 -1
  117. package/dist/runtime/server.js +35 -19
  118. package/dist/test-utils/create-plugin-test-app.d.ts.map +1 -1
  119. package/dist/test-utils/create-plugin-test-app.js +4 -1
  120. package/dist/test-utils/test-actors.d.ts +3 -2
  121. package/dist/test-utils/test-actors.d.ts.map +1 -1
  122. package/dist/test-utils/test-actors.js +3 -2
  123. package/package.json +15 -15
  124. package/src/auth/middleware.ts +18 -1
  125. package/src/config/define-config.ts +7 -0
  126. package/src/config/types.ts +12 -0
  127. package/src/hooks/checkout.ts +4 -4
  128. package/src/index.ts +18 -1
  129. package/src/interfaces/rest/audit-middleware.ts +110 -0
  130. package/src/interfaces/rest/parse-json.ts +81 -0
  131. package/src/interfaces/rest/routes/catalog.ts +21 -1
  132. package/src/interfaces/rest/routes/customers.ts +64 -3
  133. package/src/interfaces/rest/routes/inventory.ts +4 -2
  134. package/src/interfaces/rest/routes/media.ts +7 -0
  135. package/src/interfaces/rest/routes/orders.ts +21 -1
  136. package/src/interfaces/rest/routes/promotions.ts +22 -1
  137. package/src/interfaces/rest/schemas/catalog.ts +30 -1
  138. package/src/interfaces/rest/schemas/customers.ts +99 -0
  139. package/src/interfaces/rest/schemas/orders.ts +21 -0
  140. package/src/interfaces/rest/schemas/promotions.ts +32 -1
  141. package/src/interfaces/rest/schemas/shared.ts +9 -0
  142. package/src/kernel/database/adapter.ts +64 -1
  143. package/src/kernel/database/migrate.ts +50 -2
  144. package/src/modules/analytics/hooks.ts +5 -5
  145. package/src/modules/analytics/repository/index.ts +0 -1
  146. package/src/modules/catalog/category-service.ts +26 -5
  147. package/src/modules/catalog/schema.ts +2 -0
  148. package/src/modules/catalog/service.ts +25 -3
  149. package/src/modules/customers/repository/index.ts +55 -1
  150. package/src/modules/customers/schema.ts +19 -0
  151. package/src/modules/customers/service.ts +137 -2
  152. package/src/modules/inventory/schemas.ts +6 -1
  153. package/src/modules/inventory/service.ts +84 -29
  154. package/src/modules/media/noop-adapter.ts +36 -0
  155. package/src/modules/orders/repository/index.ts +63 -1
  156. package/src/modules/orders/service.ts +86 -3
  157. package/src/modules/payments/repository/index.ts +0 -1
  158. package/src/modules/promotions/schemas.ts +21 -4
  159. package/src/modules/promotions/service.ts +90 -18
  160. package/src/modules/search/repository/index.ts +0 -1
  161. package/src/modules/shipping/repository/index.ts +0 -1
  162. package/src/modules/tax/repository/index.ts +0 -1
  163. package/src/runtime/client-ip.ts +49 -0
  164. package/src/runtime/server.ts +39 -18
  165. package/src/test-utils/create-plugin-test-app.ts +3 -1
  166. package/src/test-utils/test-actors.ts +3 -2
@@ -5,6 +5,7 @@ import { csrf } from "hono/csrf";
5
5
  import { bodyLimit } from "hono/body-limit";
6
6
  import { rateLimiter } from "hono-rate-limiter";
7
7
  import { createHash } from "node:crypto";
8
+ import { createClientIpResolver } from "./client-ip.js";
8
9
  import type { Actor } from "../auth/types.js";
9
10
  import type { AuthInstance } from "../auth/setup.js";
10
11
  import type { CommerceConfig } from "../config/types.js";
@@ -90,14 +91,14 @@ export async function createServer(config: CommerceConfig) {
90
91
  throw new Error(
91
92
  "FATAL: auth.enableDevKey has been removed. " +
92
93
  "Use 'bunx @porulle/cli api-key create --scope admin' to generate a real API key. " +
93
- "See RFC-050: https://github.com/octalpixel/unified-commerce/blob/main/RFC-050-AUTH-KEY-SCOPING.md",
94
+ "See https://github.com/asyncdotengineering/porulle/blob/main/SECURITY.md for the auth/key scoping rationale.",
94
95
  );
95
96
  }
96
97
  if ((config.auth as Record<string, unknown>)?.devKey !== undefined) {
97
98
  throw new Error(
98
99
  "FATAL: auth.devKey has been removed. " +
99
100
  "Use 'bunx @porulle/cli api-key create --scope admin' to generate a real API key. " +
100
- "See RFC-050: https://github.com/octalpixel/unified-commerce/blob/main/RFC-050-AUTH-KEY-SCOPING.md",
101
+ "See https://github.com/asyncdotengineering/porulle/blob/main/SECURITY.md for the auth/key scoping rationale.",
101
102
  );
102
103
  }
103
104
 
@@ -184,28 +185,35 @@ export async function createServer(config: CommerceConfig) {
184
185
  }));
185
186
 
186
187
  // ─── Body Size Limit (F6) ──────────────────────────────────────────
187
- app.use("*", bodyLimit({
188
+ // Media uploads (phone photos are 3–8MB) get their own larger limit and are
189
+ // exempt from the global 1MB limit. Everything else stays at 1MB.
190
+ const mediaMaxUploadSize = config.media?.maxUploadSize ?? 10 * 1024 * 1024;
191
+ const MEDIA_UPLOAD_PATH = "/api/media/upload";
192
+
193
+ app.use(MEDIA_UPLOAD_PATH, bodyLimit({
194
+ maxSize: mediaMaxUploadSize,
195
+ onError: (c) => c.json({
196
+ error: { code: "FILE_TOO_LARGE", message: `Upload exceeds the ${mediaMaxUploadSize}-byte limit.` },
197
+ }, 413),
198
+ }));
199
+
200
+ const globalBodyLimit = bodyLimit({
188
201
  maxSize: 1024 * 1024, // 1 MB default
189
202
  onError: (c) => c.json({
190
203
  error: { code: "PAYLOAD_TOO_LARGE", message: "Request body exceeds 1MB limit." },
191
204
  }, 413),
192
- }));
205
+ });
206
+ app.use("*", (c, next) =>
207
+ c.req.path === MEDIA_UPLOAD_PATH ? next() : globalBodyLimit(c, next),
208
+ );
193
209
 
194
210
  // ─── Rate Limiting (F1) ──────────────────────────────────────────────
195
- // Trust X-Forwarded-For ONLY from a known reverse proxy IP.
196
- // Set TRUSTED_PROXY_IP env var to the proxy's IP (e.g., "127.0.0.1").
197
- const trustedProxyIp = process.env.TRUSTED_PROXY_IP;
211
+ // Client IP drives the rate-limit key. Defaults to the Node socket address
212
+ // (trusting X-Forwarded-For only from a known proxy via config.runtime
213
+ // .trustedProxyIp / TRUSTED_PROXY_IP); edge runtimes inject
214
+ // config.runtime.getClientIp to read the platform header instead.
198
215
  const signInEmailRateKeyCache = new WeakMap<Request, string>();
199
- const getClientIp = (c: { req: { raw: unknown; header: (name: string) => string | undefined } }): string => {
200
- const raw = c.req.raw as { socket?: { remoteAddress?: string } };
201
- const remoteAddress = raw.socket?.remoteAddress;
202
- // Only trust X-Forwarded-For when the direct connection is from a trusted proxy
203
- if (trustedProxyIp && remoteAddress === trustedProxyIp) {
204
- const xff = c.req.header("x-forwarded-for")?.split(",")[0]?.trim();
205
- if (xff) return xff;
206
- }
207
- return remoteAddress ?? "unknown";
208
- };
216
+ const getClientIp = createClientIpResolver(config);
209
217
  const keyGenerator = getClientIp;
210
218
 
211
219
  app.use("/api/auth/*", rateLimiter({
@@ -466,5 +474,18 @@ export async function createServer(config: CommerceConfig) {
466
474
  return dispatchFetch(rewritten as Request, env as never, executionCtx as never);
467
475
  }) as typeof app.fetch;
468
476
 
469
- return { app, kernel, logger, commerce };
477
+ // `runJobs` triggers one job-runner tick. On Cloudflare Workers, call it
478
+ // from `scheduled()` so cron triggers drive the queue without an in-process
479
+ // setInterval (which can't outlive a request on Workers):
480
+ //
481
+ // // wrangler.toml
482
+ // [triggers]
483
+ // crons = ["*/5 * * * *"]
484
+ //
485
+ // // worker.ts
486
+ // export default {
487
+ // fetch: (req, env, ctx) => server.app.fetch(req, env, ctx),
488
+ // scheduled: (_event, env, ctx) => ctx.waitUntil(server.runJobs()),
489
+ // };
490
+ return { app, kernel, logger, commerce, runJobs };
470
491
  }
@@ -26,6 +26,7 @@ import { createTestConfig } from "./create-test-config.js";
26
26
  import { createKernel } from "../runtime/kernel.js";
27
27
  import type { Kernel } from "../runtime/kernel.js";
28
28
  import { buildSchema } from "../kernel/database/migrate.js";
29
+ import { unwrapDb } from "../kernel/database/adapter.js";
29
30
  import { ensureDefaultOrg } from "../auth/org.js";
30
31
  import { mapErrorToStatus } from "../kernel/error-mapper.js";
31
32
 
@@ -93,7 +94,8 @@ export async function createPluginTestApp(
93
94
  };
94
95
  const { apply } = await drizzleKit.pushSchema(
95
96
  mergedSchema,
96
- kernel.database.db as PgDatabase<PgQueryResultHKT>,
97
+ // drizzle-kit needs the native driver result shape; unwrap the normalized db.
98
+ unwrapDb(kernel.database.db) as PgDatabase<PgQueryResultHKT>,
97
99
  );
98
100
  await apply();
99
101
 
@@ -3,8 +3,9 @@ import { DEFAULT_ORG_ID } from "../auth/org.js";
3
3
 
4
4
  /**
5
5
  * Organization ID used in test fixtures. Tests use the deprecated DEFAULT_ORG_ID
6
- * because ensureDefaultOrg() creates this org in test setup. When RFC-060 Phase 6
7
- * removes DEFAULT_ORG_ID, test setup will create a real org via Better Auth API.
6
+ * because ensureDefaultOrg() creates this org during test setup. When
7
+ * DEFAULT_ORG_ID is removed, test setup will create a real org via the
8
+ * Better Auth API instead.
8
9
  */
9
10
  export const TEST_ORG_ID = DEFAULT_ORG_ID;
10
11