@porulle/core 0.11.0 → 0.14.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.
- package/dist/auth/actor.d.ts +10 -0
- package/dist/auth/actor.d.ts.map +1 -0
- package/dist/auth/actor.js +91 -0
- package/dist/auth/auth-schema-guard.d.ts +2444 -0
- package/dist/auth/auth-schema-guard.d.ts.map +1 -0
- package/dist/auth/auth-schema-guard.js +113 -0
- package/dist/auth/auth-schema.d.ts +228 -0
- package/dist/auth/auth-schema.d.ts.map +1 -1
- package/dist/auth/auth-schema.js +24 -2
- package/dist/auth/middleware.d.ts +1 -0
- package/dist/auth/middleware.d.ts.map +1 -1
- package/dist/auth/middleware.js +16 -92
- package/dist/auth/org.d.ts +8 -2
- package/dist/auth/org.d.ts.map +1 -1
- package/dist/auth/org.js +12 -4
- package/dist/auth/organization-guard.d.ts +5 -0
- package/dist/auth/organization-guard.d.ts.map +1 -0
- package/dist/auth/organization-guard.js +124 -0
- package/dist/auth/permissions.d.ts +10 -0
- package/dist/auth/permissions.d.ts.map +1 -1
- package/dist/auth/permissions.js +31 -7
- package/dist/auth/role-authority.d.ts +44 -0
- package/dist/auth/role-authority.d.ts.map +1 -0
- package/dist/auth/role-authority.js +85 -0
- package/dist/auth/setup.d.ts.map +1 -1
- package/dist/auth/setup.js +19 -1
- package/dist/auth/strict-org-resolution.d.ts.map +1 -1
- package/dist/auth/strict-org-resolution.js +9 -1
- package/dist/auth/types.d.ts +1 -1
- package/dist/auth/types.d.ts.map +1 -1
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +2 -0
- package/dist/config/types.d.ts +32 -4
- package/dist/config/types.d.ts.map +1 -1
- package/dist/hooks/checkout.d.ts.map +1 -1
- package/dist/hooks/checkout.js +10 -9
- package/dist/index.d.ts +7 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -3
- package/dist/interfaces/rest/audit-middleware.d.ts.map +1 -1
- package/dist/interfaces/rest/audit-middleware.js +2 -0
- package/dist/interfaces/rest/customer-portal.d.ts.map +1 -1
- package/dist/interfaces/rest/customer-portal.js +9 -8
- package/dist/interfaces/rest/route-coverage.d.ts +14 -0
- package/dist/interfaces/rest/route-coverage.d.ts.map +1 -0
- package/dist/interfaces/rest/route-coverage.js +81 -0
- package/dist/interfaces/rest/router.d.ts +7 -2
- package/dist/interfaces/rest/router.d.ts.map +1 -1
- package/dist/interfaces/rest/router.js +8 -4
- package/dist/interfaces/rest/routes/admin/compensation-failures.d.ts.map +1 -1
- package/dist/interfaces/rest/routes/admin/compensation-failures.js +7 -5
- package/dist/interfaces/rest/routes/admin/custom-field-proposals.js +2 -2
- package/dist/interfaces/rest/routes/admin/permissions.d.ts +1 -1
- package/dist/interfaces/rest/routes/admin/permissions.d.ts.map +1 -1
- package/dist/interfaces/rest/routes/admin/permissions.js +2 -0
- package/dist/interfaces/rest/routes/admin/staff.d.ts.map +1 -1
- package/dist/interfaces/rest/routes/admin/staff.js +126 -66
- package/dist/interfaces/rest/routes/admin-jobs.js +3 -3
- package/dist/interfaces/rest/routes/audit.js +3 -3
- package/dist/interfaces/rest/routes/carts.d.ts.map +1 -1
- package/dist/interfaces/rest/routes/carts.js +13 -10
- package/dist/interfaces/rest/routes/catalog.d.ts.map +1 -1
- package/dist/interfaces/rest/routes/catalog.js +10 -2
- package/dist/interfaces/rest/routes/checkout.d.ts.map +1 -1
- package/dist/interfaces/rest/routes/checkout.js +30 -35
- package/dist/interfaces/rest/routes/documents.d.ts.map +1 -1
- package/dist/interfaces/rest/routes/documents.js +10 -5
- package/dist/interfaces/rest/routes/inventory.d.ts.map +1 -1
- package/dist/interfaces/rest/routes/inventory.js +2 -0
- package/dist/interfaces/rest/routes/media.d.ts.map +1 -1
- package/dist/interfaces/rest/routes/media.js +7 -3
- package/dist/interfaces/rest/routes/orders.d.ts.map +1 -1
- package/dist/interfaces/rest/routes/orders.js +26 -8
- package/dist/interfaces/rest/routes/pricing.js +2 -2
- package/dist/interfaces/rest/routes/promotions.d.ts.map +1 -1
- package/dist/interfaces/rest/routes/promotions.js +13 -10
- package/dist/interfaces/rest/routes/search.d.ts.map +1 -1
- package/dist/interfaces/rest/routes/search.js +2 -1
- package/dist/interfaces/rest/schemas/carts.d.ts +7 -0
- package/dist/interfaces/rest/schemas/carts.d.ts.map +1 -1
- package/dist/interfaces/rest/schemas/customer-portal.d.ts +35 -0
- package/dist/interfaces/rest/schemas/customer-portal.d.ts.map +1 -1
- package/dist/interfaces/rest/schemas/orders.d.ts +204 -0
- package/dist/interfaces/rest/schemas/orders.d.ts.map +1 -1
- package/dist/interfaces/rest/schemas/responses.d.ts +71 -0
- package/dist/interfaces/rest/schemas/responses.d.ts.map +1 -1
- package/dist/interfaces/rest/utils.d.ts +15 -0
- package/dist/interfaces/rest/utils.d.ts.map +1 -1
- package/dist/interfaces/rest/utils.js +45 -2
- package/dist/kernel/compensation/executor.js +2 -2
- package/dist/kernel/database/plugin-types.d.ts +11 -0
- package/dist/kernel/database/plugin-types.d.ts.map +1 -1
- package/dist/kernel/database/scoped-db.d.ts +12 -0
- package/dist/kernel/database/scoped-db.d.ts.map +1 -1
- package/dist/kernel/database/scoped-db.js +12 -0
- package/dist/kernel/database/tx-context.d.ts +8 -0
- package/dist/kernel/database/tx-context.d.ts.map +1 -1
- package/dist/kernel/database/tx-context.js +13 -0
- package/dist/kernel/hooks/create-context.d.ts +2 -0
- package/dist/kernel/hooks/create-context.d.ts.map +1 -1
- package/dist/kernel/hooks/create-context.js +1 -0
- package/dist/kernel/hooks/types.d.ts +2 -0
- package/dist/kernel/hooks/types.d.ts.map +1 -1
- package/dist/kernel/plugin/manifest.d.ts.map +1 -1
- package/dist/kernel/plugin/manifest.js +9 -5
- package/dist/modules/analytics/service.js +2 -2
- package/dist/modules/audit/service.js +3 -3
- package/dist/modules/cart/repository/index.d.ts +13 -7
- package/dist/modules/cart/repository/index.d.ts.map +1 -1
- package/dist/modules/cart/repository/index.js +16 -10
- package/dist/modules/cart/schema.d.ts +17 -0
- package/dist/modules/cart/schema.d.ts.map +1 -1
- package/dist/modules/cart/schema.js +4 -0
- package/dist/modules/cart/service.d.ts +22 -9
- package/dist/modules/cart/service.d.ts.map +1 -1
- package/dist/modules/cart/service.js +57 -33
- package/dist/modules/catalog/brand-service.js +7 -7
- package/dist/modules/catalog/category-service.js +7 -7
- package/dist/modules/catalog/entity-service.d.ts +14 -7
- package/dist/modules/catalog/entity-service.d.ts.map +1 -1
- package/dist/modules/catalog/entity-service.js +109 -31
- package/dist/modules/catalog/read-policy.d.ts +9 -0
- package/dist/modules/catalog/read-policy.d.ts.map +1 -0
- package/dist/modules/catalog/read-policy.js +8 -0
- package/dist/modules/catalog/repository/index.d.ts +3 -2
- package/dist/modules/catalog/repository/index.d.ts.map +1 -1
- package/dist/modules/catalog/repository/index.js +14 -4
- package/dist/modules/catalog/service.d.ts +15 -13
- package/dist/modules/catalog/service.d.ts.map +1 -1
- package/dist/modules/catalog/service.js +25 -18
- package/dist/modules/customers/module.d.ts.map +1 -1
- package/dist/modules/customers/module.js +1 -0
- package/dist/modules/customers/service.d.ts +2 -0
- package/dist/modules/customers/service.d.ts.map +1 -1
- package/dist/modules/customers/service.js +21 -20
- package/dist/modules/documents/module.d.ts.map +1 -1
- package/dist/modules/documents/module.js +1 -0
- package/dist/modules/documents/service.d.ts +18 -4
- package/dist/modules/documents/service.d.ts.map +1 -1
- package/dist/modules/documents/service.js +54 -16
- package/dist/modules/fulfillment/module.d.ts.map +1 -1
- package/dist/modules/fulfillment/module.js +1 -0
- package/dist/modules/fulfillment/service.d.ts +2 -0
- package/dist/modules/fulfillment/service.d.ts.map +1 -1
- package/dist/modules/fulfillment/service.js +15 -6
- package/dist/modules/inventory/service.d.ts.map +1 -1
- package/dist/modules/inventory/service.js +15 -14
- package/dist/modules/media/service.js +6 -6
- package/dist/modules/orders/guest-access.d.ts +27 -0
- package/dist/modules/orders/guest-access.d.ts.map +1 -0
- package/dist/modules/orders/guest-access.js +46 -0
- package/dist/modules/orders/module.d.ts.map +1 -1
- package/dist/modules/orders/module.js +1 -0
- package/dist/modules/orders/quote.d.ts.map +1 -1
- package/dist/modules/orders/quote.js +1 -0
- package/dist/modules/orders/repository/index.d.ts +1 -1
- package/dist/modules/orders/repository/index.d.ts.map +1 -1
- package/dist/modules/orders/repository/index.js +2 -2
- package/dist/modules/orders/schema.d.ts +17 -0
- package/dist/modules/orders/schema.d.ts.map +1 -1
- package/dist/modules/orders/schema.js +5 -3
- package/dist/modules/orders/service.d.ts +16 -7
- package/dist/modules/orders/service.d.ts.map +1 -1
- package/dist/modules/orders/service.js +184 -55
- package/dist/modules/pricing/module.d.ts.map +1 -1
- package/dist/modules/pricing/module.js +1 -0
- package/dist/modules/pricing/service.d.ts +2 -0
- package/dist/modules/pricing/service.d.ts.map +1 -1
- package/dist/modules/pricing/service.js +12 -11
- package/dist/modules/promotions/module.d.ts.map +1 -1
- package/dist/modules/promotions/module.js +1 -0
- package/dist/modules/promotions/service.d.ts +3 -0
- package/dist/modules/promotions/service.d.ts.map +1 -1
- package/dist/modules/promotions/service.js +19 -11
- package/dist/modules/search/adapter.d.ts +11 -0
- package/dist/modules/search/adapter.d.ts.map +1 -1
- package/dist/modules/search/module.d.ts.map +1 -1
- package/dist/modules/search/module.js +4 -1
- package/dist/modules/search/service.d.ts +1 -0
- package/dist/modules/search/service.d.ts.map +1 -1
- package/dist/modules/search/service.js +57 -6
- package/dist/modules/settings/module.d.ts.map +1 -1
- package/dist/modules/settings/module.js +1 -0
- package/dist/modules/settings/service.d.ts +3 -0
- package/dist/modules/settings/service.d.ts.map +1 -1
- package/dist/modules/settings/service.js +6 -4
- package/dist/modules/shipping/service.js +9 -9
- package/dist/modules/tax/module.d.ts.map +1 -1
- package/dist/modules/tax/module.js +1 -0
- package/dist/modules/tax/service.d.ts +3 -0
- package/dist/modules/tax/service.d.ts.map +1 -1
- package/dist/modules/tax/service.js +11 -9
- package/dist/modules/webhooks/hook.js +2 -2
- package/dist/modules/webhooks/module.d.ts.map +1 -1
- package/dist/modules/webhooks/module.js +1 -0
- package/dist/modules/webhooks/service.d.ts +2 -0
- package/dist/modules/webhooks/service.d.ts.map +1 -1
- package/dist/modules/webhooks/service.js +4 -4
- package/dist/runtime/server.d.ts.map +1 -1
- package/dist/runtime/server.js +28 -2
- package/dist/test-utils/create-pglite-adapter.d.ts.map +1 -1
- package/dist/test-utils/create-pglite-adapter.js +35 -13
- package/dist/test-utils/create-test-config.d.ts.map +1 -1
- package/dist/test-utils/create-test-config.js +4 -0
- package/dist/test-utils/rest-api-test-utils.d.ts.map +1 -1
- package/dist/test-utils/rest-api-test-utils.js +2 -0
- package/dist/test-utils/test-actors.js +2 -2
- package/dist/utils/id.d.ts +2 -0
- package/dist/utils/id.d.ts.map +1 -1
- package/dist/utils/id.js +11 -0
- package/package.json +6 -5
- package/src/auth/actor.ts +110 -0
- package/src/auth/auth-schema-guard.ts +143 -0
- package/src/auth/auth-schema.ts +50 -19
- package/src/auth/middleware.ts +25 -101
- package/src/auth/org.ts +15 -3
- package/src/auth/organization-guard.ts +158 -0
- package/src/auth/permissions.ts +31 -8
- package/src/auth/role-authority.ts +107 -0
- package/src/auth/setup.ts +19 -1
- package/src/auth/strict-org-resolution.ts +7 -1
- package/src/auth/types.ts +1 -1
- package/src/config/defaults.ts +2 -0
- package/src/config/types.ts +31 -4
- package/src/hooks/checkout.ts +19 -12
- package/src/index.ts +21 -6
- package/src/interfaces/rest/audit-middleware.ts +2 -0
- package/src/interfaces/rest/customer-portal.ts +31 -24
- package/src/interfaces/rest/route-coverage.ts +103 -0
- package/src/interfaces/rest/router.ts +16 -10
- package/src/interfaces/rest/routes/admin/compensation-failures.ts +8 -5
- package/src/interfaces/rest/routes/admin/custom-field-proposals.ts +2 -2
- package/src/interfaces/rest/routes/admin/permissions.ts +3 -1
- package/src/interfaces/rest/routes/admin/staff.ts +161 -72
- package/src/interfaces/rest/routes/admin-jobs.ts +3 -3
- package/src/interfaces/rest/routes/audit.ts +3 -3
- package/src/interfaces/rest/routes/carts.ts +17 -7
- package/src/interfaces/rest/routes/catalog.ts +11 -0
- package/src/interfaces/rest/routes/checkout.ts +41 -38
- package/src/interfaces/rest/routes/documents.ts +15 -1
- package/src/interfaces/rest/routes/inventory.ts +2 -0
- package/src/interfaces/rest/routes/media.ts +8 -3
- package/src/interfaces/rest/routes/orders.ts +27 -8
- package/src/interfaces/rest/routes/pricing.ts +2 -2
- package/src/interfaces/rest/routes/promotions.ts +12 -9
- package/src/interfaces/rest/routes/search.ts +3 -1
- package/src/interfaces/rest/utils.ts +61 -2
- package/src/kernel/compensation/executor.ts +2 -2
- package/src/kernel/database/plugin-types.ts +11 -0
- package/src/kernel/database/scoped-db.ts +12 -0
- package/src/kernel/database/tx-context.ts +25 -0
- package/src/kernel/hooks/create-context.ts +3 -0
- package/src/kernel/hooks/types.ts +2 -0
- package/src/kernel/plugin/manifest.ts +9 -5
- package/src/modules/analytics/service.ts +2 -2
- package/src/modules/audit/service.ts +3 -3
- package/src/modules/cart/repository/index.ts +22 -8
- package/src/modules/cart/schema.ts +4 -0
- package/src/modules/cart/service.ts +62 -27
- package/src/modules/catalog/brand-service.ts +7 -7
- package/src/modules/catalog/category-service.ts +7 -7
- package/src/modules/catalog/entity-service.ts +117 -35
- package/src/modules/catalog/read-policy.ts +16 -0
- package/src/modules/catalog/repository/index.ts +19 -3
- package/src/modules/catalog/service.ts +46 -31
- package/src/modules/customers/module.ts +2 -0
- package/src/modules/customers/service.ts +23 -22
- package/src/modules/documents/module.ts +2 -0
- package/src/modules/documents/service.ts +74 -11
- package/src/modules/fulfillment/module.ts +2 -0
- package/src/modules/fulfillment/service.ts +17 -6
- package/src/modules/inventory/service.ts +15 -24
- package/src/modules/media/service.ts +6 -6
- package/src/modules/orders/guest-access.ts +55 -0
- package/src/modules/orders/module.ts +1 -0
- package/src/modules/orders/quote.ts +1 -0
- package/src/modules/orders/repository/index.ts +2 -0
- package/src/modules/orders/schema.ts +5 -3
- package/src/modules/orders/service.ts +302 -52
- package/src/modules/pricing/module.ts +2 -0
- package/src/modules/pricing/service.ts +14 -16
- package/src/modules/promotions/module.ts +2 -0
- package/src/modules/promotions/service.ts +25 -16
- package/src/modules/search/adapter.ts +9 -0
- package/src/modules/search/module.ts +5 -1
- package/src/modules/search/service.ts +55 -6
- package/src/modules/settings/module.ts +2 -0
- package/src/modules/settings/service.ts +8 -4
- package/src/modules/shipping/service.ts +9 -9
- package/src/modules/tax/module.ts +1 -0
- package/src/modules/tax/service.ts +13 -9
- package/src/modules/webhooks/hook.ts +2 -2
- package/src/modules/webhooks/module.ts +2 -0
- package/src/modules/webhooks/service.ts +6 -4
- package/src/runtime/server.ts +41 -2
- package/src/test-utils/create-pglite-adapter.ts +34 -12
- package/src/test-utils/create-test-config.ts +10 -6
- package/src/test-utils/rest-api-test-utils.ts +2 -0
- package/src/test-utils/test-actors.ts +2 -2
- package/src/utils/id.ts +20 -0
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { Actor } from "../../auth/types.js";
|
|
2
|
+
import type { CommerceConfig } from "../../config/types.js";
|
|
2
3
|
import { type Result } from "../../kernel/result.js";
|
|
3
4
|
import type { TxContext } from "../../kernel/database/tx-context.js";
|
|
4
5
|
import type { WebhooksRepository, WebhookEndpoint } from "./repository/index.js";
|
|
5
6
|
interface WebhookServiceDeps {
|
|
6
7
|
repository: WebhooksRepository;
|
|
8
|
+
config: CommerceConfig;
|
|
7
9
|
}
|
|
8
10
|
export declare class WebhookService {
|
|
9
11
|
private deps;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/modules/webhooks/service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/modules/webhooks/service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qCAAqC,CAAC;AACrE,OAAO,KAAK,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AA4CjF,UAAU,kBAAkB;IAC1B,UAAU,EAAE,kBAAkB,CAAC;IAC/B,MAAM,EAAE,cAAc,CAAC;CACxB;AAED,qBAAa,cAAc;IAGb,OAAO,CAAC,IAAI;IAFxB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAqB;gBAEtB,IAAI,EAAE,kBAAkB;IAItC,cAAc,CAClB,KAAK,EAAE;QACL,GAAG,EAAE,MAAM,CAAC;QACZ,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,EAAE,CAAC;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACpC,EACD,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,EACpB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;IAyB7B,aAAa,CACjB,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,EACpB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC,CAAC;IAM/B,cAAc,CAClB,EAAE,EAAE,MAAM,EACV,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,EACpB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAWlB,oBAAoB,CACxB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC,CAAC;CAItC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { resolveOrgIdForCommerce } from "../../auth/org.js";
|
|
2
2
|
import { CommerceNotFoundError, CommerceValidationError } from "../../kernel/errors.js";
|
|
3
3
|
import { Err, Ok } from "../../kernel/result.js";
|
|
4
4
|
/**
|
|
@@ -62,7 +62,7 @@ export class WebhookService {
|
|
|
62
62
|
if (isPrivateUrl(input.url)) {
|
|
63
63
|
return Err(new CommerceValidationError("Webhook URL must not point to a private or internal address."));
|
|
64
64
|
}
|
|
65
|
-
const orgId =
|
|
65
|
+
const orgId = resolveOrgIdForCommerce(actor ?? ctx?.actor ?? null, this.deps.config);
|
|
66
66
|
const endpoint = await this.repo.createEndpoint({
|
|
67
67
|
organizationId: orgId,
|
|
68
68
|
url: input.url,
|
|
@@ -74,12 +74,12 @@ export class WebhookService {
|
|
|
74
74
|
return Ok(endpoint);
|
|
75
75
|
}
|
|
76
76
|
async listEndpoints(actor, ctx) {
|
|
77
|
-
const orgId =
|
|
77
|
+
const orgId = resolveOrgIdForCommerce(actor ?? ctx?.actor ?? null, this.deps.config);
|
|
78
78
|
const endpoints = await this.repo.findAllEndpoints(orgId, ctx);
|
|
79
79
|
return Ok(endpoints);
|
|
80
80
|
}
|
|
81
81
|
async deleteEndpoint(id, actor, ctx) {
|
|
82
|
-
const orgId =
|
|
82
|
+
const orgId = resolveOrgIdForCommerce(actor ?? ctx?.actor ?? null, this.deps.config);
|
|
83
83
|
const existing = await this.repo.findEndpointById(id, orgId, ctx);
|
|
84
84
|
if (!existing) {
|
|
85
85
|
return Err(new CommerceNotFoundError("Webhook endpoint not found."));
|
|
@@ -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;AAQhD,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;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/runtime/server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAQhD,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;AASzD,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAkB,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAOtE,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;;;;;;;GAOG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAcvC;AAeD;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,cAAc;;;;;sBAwWxB,MAAM,UAAU,MAAM;GA8GtD"}
|
package/dist/runtime/server.js
CHANGED
|
@@ -7,12 +7,16 @@ import { rateLimiter } from "hono-rate-limiter";
|
|
|
7
7
|
import { createHash } from "node:crypto";
|
|
8
8
|
import { createClientIpResolver } from "./client-ip.js";
|
|
9
9
|
import { authMiddleware } from "../auth/middleware.js";
|
|
10
|
+
import { organizationGuard } from "../auth/organization-guard.js";
|
|
10
11
|
import { CommerceCsrfError } from "../kernel/errors.js";
|
|
11
12
|
import { createRestRoutes } from "../interfaces/rest/index.js";
|
|
12
13
|
import { createCustomerPortalRoutes } from "../interfaces/rest/customer-portal.js";
|
|
13
14
|
import { createCommerce } from "./commerce.js";
|
|
14
15
|
import { rewriteCommerceAliasRequest } from "./url-alias-rewrite.js";
|
|
15
16
|
import { mapErrorToResponse } from "../kernel/error-mapper.js";
|
|
17
|
+
import { assertRouteCoverage } from "../interfaces/rest/route-coverage.js";
|
|
18
|
+
import { markRoutePermissionGuard } from "../interfaces/rest/utils.js";
|
|
19
|
+
import { isStrictOrgResolution } from "../auth/strict-org-resolution.js";
|
|
16
20
|
/**
|
|
17
21
|
* Returns true if running in a Node-like runtime that supports
|
|
18
22
|
* `process.on()` + `process.exit()`. Edge runtimes (Cloudflare Workers,
|
|
@@ -209,10 +213,16 @@ export async function createServer(config) {
|
|
|
209
213
|
const signInEmailRateKeyCache = new WeakMap();
|
|
210
214
|
const getClientIp = createClientIpResolver(config);
|
|
211
215
|
const keyGenerator = getClientIp;
|
|
216
|
+
app.use("/api/auth/get-session", rateLimiter({
|
|
217
|
+
windowMs: 60 * 1000,
|
|
218
|
+
limit: config.rateLimits?.session ?? 120,
|
|
219
|
+
keyGenerator,
|
|
220
|
+
}));
|
|
212
221
|
app.use("/api/auth/*", rateLimiter({
|
|
213
222
|
windowMs: 60 * 1000,
|
|
214
223
|
limit: config.rateLimits?.auth ?? 10,
|
|
215
224
|
keyGenerator,
|
|
225
|
+
skip: (c) => c.req.path === "/api/auth/get-session",
|
|
216
226
|
}));
|
|
217
227
|
app.use("/api/auth/sign-in/email", async (c, next) => {
|
|
218
228
|
if (c.req.method !== "POST") {
|
|
@@ -265,6 +275,10 @@ export async function createServer(config) {
|
|
|
265
275
|
}
|
|
266
276
|
}
|
|
267
277
|
// ─── Auth ──────────────────────────────────────────────────────────
|
|
278
|
+
// Better Auth's organization endpoints are mounted below but governed here:
|
|
279
|
+
// `admin/staff.ts` owns membership, and this guard keeps the plugin's own
|
|
280
|
+
// paths from minting members outside those rules.
|
|
281
|
+
app.use("/api/auth/organization/*", organizationGuard(kernel.database.db, config));
|
|
268
282
|
app.use("/api/auth/*", async (c, next) => {
|
|
269
283
|
if (c.req.path.startsWith("/api/auth/pos/")) {
|
|
270
284
|
await next();
|
|
@@ -367,7 +381,7 @@ export async function createServer(config) {
|
|
|
367
381
|
// Strategy 1: Built-in cron endpoint for serverless deployments.
|
|
368
382
|
// Point Vercel Cron or Cloudflare Cron Trigger at GET /api/jobs/run
|
|
369
383
|
// Optional query params: ?queue=emails&limit=20
|
|
370
|
-
app.get("/api/jobs/run", async (c) => {
|
|
384
|
+
app.get("/api/jobs/run", markRoutePermissionGuard(async (c) => {
|
|
371
385
|
// Always require admin — cron triggers must authenticate
|
|
372
386
|
const actor = c.get("actor");
|
|
373
387
|
if (!actor?.permissions?.includes("*:*")) {
|
|
@@ -383,7 +397,7 @@ export async function createServer(config) {
|
|
|
383
397
|
logger.error({ err }, "Job runner endpoint failed");
|
|
384
398
|
return c.json({ error: { code: "INTERNAL_ERROR", message: "Job processing failed" } }, 500);
|
|
385
399
|
}
|
|
386
|
-
});
|
|
400
|
+
}));
|
|
387
401
|
// Strategy 2: In-process polling for long-running servers (ECS, Cloud Run, Docker).
|
|
388
402
|
// Enable via config.jobs.autorun.enabled = true
|
|
389
403
|
if (config.jobs?.autorun?.enabled) {
|
|
@@ -413,6 +427,18 @@ export async function createServer(config) {
|
|
|
413
427
|
"or set config.jobs.autorun.enabled = true for in-process polling.");
|
|
414
428
|
}
|
|
415
429
|
}
|
|
430
|
+
const defaultOrganizationId = config.auth?.defaultOrganizationId;
|
|
431
|
+
const hasDefaultOrganization = typeof defaultOrganizationId === "string" && defaultOrganizationId.trim().length > 0;
|
|
432
|
+
if (isStrictOrgResolution(config) &&
|
|
433
|
+
!hasDefaultOrganization &&
|
|
434
|
+
!config.auth?.storeResolver) {
|
|
435
|
+
throw new Error("Cannot resolve an organization for a request with no actor. Set " +
|
|
436
|
+
"auth.defaultOrganizationId for a single-tenant deployment, or " +
|
|
437
|
+
"auth.storeResolver for a multi-tenant one. To keep the previous " +
|
|
438
|
+
"permissive behaviour during a migration, set " +
|
|
439
|
+
"auth.strictOrgResolution: false (or STRICT_ORG_RESOLUTION=false).");
|
|
440
|
+
}
|
|
441
|
+
assertRouteCoverage(app);
|
|
416
442
|
const dispatchFetch = app.fetch.bind(app);
|
|
417
443
|
app.fetch = ((input, env, executionCtx) => {
|
|
418
444
|
const req = input instanceof Request ? input : new Request(input);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-pglite-adapter.d.ts","sourceRoot":"","sources":["../../src/test-utils/create-pglite-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAMrE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AA0BxE;;;;;;;;;;GAUG;AACH,wBAAsB,uBAAuB,IAAI,OAAO,CAAC;IACvD,OAAO,EAAE,eAAe,CAAC;IACzB,EAAE,EAAE,eAAe,CAAC;IACpB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B,CAAC,
|
|
1
|
+
{"version":3,"file":"create-pglite-adapter.d.ts","sourceRoot":"","sources":["../../src/test-utils/create-pglite-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAMrE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AA0BxE;;;;;;;;;;GAUG;AACH,wBAAsB,uBAAuB,IAAI,OAAO,CAAC;IACvD,OAAO,EAAE,eAAe,CAAC;IACzB,EAAE,EAAE,eAAe,CAAC;IACpB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B,CAAC,CAqFD"}
|
|
@@ -62,21 +62,43 @@ export async function createPGliteTestAdapter() {
|
|
|
62
62
|
/**
|
|
63
63
|
* PGlite-compatible transaction wrapper.
|
|
64
64
|
*
|
|
65
|
-
* Drizzle's transaction() method can hang with PGlite due to how
|
|
66
|
-
*
|
|
67
|
-
*
|
|
65
|
+
* Drizzle's transaction() method can hang with PGlite due to how the adapter
|
|
66
|
+
* manages transaction state, so this drives BEGIN/COMMIT manually.
|
|
67
|
+
*
|
|
68
|
+
* PGlite is a single connection, so two transaction bodies awaiting
|
|
69
|
+
* concurrently would otherwise interleave their statements between one
|
|
70
|
+
* BEGIN and one COMMIT — the second BEGIN is a no-op Postgres only warns
|
|
71
|
+
* about. Merged that way, two callers share a snapshot and hold each other's
|
|
72
|
+
* row locks, so an isolation bug reads as if the guard failed. Bodies are
|
|
73
|
+
* queued instead, which is what a connection pool would do to two
|
|
74
|
+
* transactions competing for the same rows.
|
|
75
|
+
*
|
|
76
|
+
* A nested call joins the transaction already open rather than deadlocking
|
|
77
|
+
* on the queue; without savepoints that is the only sound reading.
|
|
68
78
|
*/
|
|
79
|
+
let transactionQueue = Promise.resolve();
|
|
80
|
+
let inTransaction = false;
|
|
69
81
|
async function transaction(fn) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
82
|
+
if (inTransaction)
|
|
83
|
+
return fn(db);
|
|
84
|
+
const run = transactionQueue.then(async () => {
|
|
85
|
+
inTransaction = true;
|
|
86
|
+
await pg.exec("BEGIN");
|
|
87
|
+
try {
|
|
88
|
+
const result = await fn(db);
|
|
89
|
+
await pg.exec("COMMIT");
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
await pg.exec("ROLLBACK");
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
finally {
|
|
97
|
+
inTransaction = false;
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
transactionQueue = run.catch(() => undefined);
|
|
101
|
+
return run;
|
|
80
102
|
}
|
|
81
103
|
const adapter = {
|
|
82
104
|
provider: "postgresql",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-test-config.d.ts","sourceRoot":"","sources":["../../src/test-utils/create-test-config.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAgDzD,wBAAsB,gBAAgB,CACpC,SAAS,GAAE,OAAO,CAAC,cAAc,CAAM,GACtC,OAAO,CAAC,cAAc,CAAC,
|
|
1
|
+
{"version":3,"file":"create-test-config.d.ts","sourceRoot":"","sources":["../../src/test-utils/create-test-config.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAgDzD,wBAAsB,gBAAgB,CACpC,SAAS,GAAE,OAAO,CAAC,cAAc,CAAM,GACtC,OAAO,CAAC,cAAc,CAAC,CAqHzB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,sBAAsB,CAC1C,SAAS,GAAE,OAAO,CAAC,cAAc,CAAM,GACtC,OAAO,CAAC;IAAE,MAAM,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC,CAUnE"}
|
|
@@ -67,11 +67,13 @@ export async function createTestConfig(overrides = {}) {
|
|
|
67
67
|
"catalog:update",
|
|
68
68
|
"catalog:delete",
|
|
69
69
|
"catalog:read",
|
|
70
|
+
"catalog:read:unpublished",
|
|
70
71
|
"inventory:adjust",
|
|
71
72
|
"orders:create",
|
|
72
73
|
"orders:read",
|
|
73
74
|
"orders:update",
|
|
74
75
|
"orders:manage",
|
|
76
|
+
"orders:create:on-behalf",
|
|
75
77
|
"cart:create",
|
|
76
78
|
"cart:update",
|
|
77
79
|
"customers:update:self",
|
|
@@ -80,10 +82,12 @@ export async function createTestConfig(overrides = {}) {
|
|
|
80
82
|
ai_agent: {
|
|
81
83
|
permissions: [
|
|
82
84
|
"catalog:read",
|
|
85
|
+
"catalog:read:unpublished",
|
|
83
86
|
"catalog:create",
|
|
84
87
|
"inventory:read",
|
|
85
88
|
"inventory:adjust",
|
|
86
89
|
"orders:read",
|
|
90
|
+
"orders:create:on-behalf",
|
|
87
91
|
"cart:create",
|
|
88
92
|
"cart:update",
|
|
89
93
|
],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rest-api-test-utils.d.ts","sourceRoot":"","sources":["../../src/test-utils/rest-api-test-utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAOpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,KAAK,SAAS,GAAG;IACf,SAAS,EAAE;QACT,IAAI,EAAE,YAAY,CAAC;QACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;KACrB,CAAC;CACH,CAAC;AAiDF;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,SAAS,GAAE,OAAO,CAAC,cAAc,CAAM,GACtC,OAAO,CAAC;IACT,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IACxB,MAAM,EAAE,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;IACxC,IAAI,EAAE,YAAY,CAAC;IACnB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B,CAAC,CAqDD;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,CAAC,GAAG,OAAO,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,CAEnF;AAED;;GAEG;AACH,eAAO,MAAM,SAAS,EAAE,
|
|
1
|
+
{"version":3,"file":"rest-api-test-utils.d.ts","sourceRoot":"","sources":["../../src/test-utils/rest-api-test-utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAOpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,KAAK,SAAS,GAAG;IACf,SAAS,EAAE;QACT,IAAI,EAAE,YAAY,CAAC;QACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;KACrB,CAAC;CACH,CAAC;AAiDF;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,SAAS,GAAE,OAAO,CAAC,cAAc,CAAM,GACtC,OAAO,CAAC;IACT,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IACxB,MAAM,EAAE,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;IACxC,IAAI,EAAE,YAAY,CAAC;IACnB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B,CAAC,CAqDD;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,CAAC,GAAG,OAAO,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,CAEnF;AAED;;GAEG;AACH,eAAO,MAAM,SAAS,EAAE,KAsCvB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,KAS3B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,WAAW,EAAE,KASzB,CAAC;AAEF;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE;IAClE,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,KAAK,CAAC;CACf,WAwBA;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,EACvB,OAAO,EAAE;IACP,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,KAAK,CAAC;CACf,qBAYF"}
|
|
@@ -125,9 +125,11 @@ export const testActor = {
|
|
|
125
125
|
"catalog:create",
|
|
126
126
|
"catalog:update",
|
|
127
127
|
"catalog:read",
|
|
128
|
+
"catalog:read:unpublished",
|
|
128
129
|
"inventory:adjust",
|
|
129
130
|
"inventory:read",
|
|
130
131
|
"orders:create",
|
|
132
|
+
"orders:create:on-behalf",
|
|
131
133
|
"orders:read",
|
|
132
134
|
"orders:update",
|
|
133
135
|
"orders:manage",
|
|
@@ -27,8 +27,8 @@ export const testStaffActor = {
|
|
|
27
27
|
organizationId: TEST_ORG_ID,
|
|
28
28
|
role: "staff",
|
|
29
29
|
permissions: [
|
|
30
|
-
"catalog:read", "catalog:create", "catalog:update",
|
|
31
|
-
"inventory:adjust", "orders:read", "orders:create", "orders:update", "orders:manage",
|
|
30
|
+
"catalog:read", "catalog:read:unpublished", "catalog:create", "catalog:update",
|
|
31
|
+
"inventory:adjust", "orders:read", "orders:create", "orders:create:on-behalf", "orders:update", "orders:manage",
|
|
32
32
|
],
|
|
33
33
|
};
|
|
34
34
|
/** Customer with minimal read/write-own permissions. */
|
package/dist/utils/id.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { Actor } from "../auth/types.js";
|
|
1
2
|
export declare function makeId(): string;
|
|
2
3
|
export declare function makeDeterministicId(value: string): Promise<string>;
|
|
4
|
+
export declare function makeIdempotencyScope(actor: Actor | null | undefined, guestCredential?: string, fallbackBinding?: string): Promise<string>;
|
|
3
5
|
//# sourceMappingURL=id.d.ts.map
|
package/dist/utils/id.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"id.d.ts","sourceRoot":"","sources":["../../src/utils/id.ts"],"names":[],"mappings":"AAAA,wBAAgB,MAAM,IAAI,MAAM,CAE/B;AAED,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CASxE"}
|
|
1
|
+
{"version":3,"file":"id.d.ts","sourceRoot":"","sources":["../../src/utils/id.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAE9C,wBAAgB,MAAM,IAAI,MAAM,CAE/B;AAED,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CASxE;AAED,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,KAAK,GAAG,IAAI,GAAG,SAAS,EAC/B,eAAe,CAAC,EAAE,MAAM,EACxB,eAAe,CAAC,EAAE,MAAM,GACvB,OAAO,CAAC,MAAM,CAAC,CAYjB"}
|
package/dist/utils/id.js
CHANGED
|
@@ -9,3 +9,14 @@ export async function makeDeterministicId(value) {
|
|
|
9
9
|
const hex = [...bytes].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
10
10
|
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
11
11
|
}
|
|
12
|
+
export async function makeIdempotencyScope(actor, guestCredential, fallbackBinding) {
|
|
13
|
+
const binding = actor?.userId
|
|
14
|
+
? `actor:${actor.type}:${actor.userId}`
|
|
15
|
+
: guestCredential
|
|
16
|
+
? `guest:${guestCredential}`
|
|
17
|
+
: fallbackBinding
|
|
18
|
+
? `resource:${fallbackBinding}`
|
|
19
|
+
: "anonymous";
|
|
20
|
+
const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(binding)));
|
|
21
|
+
return [...digest].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
22
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@porulle/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -36,11 +36,11 @@
|
|
|
36
36
|
}
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@better-auth/api-key": "^1.
|
|
40
|
-
"@better-auth/drizzle-adapter": "^1.
|
|
39
|
+
"@better-auth/api-key": "^1.7.1",
|
|
40
|
+
"@better-auth/drizzle-adapter": "^1.7.1",
|
|
41
41
|
"@hono/zod-openapi": "^1.2.2",
|
|
42
42
|
"@scalar/hono-api-reference": "^0.10.5",
|
|
43
|
-
"better-auth": "^1.
|
|
43
|
+
"better-auth": "^1.7.1",
|
|
44
44
|
"drizzle-orm": "^0.45.1",
|
|
45
45
|
"drizzle-zod": "^0.8.3",
|
|
46
46
|
"hono": "^4.9.8",
|
|
@@ -81,7 +81,8 @@
|
|
|
81
81
|
"scripts": {
|
|
82
82
|
"generate:plugins": "node ./scripts/generate-plugin-types.mjs",
|
|
83
83
|
"check:store-guard": "node ./scripts/check-store-guard.mjs",
|
|
84
|
-
"
|
|
84
|
+
"check:auth-schema": "vitest run test/auth-schema-better-auth-parity.test.ts",
|
|
85
|
+
"build": "rm -rf dist tsconfig.build.tsbuildinfo && npm run generate:plugins && npm run check:store-guard && npm run check:auth-schema && tsc -p tsconfig.build.json",
|
|
85
86
|
"check-types": "npm run generate:plugins && npm run check:store-guard && tsc --noEmit",
|
|
86
87
|
"lint": "eslint . --max-warnings 1000",
|
|
87
88
|
"test": "vitest run"
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type { AuthSessionLike, CommerceConfig } from "../config/types.js";
|
|
2
|
+
import type { Actor } from "./types.js";
|
|
3
|
+
import type { AuthInstance } from "./setup.js";
|
|
4
|
+
import { DEFAULT_ORG_ID } from "./org.js";
|
|
5
|
+
|
|
6
|
+
export const AUTH_COOKIE_PREFIX = "uc";
|
|
7
|
+
export const SESSION_COOKIE_NAME = `${AUTH_COOKIE_PREFIX}.session_token`;
|
|
8
|
+
|
|
9
|
+
export const DEFAULT_CUSTOMER_PERMISSIONS = [
|
|
10
|
+
"catalog:read",
|
|
11
|
+
"cart:create",
|
|
12
|
+
"cart:read",
|
|
13
|
+
"cart:update",
|
|
14
|
+
"orders:create",
|
|
15
|
+
"orders:read:own",
|
|
16
|
+
"customers:read:self",
|
|
17
|
+
"customers:update:self",
|
|
18
|
+
] as const;
|
|
19
|
+
|
|
20
|
+
export function getCustomerPermissions(config: CommerceConfig): string[] {
|
|
21
|
+
return config.auth?.customerPermissions ?? [...DEFAULT_CUSTOMER_PERMISSIONS];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function resolvePermissions(
|
|
25
|
+
session: AuthSessionLike,
|
|
26
|
+
config: CommerceConfig,
|
|
27
|
+
): string[] {
|
|
28
|
+
const role = session.session.activeOrganizationRole;
|
|
29
|
+
if (!role) return getCustomerPermissions(config);
|
|
30
|
+
const roleConfig = config.auth?.roles?.[role];
|
|
31
|
+
return roleConfig ? roleConfig.permissions : [];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Resolve a better-auth session and its porulle organization permissions. */
|
|
35
|
+
export async function resolveActor(
|
|
36
|
+
headers: Headers,
|
|
37
|
+
auth: AuthInstance,
|
|
38
|
+
config: CommerceConfig,
|
|
39
|
+
request: Request = new Request("http://localhost", { headers }),
|
|
40
|
+
): Promise<Actor | null> {
|
|
41
|
+
let session: AuthSessionLike | null;
|
|
42
|
+
try {
|
|
43
|
+
session = (await auth.api.getSession({
|
|
44
|
+
headers,
|
|
45
|
+
})) as AuthSessionLike | null;
|
|
46
|
+
} catch {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!session) return null;
|
|
51
|
+
|
|
52
|
+
const defaultOrgId = config.auth?.defaultOrganizationId ?? DEFAULT_ORG_ID;
|
|
53
|
+
let role = session.session.activeOrganizationRole as string | undefined;
|
|
54
|
+
let orgId = session.session.activeOrganizationId as string | null;
|
|
55
|
+
|
|
56
|
+
if (!role && auth.api.getFullOrganization) {
|
|
57
|
+
try {
|
|
58
|
+
const org = await auth.api.getFullOrganization({
|
|
59
|
+
query: { organizationId: orgId ?? defaultOrgId },
|
|
60
|
+
headers,
|
|
61
|
+
});
|
|
62
|
+
if (org?.members) {
|
|
63
|
+
const membership = org.members.find(
|
|
64
|
+
(m) => m.userId === session.user.id,
|
|
65
|
+
);
|
|
66
|
+
if (membership) {
|
|
67
|
+
role = membership.role;
|
|
68
|
+
orgId = orgId ?? defaultOrgId;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
} catch {
|
|
72
|
+
// fall through — treat as customer
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (!role && orgId && auth.api.getActiveMemberRole) {
|
|
77
|
+
try {
|
|
78
|
+
const roleResult = await auth.api.getActiveMemberRole({ headers });
|
|
79
|
+
role = (roleResult as Record<string, unknown>)?.role as
|
|
80
|
+
| string
|
|
81
|
+
| undefined;
|
|
82
|
+
} catch {
|
|
83
|
+
// fall through — treat as customer
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (!orgId && config.auth?.storeResolver) {
|
|
88
|
+
try {
|
|
89
|
+
const resolved = await config.auth.storeResolver(request);
|
|
90
|
+
if (resolved) orgId = resolved;
|
|
91
|
+
} catch {
|
|
92
|
+
// fall through — use defaultOrgId
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const enrichedSession = {
|
|
97
|
+
...session,
|
|
98
|
+
session: { ...session.session, activeOrganizationRole: role ?? null },
|
|
99
|
+
};
|
|
100
|
+
return {
|
|
101
|
+
type: "user",
|
|
102
|
+
userId: session.user.id,
|
|
103
|
+
email: session.user.email ?? null,
|
|
104
|
+
name: session.user.name ?? "User",
|
|
105
|
+
vendorId: session.user.vendorId ?? null,
|
|
106
|
+
organizationId: orgId ?? defaultOrgId,
|
|
107
|
+
role: role ?? "customer",
|
|
108
|
+
permissions: resolvePermissions(enrichedSession, config),
|
|
109
|
+
} satisfies Actor;
|
|
110
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compares Porulle's Drizzle auth tables against better-auth's canonical
|
|
3
|
+
* {@link getAuthTables} output so a dependency minor bump cannot add a column
|
|
4
|
+
* the declared schema omits.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { getTableColumns, getTableName } from "drizzle-orm";
|
|
8
|
+
import type { PgColumn, PgTable } from "drizzle-orm/pg-core";
|
|
9
|
+
import { getAuthTables } from "better-auth/db";
|
|
10
|
+
import type { BetterAuthOptions } from "better-auth";
|
|
11
|
+
import { apiKey } from "@better-auth/api-key";
|
|
12
|
+
import { organization, twoFactor, phoneNumber, jwt, bearer } from "better-auth/plugins";
|
|
13
|
+
import * as authSchema from "./auth-schema.js";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Every plugin {@link createAuth} can enable, not only the ones it enables by
|
|
17
|
+
* default. The shipped schema has to satisfy any configuration a merchant
|
|
18
|
+
* chooses, so the guard compares against the maximal table set: a column that
|
|
19
|
+
* only `twoFactor` needs is still a column this package must declare.
|
|
20
|
+
*/
|
|
21
|
+
export const AUTH_SCHEMA_GUARD_OPTIONS = {
|
|
22
|
+
plugins: [
|
|
23
|
+
organization({ roles: {} }),
|
|
24
|
+
bearer(),
|
|
25
|
+
jwt(),
|
|
26
|
+
twoFactor({ issuer: "porulle-auth-schema-guard" }),
|
|
27
|
+
apiKey(),
|
|
28
|
+
phoneNumber(),
|
|
29
|
+
],
|
|
30
|
+
user: {
|
|
31
|
+
additionalFields: {
|
|
32
|
+
vendorId: { type: "string", required: false },
|
|
33
|
+
posOperatorPin: { type: "string", required: false },
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
} satisfies BetterAuthOptions;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Drizzle tables keyed by the SQL table name they declare, so the guard walks
|
|
40
|
+
* whatever models better-auth reports rather than a list someone has to
|
|
41
|
+
* remember to extend. A hand-maintained model list is how `jwks.alg` shipped
|
|
42
|
+
* missing while this guard passed.
|
|
43
|
+
*/
|
|
44
|
+
function drizzleTablesByName(): Map<string, PgTable> {
|
|
45
|
+
const byName = new Map<string, PgTable>();
|
|
46
|
+
for (const value of Object.values(authSchema)) {
|
|
47
|
+
try {
|
|
48
|
+
byName.set(getTableName(value as PgTable), value as PgTable);
|
|
49
|
+
} catch {
|
|
50
|
+
// Not a Drizzle table (type export, index helper) — skip.
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return byName;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function drizzleColumnForField(
|
|
57
|
+
table: PgTable,
|
|
58
|
+
fieldKey: string,
|
|
59
|
+
fieldName: string,
|
|
60
|
+
): PgColumn | undefined {
|
|
61
|
+
const columns = getTableColumns(table);
|
|
62
|
+
if (fieldKey in columns) {
|
|
63
|
+
return columns[fieldKey] as PgColumn;
|
|
64
|
+
}
|
|
65
|
+
for (const column of Object.values(columns)) {
|
|
66
|
+
if (column.name === fieldName) {
|
|
67
|
+
return column as PgColumn;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export type AuthSchemaParityMismatch = {
|
|
74
|
+
model: string;
|
|
75
|
+
field: string;
|
|
76
|
+
issue: string;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Returns every mismatch between better-auth's expected auth tables and the
|
|
81
|
+
* Drizzle definitions Porulle ships. An empty array means parity holds.
|
|
82
|
+
*/
|
|
83
|
+
export function findAuthSchemaParityMismatches(
|
|
84
|
+
options: BetterAuthOptions = AUTH_SCHEMA_GUARD_OPTIONS,
|
|
85
|
+
): AuthSchemaParityMismatch[] {
|
|
86
|
+
const mismatches: AuthSchemaParityMismatch[] = [];
|
|
87
|
+
const authTables = getAuthTables(options);
|
|
88
|
+
const byName = drizzleTablesByName();
|
|
89
|
+
|
|
90
|
+
for (const [model, tableDef] of Object.entries(authTables)) {
|
|
91
|
+
if (!tableDef) continue;
|
|
92
|
+
const drizzleTable = byName.get(tableDef.modelName);
|
|
93
|
+
if (!drizzleTable) {
|
|
94
|
+
mismatches.push({
|
|
95
|
+
model,
|
|
96
|
+
field: "*",
|
|
97
|
+
issue: `no drizzle table declares better-auth model "${tableDef.modelName}"`,
|
|
98
|
+
});
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
for (const [fieldKey, fieldAttr] of Object.entries(tableDef.fields)) {
|
|
103
|
+
const column = drizzleColumnForField(
|
|
104
|
+
drizzleTable,
|
|
105
|
+
fieldKey,
|
|
106
|
+
fieldAttr.fieldName ?? fieldKey,
|
|
107
|
+
);
|
|
108
|
+
if (!column) {
|
|
109
|
+
mismatches.push({
|
|
110
|
+
model,
|
|
111
|
+
field: fieldKey,
|
|
112
|
+
issue: `drizzle column missing for better-auth field "${fieldKey}"`,
|
|
113
|
+
});
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const required = fieldAttr.required !== false;
|
|
118
|
+
if (required && !column.notNull) {
|
|
119
|
+
mismatches.push({
|
|
120
|
+
model,
|
|
121
|
+
field: fieldKey,
|
|
122
|
+
issue: `column "${column.name}" must be NOT NULL (better-auth required)`,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return mismatches;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function assertAuthSchemaParity(
|
|
132
|
+
options: BetterAuthOptions = AUTH_SCHEMA_GUARD_OPTIONS,
|
|
133
|
+
): void {
|
|
134
|
+
const mismatches = findAuthSchemaParityMismatches(options);
|
|
135
|
+
if (mismatches.length === 0) return;
|
|
136
|
+
|
|
137
|
+
const detail = mismatches
|
|
138
|
+
.map((m) => ` ${m.model}.${m.field}: ${m.issue}`)
|
|
139
|
+
.join("\n");
|
|
140
|
+
throw new Error(
|
|
141
|
+
`Porulle auth-schema.ts is out of sync with installed better-auth:\n${detail}`,
|
|
142
|
+
);
|
|
143
|
+
}
|