@sovecom/module-sdk 1.0.0-rc.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.
- package/LICENSE +661 -0
- package/README.md +63 -0
- package/dist/capabilities.d.ts +125 -0
- package/dist/capabilities.d.ts.map +1 -0
- package/dist/capabilities.js +3 -0
- package/dist/capabilities.js.map +1 -0
- package/dist/core-version.d.ts +13 -0
- package/dist/core-version.d.ts.map +1 -0
- package/dist/core-version.js +16 -0
- package/dist/core-version.js.map +1 -0
- package/dist/dto.d.ts +64 -0
- package/dist/dto.d.ts.map +1 -0
- package/dist/dto.js +12 -0
- package/dist/dto.js.map +1 -0
- package/dist/email.d.ts +85 -0
- package/dist/email.d.ts.map +1 -0
- package/dist/email.js +40 -0
- package/dist/email.js.map +1 -0
- package/dist/errors.d.ts +40 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +42 -0
- package/dist/errors.js.map +1 -0
- package/dist/events.d.ts +48 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +13 -0
- package/dist/events.js.map +1 -0
- package/dist/http.d.ts +43 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +10 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +45 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest.d.ts +82 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +219 -0
- package/dist/manifest.js.map +1 -0
- package/dist/module.d.ts +29 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +19 -0
- package/dist/module.js.map +1 -0
- package/dist/slots.d.ts +20 -0
- package/dist/slots.d.ts.map +1 -0
- package/dist/slots.js +47 -0
- package/dist/slots.js.map +1 -0
- package/dist/tables.d.ts +11 -0
- package/dist/tables.d.ts.map +1 -0
- package/dist/tables.js +41 -0
- package/dist/tables.js.map +1 -0
- package/package.json +50 -0
package/README.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# @sovecom/module-sdk
|
|
2
|
+
|
|
3
|
+
The public, author-facing SDK for building [SovEcom](../../README.md) modules.
|
|
4
|
+
|
|
5
|
+
> **Status:** stub. This package is `private` and consumed in-repo via the pnpm workspace; it is
|
|
6
|
+
> not yet published to npm. This README is a placeholder — the full author guide is forthcoming.
|
|
7
|
+
|
|
8
|
+
## What it is
|
|
9
|
+
|
|
10
|
+
This package is the **single source of truth** for the SovEcom module contract: the capability
|
|
11
|
+
interfaces the core broker enforces, the data-transfer types it returns, the manifest validators, and
|
|
12
|
+
the author ergonomics. The core runtime (`apps/api`) imports these same definitions, so the published
|
|
13
|
+
contract can never drift from what the broker actually enforces.
|
|
14
|
+
|
|
15
|
+
It is a **contract package, not a code-execution surface**. All permission / tenant / refusal
|
|
16
|
+
enforcement lives in the core-side broker; nothing here can disable or weaken it.
|
|
17
|
+
|
|
18
|
+
## Authoring a module
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { defineModule, defineSlots, createNamespacedTable } from '@sovecom/module-sdk';
|
|
22
|
+
|
|
23
|
+
export default defineModule({
|
|
24
|
+
async activate(sdk) {
|
|
25
|
+
// `sdk` is the capability object: store, admin, tables, events, http, serve.
|
|
26
|
+
const products = await sdk.store.products.list({ limit: 20 });
|
|
27
|
+
|
|
28
|
+
await sdk.events.on('order.paid', async (payload) => {
|
|
29
|
+
// react to a core event
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
sdk.serve((req) => ({ status: 200, body: JSON.stringify({ ok: true }) }));
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Declarative slot metadata for the manifest (NOT a runtime call):
|
|
37
|
+
export const slots = defineSlots([{ slot: 'product-page', component: 'my-widget' }]);
|
|
38
|
+
|
|
39
|
+
// DDL/migration helper — enforces the `mod_<name>_` table prefix:
|
|
40
|
+
const itemsTable = createNamespacedTable('my-module', 'items'); // → 'mod_my-module_items'
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Exports
|
|
44
|
+
|
|
45
|
+
- `defineModule(config)` — wraps your `activate(sdk)` body into the `{ activate }` object the
|
|
46
|
+
worker loader expects.
|
|
47
|
+
- `defineSlots(entries)` — typed, validated builder for the manifest's declarative `slots` array.
|
|
48
|
+
- `createNamespacedTable(name, suffix)` — DDL identifier helper enforcing the `mod_<name>_` prefix
|
|
49
|
+
(it returns a table NAME; it is not a live query builder).
|
|
50
|
+
- Capability types: `ModuleSdk`, `StoreClient`, `AdminClient`, `HttpClient`, `TablesClient`,
|
|
51
|
+
`EventsClient`.
|
|
52
|
+
- DTOs: `ModuleProductDto`, `ModuleCategoryDto`, `ModuleOrderDto`, `ModuleCustomerDto`,
|
|
53
|
+
`ListQuery`, `ListResult`.
|
|
54
|
+
- HTTP contract: `ModuleHttpRequest`, `ModuleHttpResponse`, `ModuleHttpHandler`,
|
|
55
|
+
`ModuleHttpSurface`.
|
|
56
|
+
- Manifest types + validators: `ModuleManifest`, `ModuleSlotEntry`, `ModulePermission`,
|
|
57
|
+
`MODULE_PERMISSION_ALLOWLIST`, `moduleManifestSchema`, `parseAndVerifyManifest`,
|
|
58
|
+
`assertCoreCompatible`, `CORE_API_VERSION`.
|
|
59
|
+
- `RpcErrorCode` — stable broker error codes for author error-handling.
|
|
60
|
+
|
|
61
|
+
## License
|
|
62
|
+
|
|
63
|
+
AGPL-3.0 (core). See the repository root.
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* the {@link ModuleSdk} capability contract, EXTRACTED here
|
|
3
|
+
* as the single source of truth (was `apps/api/src/modules/runtime/worker-sdk.ts`). These are
|
|
4
|
+
* PURE TYPES: no `RpcPeer`, no runtime, no DB/pg handle. The capability object is what core hands
|
|
5
|
+
* a module's `activate(sdk)`; each method maps 1:1 onto ONE gated broker RPC. ALL permission /
|
|
6
|
+
* tenant / refusal enforcement lives in the core-side broker — a module cannot weaken it.
|
|
7
|
+
*
|
|
8
|
+
* apps/api's in-tree `createModuleSdk(peer)` IMPLEMENTS this interface and a compile-time
|
|
9
|
+
* conformance check (`*.type-test.ts`) guards that the broker never drifts from this contract.
|
|
10
|
+
*/
|
|
11
|
+
import type { ListQuery, ListResult, ModuleProductDto, ModuleCategoryDto, ModuleOrderDto, ModuleCustomerDto } from './dto.js';
|
|
12
|
+
import type { ModuleHttpHandler } from './http.js';
|
|
13
|
+
import type { ModuleEmailMessage, ModuleCustomerEmailMessage, ModuleEmailSendResult } from './email.js';
|
|
14
|
+
/** Storefront-facing read surface (catalog). Gated by `read:products` / `read:categories`. */
|
|
15
|
+
export interface StoreClient {
|
|
16
|
+
products: {
|
|
17
|
+
list(query?: Partial<ListQuery>): Promise<ListResult<ModuleProductDto>>;
|
|
18
|
+
get(id: string): Promise<ModuleProductDto | null>;
|
|
19
|
+
};
|
|
20
|
+
categories: {
|
|
21
|
+
list(query?: Partial<ListQuery>): Promise<ListResult<ModuleCategoryDto>>;
|
|
22
|
+
get(id: string): Promise<ModuleCategoryDto | null>;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/** Admin-facing read surface. Gated by `read:orders` / `read:customers` (customer = field-limited). */
|
|
26
|
+
export interface AdminClient {
|
|
27
|
+
orders: {
|
|
28
|
+
list(query?: Partial<ListQuery>): Promise<ListResult<ModuleOrderDto>>;
|
|
29
|
+
get(id: string): Promise<ModuleOrderDto | null>;
|
|
30
|
+
};
|
|
31
|
+
customers: {
|
|
32
|
+
list(query?: Partial<ListQuery>): Promise<ListResult<ModuleCustomerDto>>;
|
|
33
|
+
get(id: string): Promise<ModuleCustomerDto | null>;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Narrow commerce read surface (follow-up B1). Gated by the EXISTING `read:orders` permission
|
|
38
|
+
* (default-deny: FORBIDDEN without it). It deliberately returns ONLY a boolean verdict — never order
|
|
39
|
+
* rows — so a module can ASK "did this customer buy this product?" without being handed any order
|
|
40
|
+
* details (least-privilege). The query is tenant-scoped from the broker context (never module
|
|
41
|
+
* input) and runs against paid/fulfilled orders only.
|
|
42
|
+
*/
|
|
43
|
+
export interface CommerceClient {
|
|
44
|
+
/**
|
|
45
|
+
* @returns `true` iff the tenant has a paid (or later: fulfilled/shipped/…) order for
|
|
46
|
+
* `customerId` that contains `productId`. Both ids are opaque, bound params. A bare
|
|
47
|
+
* boolean — no order data crosses the boundary.
|
|
48
|
+
*/
|
|
49
|
+
hasPurchased(customerId: string, productId: string): Promise<boolean>;
|
|
50
|
+
}
|
|
51
|
+
/** Outbound HTTP (gated by `http:outbound`; mediated + SSRF-guarded in core). */
|
|
52
|
+
export interface HttpClient {
|
|
53
|
+
fetch(request: {
|
|
54
|
+
url: string;
|
|
55
|
+
method?: string;
|
|
56
|
+
headers?: Record<string, string>;
|
|
57
|
+
body?: string;
|
|
58
|
+
}): Promise<{
|
|
59
|
+
status: number;
|
|
60
|
+
headers: Record<string, string>;
|
|
61
|
+
body: string;
|
|
62
|
+
}>;
|
|
63
|
+
}
|
|
64
|
+
/** Module's OWN tables (gated by `write:own_tables`). Parameterized SQL against its own schema. */
|
|
65
|
+
export interface TablesClient {
|
|
66
|
+
/** Run a SELECT (or any returning statement); resolves the rows. */
|
|
67
|
+
query<T = Record<string, unknown>>(sql: string, params?: ReadonlyArray<string | number | boolean | null>): Promise<{
|
|
68
|
+
rows: T[];
|
|
69
|
+
rowCount: number;
|
|
70
|
+
}>;
|
|
71
|
+
/** Run a mutation; resolves the affected/returned rows + count. */
|
|
72
|
+
exec(sql: string, params?: ReadonlyArray<string | number | boolean | null>): Promise<{
|
|
73
|
+
rows: Array<Record<string, unknown>>;
|
|
74
|
+
rowCount: number;
|
|
75
|
+
}>;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Outbound email (gated by `email:send`; 3.10-i). The module supplies its own subject + body and
|
|
79
|
+
* NEVER sees SMTP credentials or core's transactional templates. Core validates the recipient +
|
|
80
|
+
* subject (header-injection-safe), rate-limits per module, audits every send, and QUEUES the
|
|
81
|
+
* message through core's MailService scoped to the module's tenant. Resolves `{ queued: true }`
|
|
82
|
+
* on success; rejects with `RATE_LIMITED` over the cap or `FORBIDDEN` without the grant.
|
|
83
|
+
*
|
|
84
|
+
* `sendToCustomer` (follow-up B3) is the PRIVACY-PRESERVING variant: the module names a customer by
|
|
85
|
+
* its opaque `customerId` ONLY — it supplies no address and NEVER receives one. Core resolves the
|
|
86
|
+
* recipient from the (broker-context tenant, customerId) composite, honours marketing CONSENT
|
|
87
|
+
* (`accepts_marketing`) and RGPD erasure (`deleted_at`/`anonymized_at`), and either sends or
|
|
88
|
+
* SUPPRESSES. It resolves `{ queued: true }` when sent or `{ queued: false }` when suppressed — and
|
|
89
|
+
* the module CANNOT learn WHY a send was suppressed (no consent/existence oracle). It shares the
|
|
90
|
+
* EXISTING `email:send` grant + per-module rate limit (it is strictly more privacy-preserving than
|
|
91
|
+
* `send`, which already lets a module email any address it supplies).
|
|
92
|
+
*/
|
|
93
|
+
export interface EmailClient {
|
|
94
|
+
send(message: ModuleEmailMessage): Promise<ModuleEmailSendResult>;
|
|
95
|
+
sendToCustomer(message: ModuleCustomerEmailMessage): Promise<ModuleEmailSendResult>;
|
|
96
|
+
}
|
|
97
|
+
/** Domain events (gated by `subscribe:events` / `emit:events`). */
|
|
98
|
+
export interface EventsClient {
|
|
99
|
+
/**
|
|
100
|
+
* Subscribe to an event and register a handler. Re-sends the full subscription set to core, so
|
|
101
|
+
* call all `on(...)` during `activate`. Events: curated core names (`order.paid`, `product.*`,
|
|
102
|
+
* …) or another module's `mod.<name>.*`.
|
|
103
|
+
*/
|
|
104
|
+
on(event: string, handler: (payload: unknown) => void | Promise<void>): Promise<void>;
|
|
105
|
+
/** Emit a module event (delivered as `mod.<thisModule>.<event>` to other subscribed modules). */
|
|
106
|
+
emit(event: string, payload?: unknown): Promise<void>;
|
|
107
|
+
}
|
|
108
|
+
/** The capability object handed to a module's `activate(sdk)` — its ONLY channel to core. */
|
|
109
|
+
export interface ModuleSdk {
|
|
110
|
+
readonly store: StoreClient;
|
|
111
|
+
readonly admin: AdminClient;
|
|
112
|
+
/** Narrow commerce reads (gated by `read:orders`; boolean-only — see {@link CommerceClient}). */
|
|
113
|
+
readonly commerce: CommerceClient;
|
|
114
|
+
readonly http: HttpClient;
|
|
115
|
+
readonly tables: TablesClient;
|
|
116
|
+
readonly events: EventsClient;
|
|
117
|
+
/** Outbound email via core's MailService (gated by `email:send`; 3.10-i). */
|
|
118
|
+
readonly email: EmailClient;
|
|
119
|
+
/**
|
|
120
|
+
* Register the module's HTTP handler. Core proxies requests on the module's mounted routes
|
|
121
|
+
* (`/{store,admin}/v1/modules/<name>/*`) to it. At most one handler; the last call wins.
|
|
122
|
+
*/
|
|
123
|
+
serve(handler: ModuleHttpHandler): void;
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=capabilities.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities.js","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The module-API contract version, EXTRACTED here (was
|
|
3
|
+
* `apps/api/src/modules/core-version.ts`) so the SDK's `assertCoreCompatible` and apps/api's
|
|
4
|
+
* runtime gate share ONE constant. apps/api now re-exports this from `@sovecom/module-sdk`.
|
|
5
|
+
*
|
|
6
|
+
* This is the version a module's `compatibleCore` range is checked against — the stable
|
|
7
|
+
* contract between core and modules. It is INTENTIONALLY decoupled from the API package's own
|
|
8
|
+
* `package.json` version (pre-1.0): module authors target a stable `^1.0.0` while the package
|
|
9
|
+
* version churns through 0.x. Bumping the MAJOR here means modules pinned to an older major
|
|
10
|
+
* refuse to load.
|
|
11
|
+
*/
|
|
12
|
+
export declare const CORE_API_VERSION = "1.0.0";
|
|
13
|
+
//# sourceMappingURL=core-version.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core-version.d.ts","sourceRoot":"","sources":["../src/core-version.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,eAAO,MAAM,gBAAgB,UAAU,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CORE_API_VERSION = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* The module-API contract version, EXTRACTED here (was
|
|
6
|
+
* `apps/api/src/modules/core-version.ts`) so the SDK's `assertCoreCompatible` and apps/api's
|
|
7
|
+
* runtime gate share ONE constant. apps/api now re-exports this from `@sovecom/module-sdk`.
|
|
8
|
+
*
|
|
9
|
+
* This is the version a module's `compatibleCore` range is checked against — the stable
|
|
10
|
+
* contract between core and modules. It is INTENTIONALLY decoupled from the API package's own
|
|
11
|
+
* `package.json` version (pre-1.0): module authors target a stable `^1.0.0` while the package
|
|
12
|
+
* version churns through 0.x. Bumping the MAJOR here means modules pinned to an older major
|
|
13
|
+
* refuse to load.
|
|
14
|
+
*/
|
|
15
|
+
exports.CORE_API_VERSION = '1.0.0';
|
|
16
|
+
//# sourceMappingURL=core-version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core-version.js","sourceRoot":"","sources":["../src/core-version.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;GAUG;AACU,QAAA,gBAAgB,GAAG,OAAO,CAAC"}
|
package/dist/dto.d.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* the broker read-port DTOs, EXTRACTED here as the single
|
|
3
|
+
* source of truth. These were defined in `apps/api/src/modules/runtime/broker-ports.ts`; the
|
|
4
|
+
* dependency direction is now reversed (apps/api imports them from this package), so the
|
|
5
|
+
* published SDK contract can never drift from what the core broker returns.
|
|
6
|
+
*
|
|
7
|
+
* The DTO TYPES are the privacy boundary: e.g. {@link ModuleCustomerDto} has no email/phone/
|
|
8
|
+
* address/VAT fields, so `read:customers` is field-limited by construction.
|
|
9
|
+
*/
|
|
10
|
+
/** Cursor/limit list query a module may pass (the broker validates + bounds it). */
|
|
11
|
+
export interface ListQuery {
|
|
12
|
+
readonly limit: number;
|
|
13
|
+
readonly cursor?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface ListResult<T> {
|
|
16
|
+
readonly items: readonly T[];
|
|
17
|
+
readonly nextCursor?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* READ-ONLY product→category metadata on {@link ModuleProductDto} (follow-up B1). A product's
|
|
21
|
+
* PRIMARY category (the lowest-`position`, id-tiebroken row of its `product_categories` links),
|
|
22
|
+
* projected to the same `{ id, slug, name }` shape as {@link ModuleCategoryDto}. It rides the
|
|
23
|
+
* EXISTING `read:products` grant (no new permission — it is catalog metadata, never PII) and is
|
|
24
|
+
* `undefined` when the product has no category. Lets a module filter/exclude by category without a
|
|
25
|
+
* separate product→category port.
|
|
26
|
+
*/
|
|
27
|
+
export interface ModuleProductCategory {
|
|
28
|
+
readonly id: string;
|
|
29
|
+
readonly slug: string;
|
|
30
|
+
readonly name: string;
|
|
31
|
+
}
|
|
32
|
+
export interface ModuleProductDto {
|
|
33
|
+
readonly id: string;
|
|
34
|
+
readonly slug: string;
|
|
35
|
+
readonly title: string;
|
|
36
|
+
readonly status: string;
|
|
37
|
+
/** The product's primary category (read-only; omitted/undefined when it has none). */
|
|
38
|
+
readonly category?: ModuleProductCategory;
|
|
39
|
+
}
|
|
40
|
+
export interface ModuleCategoryDto {
|
|
41
|
+
readonly id: string;
|
|
42
|
+
readonly slug: string;
|
|
43
|
+
readonly name: string;
|
|
44
|
+
}
|
|
45
|
+
export interface ModuleOrderDto {
|
|
46
|
+
readonly id: string;
|
|
47
|
+
readonly number: string;
|
|
48
|
+
readonly status: string;
|
|
49
|
+
readonly totalMinor: number;
|
|
50
|
+
readonly currency: string;
|
|
51
|
+
readonly createdAt: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* FIELD-LIMITED customer projection. Deliberately carries NO email, phone,
|
|
55
|
+
* address, or VAT — `read:customers` must not hand modules raw PII. Widening requires a future
|
|
56
|
+
* explicit PII sub-permission with its own DTO/port.
|
|
57
|
+
*/
|
|
58
|
+
export interface ModuleCustomerDto {
|
|
59
|
+
readonly id: string;
|
|
60
|
+
readonly displayName: string;
|
|
61
|
+
readonly locale: string | null;
|
|
62
|
+
readonly createdAt: string;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=dto.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dto.d.ts","sourceRoot":"","sources":["../src/dto.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,oFAAoF;AACpF,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,UAAU,CAAC,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE,CAAC;IAC7B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,sFAAsF;IACtF,QAAQ,CAAC,QAAQ,CAAC,EAAE,qBAAqB,CAAC;CAC3C;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B"}
|
package/dist/dto.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* the broker read-port DTOs, EXTRACTED here as the single
|
|
4
|
+
* source of truth. These were defined in `apps/api/src/modules/runtime/broker-ports.ts`; the
|
|
5
|
+
* dependency direction is now reversed (apps/api imports them from this package), so the
|
|
6
|
+
* published SDK contract can never drift from what the core broker returns.
|
|
7
|
+
*
|
|
8
|
+
* The DTO TYPES are the privacy boundary: e.g. {@link ModuleCustomerDto} has no email/phone/
|
|
9
|
+
* address/VAT fields, so `read:customers` is field-limited by construction.
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
//# sourceMappingURL=dto.js.map
|
package/dist/dto.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dto.js","sourceRoot":"","sources":["../src/dto.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG"}
|
package/dist/email.d.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* the module `email:send` capability message DTO.
|
|
3
|
+
*
|
|
4
|
+
* A PURE type: the shape a module passes to `sdk.email.send(...)`. ALL enforcement — the
|
|
5
|
+
* `email:send` permission gate, strict param validation (header-injection-safe recipient/subject),
|
|
6
|
+
* the per-module rate limit, the audit record, and tenant scoping — lives in the CORE broker
|
|
7
|
+
* (`apps/api/.../module-broker.ts` + `module-mail.port.ts`). Nothing here can weaken it.
|
|
8
|
+
*
|
|
9
|
+
* The module supplies its OWN subject + plaintext body; it gets NO access to core's transactional
|
|
10
|
+
* templates and NO SMTP/transport credentials. Core routes the message through the existing
|
|
11
|
+
* `MailService` as a module-originated email.
|
|
12
|
+
*
|
|
13
|
+
* TENANT SCOPING (v1): "tenant-scoped" here means the send is ATTRIBUTED to the module's tenant for
|
|
14
|
+
* AUDIT and RATE-LIMIT purposes (both keyed by the worker's tenant + module identity, never module
|
|
15
|
+
* input). It is NOT transport-level isolation — single-tenant v1 uses one shared mail transport.
|
|
16
|
+
*
|
|
17
|
+
* BOUNDS (mirrored exactly by the broker's Zod schema — the single source of truth is the broker;
|
|
18
|
+
* these constants document the contract for authors and let the SDK package self-check):
|
|
19
|
+
* - `to` — a single, syntactically-valid email, ≤ {@link EMAIL_TO_MAX} chars, NO CR/LF/comma/
|
|
20
|
+
* semicolon (header-injection guard — a module may never address multiple recipients
|
|
21
|
+
* or smuggle a header);
|
|
22
|
+
* - `subject`— ≤ {@link EMAIL_SUBJECT_MAX} chars, NO CR/LF (header-injection guard);
|
|
23
|
+
* - `text` — required plaintext body, ≤ {@link EMAIL_TEXT_MAX} chars;
|
|
24
|
+
* - `html` — OPTIONAL html body, ≤ {@link EMAIL_HTML_MAX} chars (escaping/CSP is whatever the
|
|
25
|
+
* core mail layer already applies — the module gains no new injection surface).
|
|
26
|
+
* Unknown keys are REJECTED (`.strict()` in the broker) — a module cannot smuggle `from`, `to`
|
|
27
|
+
* arrays, `cc`/`bcc`, `tenantId`, or transport options.
|
|
28
|
+
*/
|
|
29
|
+
/** Max length of the `to` recipient address. */
|
|
30
|
+
export declare const EMAIL_TO_MAX = 254;
|
|
31
|
+
/** Max length of the `subject` line. */
|
|
32
|
+
export declare const EMAIL_SUBJECT_MAX = 200;
|
|
33
|
+
/** Max length of the plaintext `text` body. */
|
|
34
|
+
export declare const EMAIL_TEXT_MAX = 50000;
|
|
35
|
+
/** Max length of the optional `html` body. */
|
|
36
|
+
export declare const EMAIL_HTML_MAX = 100000;
|
|
37
|
+
/** The message a module supplies to {@link EmailClient.send}. */
|
|
38
|
+
export interface ModuleEmailMessage {
|
|
39
|
+
/** A single, syntactically-valid recipient email. No CR/LF/comma/semicolon. */
|
|
40
|
+
readonly to: string;
|
|
41
|
+
/** The subject line. No CR/LF. */
|
|
42
|
+
readonly subject: string;
|
|
43
|
+
/** Plain-text body (required). */
|
|
44
|
+
readonly text: string;
|
|
45
|
+
/** Optional HTML body. */
|
|
46
|
+
readonly html?: string;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The message a module supplies to {@link EmailClient.sendToCustomer}.
|
|
50
|
+
*
|
|
51
|
+
* PRIVACY-PRESERVING module→customer email: the module addresses a customer it holds only an
|
|
52
|
+
* opaque `customerId` for — it supplies NO email address and NEVER receives one. Core resolves the
|
|
53
|
+
* recipient by the (broker-context tenant, customerId) composite, honours marketing CONSENT
|
|
54
|
+
* (`accepts_marketing`) and RGPD erasure (`deleted_at` / `anonymized_at`), and either sends or
|
|
55
|
+
* SUPPRESSES the message. The module cannot tell WHY a send was suppressed (no consent/existence
|
|
56
|
+
* oracle) — it only learns whether it was {@link ModuleEmailSendResult.queued}.
|
|
57
|
+
*
|
|
58
|
+
* `subject`/`text`/`html` carry the SAME header-injection rules + length bounds as
|
|
59
|
+
* {@link ModuleEmailMessage} (validated in the core broker — the single source of truth). There is
|
|
60
|
+
* NO `to` field by construction.
|
|
61
|
+
*/
|
|
62
|
+
export interface ModuleCustomerEmailMessage {
|
|
63
|
+
/** The opaque customer id (a uuid). Core resolves the recipient; the module never sees it. */
|
|
64
|
+
readonly customerId: string;
|
|
65
|
+
/** The subject line. No CR/LF (header-injection guard). */
|
|
66
|
+
readonly subject: string;
|
|
67
|
+
/** Plain-text body (required). */
|
|
68
|
+
readonly text: string;
|
|
69
|
+
/** Optional HTML body. */
|
|
70
|
+
readonly html?: string;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The result of an attempted send — handed back to the module.
|
|
74
|
+
*
|
|
75
|
+
* - `{@link send}` always resolves `{ queued: true }` (the module supplied the address; on success
|
|
76
|
+
* the message is queued, else it rejects).
|
|
77
|
+
* - `{@link sendToCustomer}` resolves `{ queued: true }` when core sent, or `{ queued: false }` when
|
|
78
|
+
* core SUPPRESSED it (recipient missing / erased / not marketing-consented). `queued:false` is a
|
|
79
|
+
* DELIBERATELY OPAQUE outcome — it leaks no reason, so the module gains no consent/existence
|
|
80
|
+
* oracle over the customer base.
|
|
81
|
+
*/
|
|
82
|
+
export interface ModuleEmailSendResult {
|
|
83
|
+
readonly queued: boolean;
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=email.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../src/email.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,gDAAgD;AAChD,eAAO,MAAM,YAAY,MAAM,CAAC;AAChC,wCAAwC;AACxC,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,+CAA+C;AAC/C,eAAO,MAAM,cAAc,QAAS,CAAC;AACrC,8CAA8C;AAC9C,eAAO,MAAM,cAAc,SAAU,CAAC;AAEtC,iEAAiE;AACjE,MAAM,WAAW,kBAAkB;IACjC,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,kCAAkC;IAClC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,kCAAkC;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,0BAA0B;IAC1B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,0BAA0B;IACzC,8FAA8F;IAC9F,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,2DAA2D;IAC3D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,kCAAkC;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,0BAA0B;IAC1B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CAC1B"}
|
package/dist/email.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* the module `email:send` capability message DTO.
|
|
4
|
+
*
|
|
5
|
+
* A PURE type: the shape a module passes to `sdk.email.send(...)`. ALL enforcement — the
|
|
6
|
+
* `email:send` permission gate, strict param validation (header-injection-safe recipient/subject),
|
|
7
|
+
* the per-module rate limit, the audit record, and tenant scoping — lives in the CORE broker
|
|
8
|
+
* (`apps/api/.../module-broker.ts` + `module-mail.port.ts`). Nothing here can weaken it.
|
|
9
|
+
*
|
|
10
|
+
* The module supplies its OWN subject + plaintext body; it gets NO access to core's transactional
|
|
11
|
+
* templates and NO SMTP/transport credentials. Core routes the message through the existing
|
|
12
|
+
* `MailService` as a module-originated email.
|
|
13
|
+
*
|
|
14
|
+
* TENANT SCOPING (v1): "tenant-scoped" here means the send is ATTRIBUTED to the module's tenant for
|
|
15
|
+
* AUDIT and RATE-LIMIT purposes (both keyed by the worker's tenant + module identity, never module
|
|
16
|
+
* input). It is NOT transport-level isolation — single-tenant v1 uses one shared mail transport.
|
|
17
|
+
*
|
|
18
|
+
* BOUNDS (mirrored exactly by the broker's Zod schema — the single source of truth is the broker;
|
|
19
|
+
* these constants document the contract for authors and let the SDK package self-check):
|
|
20
|
+
* - `to` — a single, syntactically-valid email, ≤ {@link EMAIL_TO_MAX} chars, NO CR/LF/comma/
|
|
21
|
+
* semicolon (header-injection guard — a module may never address multiple recipients
|
|
22
|
+
* or smuggle a header);
|
|
23
|
+
* - `subject`— ≤ {@link EMAIL_SUBJECT_MAX} chars, NO CR/LF (header-injection guard);
|
|
24
|
+
* - `text` — required plaintext body, ≤ {@link EMAIL_TEXT_MAX} chars;
|
|
25
|
+
* - `html` — OPTIONAL html body, ≤ {@link EMAIL_HTML_MAX} chars (escaping/CSP is whatever the
|
|
26
|
+
* core mail layer already applies — the module gains no new injection surface).
|
|
27
|
+
* Unknown keys are REJECTED (`.strict()` in the broker) — a module cannot smuggle `from`, `to`
|
|
28
|
+
* arrays, `cc`/`bcc`, `tenantId`, or transport options.
|
|
29
|
+
*/
|
|
30
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
31
|
+
exports.EMAIL_HTML_MAX = exports.EMAIL_TEXT_MAX = exports.EMAIL_SUBJECT_MAX = exports.EMAIL_TO_MAX = void 0;
|
|
32
|
+
/** Max length of the `to` recipient address. */
|
|
33
|
+
exports.EMAIL_TO_MAX = 254;
|
|
34
|
+
/** Max length of the `subject` line. */
|
|
35
|
+
exports.EMAIL_SUBJECT_MAX = 200;
|
|
36
|
+
/** Max length of the plaintext `text` body. */
|
|
37
|
+
exports.EMAIL_TEXT_MAX = 50_000;
|
|
38
|
+
/** Max length of the optional `html` body. */
|
|
39
|
+
exports.EMAIL_HTML_MAX = 100_000;
|
|
40
|
+
//# sourceMappingURL=email.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email.js","sourceRoot":"","sources":["../src/email.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;;;AAEH,gDAAgD;AACnC,QAAA,YAAY,GAAG,GAAG,CAAC;AAChC,wCAAwC;AAC3B,QAAA,iBAAiB,GAAG,GAAG,CAAC;AACrC,+CAA+C;AAClC,QAAA,cAAc,GAAG,MAAM,CAAC;AACrC,8CAA8C;AACjC,QAAA,cAAc,GAAG,OAAO,CAAC"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* the stable RPC error codes carried on a broker response,
|
|
3
|
+
* EXTRACTED here so module authors can branch on failures (e.g. distinguish `forbidden` from
|
|
4
|
+
* `timeout`) without string-matching. Was defined in
|
|
5
|
+
* `apps/api/src/modules/runtime/ipc-protocol.ts`; apps/api now imports it from here.
|
|
6
|
+
*
|
|
7
|
+
* This is the ONLY runtime-value half of the error contract the SDK exposes. The IPC envelope,
|
|
8
|
+
* frame Zod schemas, and `RpcError` class stay core-side (transport/enforcement, not contract).
|
|
9
|
+
*/
|
|
10
|
+
/** Stable error codes carried on a response frame. */
|
|
11
|
+
export declare const RpcErrorCode: {
|
|
12
|
+
/** The peer did not register a handler for the requested method. */
|
|
13
|
+
readonly UNKNOWN_METHOD: "unknown_method";
|
|
14
|
+
/** The request timed out waiting for a response. */
|
|
15
|
+
readonly TIMEOUT: "timeout";
|
|
16
|
+
/** The handler threw. */
|
|
17
|
+
readonly HANDLER_ERROR: "handler_error";
|
|
18
|
+
/** The channel closed before a response arrived. */
|
|
19
|
+
readonly CHANNEL_CLOSED: "channel_closed";
|
|
20
|
+
/** Params/result failed validation, or a frame was malformed. */
|
|
21
|
+
readonly PROTOCOL: "protocol";
|
|
22
|
+
/** The broker refused the call (permission / tenant / transactional-path). */
|
|
23
|
+
readonly FORBIDDEN: "forbidden";
|
|
24
|
+
/** The capability exists in the manifest vocabulary but is not implemented yet. */
|
|
25
|
+
readonly NOT_AVAILABLE: "not_available";
|
|
26
|
+
/**
|
|
27
|
+
* The worker's inbound RPC concurrency cap was exceeded (DoS hardening).
|
|
28
|
+
* The caller should back off and retry rather than queuing indefinitely in core.
|
|
29
|
+
*/
|
|
30
|
+
readonly BUSY: "busy";
|
|
31
|
+
/**
|
|
32
|
+
* A per-module capability rate limit was exceeded (e.g. `email:send`). The call was
|
|
33
|
+
* REFUSED, not queued; the caller should back off until the limit window rolls over. Distinct
|
|
34
|
+
* from {@link BUSY} (transient concurrency) — this is a sustained-volume cap on a side-effecting
|
|
35
|
+
* capability, surfaced to the module as a clean refusal rather than a thrown crash.
|
|
36
|
+
*/
|
|
37
|
+
readonly RATE_LIMITED: "rate_limited";
|
|
38
|
+
};
|
|
39
|
+
export type RpcErrorCode = (typeof RpcErrorCode)[keyof typeof RpcErrorCode];
|
|
40
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,sDAAsD;AACtD,eAAO,MAAM,YAAY;IACvB,oEAAoE;;IAEpE,oDAAoD;;IAEpD,yBAAyB;;IAEzB,oDAAoD;;IAEpD,iEAAiE;;IAEjE,8EAA8E;;IAE9E,mFAAmF;;IAEnF;;;OAGG;;IAEH;;;;;OAKG;;CAEK,CAAC;AAEX,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,OAAO,YAAY,CAAC,CAAC"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* the stable RPC error codes carried on a broker response,
|
|
4
|
+
* EXTRACTED here so module authors can branch on failures (e.g. distinguish `forbidden` from
|
|
5
|
+
* `timeout`) without string-matching. Was defined in
|
|
6
|
+
* `apps/api/src/modules/runtime/ipc-protocol.ts`; apps/api now imports it from here.
|
|
7
|
+
*
|
|
8
|
+
* This is the ONLY runtime-value half of the error contract the SDK exposes. The IPC envelope,
|
|
9
|
+
* frame Zod schemas, and `RpcError` class stay core-side (transport/enforcement, not contract).
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.RpcErrorCode = void 0;
|
|
13
|
+
/** Stable error codes carried on a response frame. */
|
|
14
|
+
exports.RpcErrorCode = {
|
|
15
|
+
/** The peer did not register a handler for the requested method. */
|
|
16
|
+
UNKNOWN_METHOD: 'unknown_method',
|
|
17
|
+
/** The request timed out waiting for a response. */
|
|
18
|
+
TIMEOUT: 'timeout',
|
|
19
|
+
/** The handler threw. */
|
|
20
|
+
HANDLER_ERROR: 'handler_error',
|
|
21
|
+
/** The channel closed before a response arrived. */
|
|
22
|
+
CHANNEL_CLOSED: 'channel_closed',
|
|
23
|
+
/** Params/result failed validation, or a frame was malformed. */
|
|
24
|
+
PROTOCOL: 'protocol',
|
|
25
|
+
/** The broker refused the call (permission / tenant / transactional-path). */
|
|
26
|
+
FORBIDDEN: 'forbidden',
|
|
27
|
+
/** The capability exists in the manifest vocabulary but is not implemented yet. */
|
|
28
|
+
NOT_AVAILABLE: 'not_available',
|
|
29
|
+
/**
|
|
30
|
+
* The worker's inbound RPC concurrency cap was exceeded (DoS hardening).
|
|
31
|
+
* The caller should back off and retry rather than queuing indefinitely in core.
|
|
32
|
+
*/
|
|
33
|
+
BUSY: 'busy',
|
|
34
|
+
/**
|
|
35
|
+
* A per-module capability rate limit was exceeded (e.g. `email:send`). The call was
|
|
36
|
+
* REFUSED, not queued; the caller should back off until the limit window rolls over. Distinct
|
|
37
|
+
* from {@link BUSY} (transient concurrency) — this is a sustained-volume cap on a side-effecting
|
|
38
|
+
* capability, surfaced to the module as a clean refusal rather than a thrown crash.
|
|
39
|
+
*/
|
|
40
|
+
RATE_LIMITED: 'rate_limited',
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;AAEH,sDAAsD;AACzC,QAAA,YAAY,GAAG;IAC1B,oEAAoE;IACpE,cAAc,EAAE,gBAAgB;IAChC,oDAAoD;IACpD,OAAO,EAAE,SAAS;IAClB,yBAAyB;IACzB,aAAa,EAAE,eAAe;IAC9B,oDAAoD;IACpD,cAAc,EAAE,gBAAgB;IAChC,iEAAiE;IACjE,QAAQ,EAAE,UAAU;IACpB,8EAA8E;IAC9E,SAAS,EAAE,WAAW;IACtB,mFAAmF;IACnF,aAAa,EAAE,eAAe;IAC9B;;;OAGG;IACH,IAAI,EAAE,MAAM;IACZ;;;;;OAKG;IACH,YAAY,EAAE,cAAc;CACpB,CAAC"}
|
package/dist/events.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Follow-up B2 — the MODULE-FACING payload contracts for the two OBSERVATIONAL commerce events a
|
|
3
|
+
* module may subscribe to (`product.price_changed`, `product.stock_changed`). These are the EXACT
|
|
4
|
+
* shapes core fans to a subscribed worker's event handler, so they are part of the published SDK
|
|
5
|
+
* contract: a change here is a contract change. `apps/api` (the core runtime) imports these FROM
|
|
6
|
+
* here, so what the broker delivers can never drift from what a module author types against.
|
|
7
|
+
*
|
|
8
|
+
* Both are deliberately minimal, NON-PII projections. Modules only OBSERVE these signals — emitting
|
|
9
|
+
* them stays entirely inside core's transactional/admin paths (a module never enters them).
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* `eventId` — an opaque, core-assigned identifier that is UNIQUE PER EMIT. Two genuinely distinct
|
|
13
|
+
* events (e.g. a flash-sale cycle that drops a variant to the SAME price TWICE) carry DIFFERENT
|
|
14
|
+
* `eventId`s, so a module can use it as an idempotency key that dedupes only redelivery/reprocessing
|
|
15
|
+
* of the SAME event — never two real events that happen to share the same {old,new} values. (Core
|
|
16
|
+
* delivery is at-most-once today, but a module persisting a dedup ledger must key on this, not on
|
|
17
|
+
* the collision-prone value tuple.) Treat it as opaque — do not parse it.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* `product.price_changed` — a variant's price ACTUALLY changed (old !== new). Prices are PUBLIC
|
|
21
|
+
* catalog data, so carrying BOTH the old and new minor-unit price is safe and is exactly what a
|
|
22
|
+
* module needs to detect a drop without a second read. NEVER delivered on a no-op price update.
|
|
23
|
+
*/
|
|
24
|
+
export interface ProductPriceChangedPayload {
|
|
25
|
+
/** Opaque, core-assigned, unique-per-emit id — the correct module-side idempotency key. */
|
|
26
|
+
readonly eventId: string;
|
|
27
|
+
readonly productId: string;
|
|
28
|
+
readonly variantId: string;
|
|
29
|
+
readonly oldPriceMinor: number;
|
|
30
|
+
readonly newPriceMinor: number;
|
|
31
|
+
readonly currency: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* `product.stock_changed` — a variant's AVAILABILITY flipped across the zero boundary. Carries a
|
|
35
|
+
* back-in-stock BOOLEAN ONLY: `available: true` on an out-of-stock → in-stock (0 → positive)
|
|
36
|
+
* transition, `false` on in-stock → out-of-stock. It NEVER carries the exact stock level / quantity
|
|
37
|
+
* — exposing precise inventory to a sandboxed module is a competitive-information leak, and the
|
|
38
|
+
* boolean is all a back-in-stock notifier needs. `available` reflects PHYSICAL stock crossing zero
|
|
39
|
+
* (NOT stock-minus-active-reservations).
|
|
40
|
+
*/
|
|
41
|
+
export interface ProductStockChangedPayload {
|
|
42
|
+
/** Opaque, core-assigned, unique-per-emit id — the correct module-side idempotency key. */
|
|
43
|
+
readonly eventId: string;
|
|
44
|
+
readonly productId: string;
|
|
45
|
+
readonly variantId: string;
|
|
46
|
+
readonly available: boolean;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;;;;;;GAOG;AAEH;;;;GAIG;AACH,MAAM,WAAW,0BAA0B;IACzC,2FAA2F;IAC3F,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,0BAA0B;IACzC,2FAA2F;IAC3F,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B"}
|
package/dist/events.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Follow-up B2 — the MODULE-FACING payload contracts for the two OBSERVATIONAL commerce events a
|
|
4
|
+
* module may subscribe to (`product.price_changed`, `product.stock_changed`). These are the EXACT
|
|
5
|
+
* shapes core fans to a subscribed worker's event handler, so they are part of the published SDK
|
|
6
|
+
* contract: a change here is a contract change. `apps/api` (the core runtime) imports these FROM
|
|
7
|
+
* here, so what the broker delivers can never drift from what a module author types against.
|
|
8
|
+
*
|
|
9
|
+
* Both are deliberately minimal, NON-PII projections. Modules only OBSERVE these signals — emitting
|
|
10
|
+
* them stays entirely inside core's transactional/admin paths (a module never enters them).
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
//# sourceMappingURL=events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.js","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG"}
|