@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.
Files changed (51) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +63 -0
  3. package/dist/capabilities.d.ts +125 -0
  4. package/dist/capabilities.d.ts.map +1 -0
  5. package/dist/capabilities.js +3 -0
  6. package/dist/capabilities.js.map +1 -0
  7. package/dist/core-version.d.ts +13 -0
  8. package/dist/core-version.d.ts.map +1 -0
  9. package/dist/core-version.js +16 -0
  10. package/dist/core-version.js.map +1 -0
  11. package/dist/dto.d.ts +64 -0
  12. package/dist/dto.d.ts.map +1 -0
  13. package/dist/dto.js +12 -0
  14. package/dist/dto.js.map +1 -0
  15. package/dist/email.d.ts +85 -0
  16. package/dist/email.d.ts.map +1 -0
  17. package/dist/email.js +40 -0
  18. package/dist/email.js.map +1 -0
  19. package/dist/errors.d.ts +40 -0
  20. package/dist/errors.d.ts.map +1 -0
  21. package/dist/errors.js +42 -0
  22. package/dist/errors.js.map +1 -0
  23. package/dist/events.d.ts +48 -0
  24. package/dist/events.d.ts.map +1 -0
  25. package/dist/events.js +13 -0
  26. package/dist/events.js.map +1 -0
  27. package/dist/http.d.ts +43 -0
  28. package/dist/http.d.ts.map +1 -0
  29. package/dist/http.js +10 -0
  30. package/dist/http.js.map +1 -0
  31. package/dist/index.d.ts +27 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +45 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/manifest.d.ts +82 -0
  36. package/dist/manifest.d.ts.map +1 -0
  37. package/dist/manifest.js +219 -0
  38. package/dist/manifest.js.map +1 -0
  39. package/dist/module.d.ts +29 -0
  40. package/dist/module.d.ts.map +1 -0
  41. package/dist/module.js +19 -0
  42. package/dist/module.js.map +1 -0
  43. package/dist/slots.d.ts +20 -0
  44. package/dist/slots.d.ts.map +1 -0
  45. package/dist/slots.js +47 -0
  46. package/dist/slots.js.map +1 -0
  47. package/dist/tables.d.ts +11 -0
  48. package/dist/tables.d.ts.map +1 -0
  49. package/dist/tables.js +41 -0
  50. package/dist/tables.js.map +1 -0
  51. package/package.json +50 -0
package/dist/http.d.ts ADDED
@@ -0,0 +1,43 @@
1
+ /**
2
+ * the module HTTP endpoint contract types, EXTRACTED here as
3
+ * the single source of truth (was `apps/api/src/modules/runtime/module-http.ts`). These are the
4
+ * wire shapes an author's `sdk.serve(handler)` deals with; core shapes the request and BOUNDS
5
+ * the response. The byte caps / header allowlists / media-type policy stay core-side (they are
6
+ * enforcement, not contract) and are intentionally NOT exported from the public SDK.
7
+ */
8
+ /** Which mounted surface the request arrived on. `store` is public; `admin` is RBAC-gated. */
9
+ export type ModuleHttpSurface = 'store' | 'admin';
10
+ export interface ModuleHttpRequest {
11
+ readonly surface: ModuleHttpSurface;
12
+ readonly method: string;
13
+ /** The path UNDER the module mount, e.g. `/items/42` for `/store/v1/modules/foo/items/42`. */
14
+ readonly path: string;
15
+ readonly query: Record<string, string | string[]>;
16
+ /** Already-sanitized request headers (core strips hop-by-hop + auth/cookie). */
17
+ readonly headers: Record<string, string>;
18
+ readonly body?: string;
19
+ /** The tenant the request is scoped to (the module cannot widen it). */
20
+ readonly tenantId: string;
21
+ /**
22
+ * The CORE-VERIFIED customer principal for this request, or `undefined` for an anonymous call.
23
+ *
24
+ * Set ONLY by the core proxy from a customer JWT it verified itself (the store mount runs an
25
+ * optional customer-auth guard). It is NEVER read from client input — a `customer` field in the
26
+ * request body, headers, or query cannot influence it, and the raw token is still stripped before
27
+ * the request reaches the module. Absent (`undefined`) when no valid customer token was presented.
28
+ *
29
+ * The `id` is the same tenant-scoped customer id the JWT verification resolved against the DB;
30
+ * a customer-scoped module endpoint should return 401/empty when this is absent.
31
+ */
32
+ readonly customer?: {
33
+ readonly id: string;
34
+ };
35
+ }
36
+ export interface ModuleHttpResponse {
37
+ readonly status: number;
38
+ readonly headers?: Record<string, string>;
39
+ readonly body?: string;
40
+ }
41
+ /** A module's request handler, registered via `sdk.serve(...)`. */
42
+ export type ModuleHttpHandler = (req: ModuleHttpRequest) => ModuleHttpResponse | Promise<ModuleHttpResponse>;
43
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,8FAA8F;AAC9F,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,OAAO,CAAC;AAElD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,8FAA8F;IAC9F,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IAClD,gFAAgF;IAChF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7C;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,mEAAmE;AACnE,MAAM,MAAM,iBAAiB,GAAG,CAC9B,GAAG,EAAE,iBAAiB,KACnB,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC"}
package/dist/http.js ADDED
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ /**
3
+ * the module HTTP endpoint contract types, EXTRACTED here as
4
+ * the single source of truth (was `apps/api/src/modules/runtime/module-http.ts`). These are the
5
+ * wire shapes an author's `sdk.serve(handler)` deals with; core shapes the request and BOUNDS
6
+ * the response. The byte caps / header allowlists / media-type policy stay core-side (they are
7
+ * enforcement, not contract) and are intentionally NOT exported from the public SDK.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * `@sovecom/module-sdk` — the public, semver-pinned author-facing contract for the SovEcom
3
+ * module ecosystem. This package is the SINGLE SOURCE OF TRUTH for the
4
+ * module trust boundary: the capability interfaces, DTOs, manifest validators, and author
5
+ * ergonomics. `apps/api` (the core runtime) imports these definitions FROM here, so the
6
+ * published SDK can never drift from what the broker actually enforces.
7
+ *
8
+ * It is a CONTRACT package, not a code-execution surface: ALL permission / tenant / refusal
9
+ * enforcement lives in the core-side broker. Nothing exported here can disable or weaken it.
10
+ */
11
+ /** SDK package version (independent of the core API contract version below). */
12
+ export declare const MODULE_SDK_VERSION = "1.0.0-rc.1";
13
+ export { defineModule } from './module.js';
14
+ export type { SovecomModule, DefineModuleConfig } from './module.js';
15
+ export { defineSlots } from './slots.js';
16
+ export { createNamespacedTable } from './tables.js';
17
+ export type { ModuleSdk, StoreClient, AdminClient, CommerceClient, HttpClient, TablesClient, EventsClient, EmailClient, } from './capabilities.js';
18
+ export { EMAIL_TO_MAX, EMAIL_SUBJECT_MAX, EMAIL_TEXT_MAX, EMAIL_HTML_MAX } from './email.js';
19
+ export type { ModuleEmailMessage, ModuleCustomerEmailMessage, ModuleEmailSendResult, } from './email.js';
20
+ export type { ListQuery, ListResult, ModuleProductDto, ModuleProductCategory, ModuleCategoryDto, ModuleOrderDto, ModuleCustomerDto, } from './dto.js';
21
+ export type { ProductPriceChangedPayload, ProductStockChangedPayload } from './events.js';
22
+ export type { ModuleHttpSurface, ModuleHttpRequest, ModuleHttpResponse, ModuleHttpHandler, } from './http.js';
23
+ export { MODULE_PERMISSION_ALLOWLIST, MANIFEST_MAX_BYTES, MODULE_NAME_RE, SLOT_SLUG_RE, TABLE_SUFFIX_RE, moduleManifestSchema, parseAndVerifyManifest, assertCoreCompatible, } from './manifest.js';
24
+ export type { ModulePermission, ModuleManifest, ModuleSlotEntry } from './manifest.js';
25
+ export { CORE_API_VERSION } from './core-version.js';
26
+ export { RpcErrorCode } from './errors.js';
27
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,gFAAgF;AAChF,eAAO,MAAM,kBAAkB,eAAe,CAAC;AAG/C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,YAAY,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAGpD,YAAY,EACV,SAAS,EACT,WAAW,EACX,WAAW,EACX,cAAc,EACd,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,WAAW,GACZ,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC7F,YAAY,EACV,kBAAkB,EAClB,0BAA0B,EAC1B,qBAAqB,GACtB,MAAM,YAAY,CAAC;AAGpB,YAAY,EACV,SAAS,EACT,UAAU,EACV,gBAAgB,EAChB,qBAAqB,EACrB,iBAAiB,EACjB,cAAc,EACd,iBAAiB,GAClB,MAAM,UAAU,CAAC;AAGlB,YAAY,EAAE,0BAA0B,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC;AAG1F,YAAY,EACV,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,WAAW,CAAC;AAGnB,OAAO,EACL,2BAA2B,EAC3B,kBAAkB,EAClB,cAAc,EACd,YAAY,EACZ,eAAe,EACf,oBAAoB,EACpB,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAGvF,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAGrD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ /**
3
+ * `@sovecom/module-sdk` — the public, semver-pinned author-facing contract for the SovEcom
4
+ * module ecosystem. This package is the SINGLE SOURCE OF TRUTH for the
5
+ * module trust boundary: the capability interfaces, DTOs, manifest validators, and author
6
+ * ergonomics. `apps/api` (the core runtime) imports these definitions FROM here, so the
7
+ * published SDK can never drift from what the broker actually enforces.
8
+ *
9
+ * It is a CONTRACT package, not a code-execution surface: ALL permission / tenant / refusal
10
+ * enforcement lives in the core-side broker. Nothing exported here can disable or weaken it.
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.RpcErrorCode = exports.CORE_API_VERSION = exports.assertCoreCompatible = exports.parseAndVerifyManifest = exports.moduleManifestSchema = exports.TABLE_SUFFIX_RE = exports.SLOT_SLUG_RE = exports.MODULE_NAME_RE = exports.MANIFEST_MAX_BYTES = exports.MODULE_PERMISSION_ALLOWLIST = exports.EMAIL_HTML_MAX = exports.EMAIL_TEXT_MAX = exports.EMAIL_SUBJECT_MAX = exports.EMAIL_TO_MAX = exports.createNamespacedTable = exports.defineSlots = exports.defineModule = exports.MODULE_SDK_VERSION = void 0;
14
+ /** SDK package version (independent of the core API contract version below). */
15
+ exports.MODULE_SDK_VERSION = '1.0.0-rc.1';
16
+ // ── author ergonomics ───────────────────────────────────────────────────────────
17
+ var module_js_1 = require("./module.js");
18
+ Object.defineProperty(exports, "defineModule", { enumerable: true, get: function () { return module_js_1.defineModule; } });
19
+ var slots_js_1 = require("./slots.js");
20
+ Object.defineProperty(exports, "defineSlots", { enumerable: true, get: function () { return slots_js_1.defineSlots; } });
21
+ var tables_js_1 = require("./tables.js");
22
+ Object.defineProperty(exports, "createNamespacedTable", { enumerable: true, get: function () { return tables_js_1.createNamespacedTable; } });
23
+ // ── email:send message DTO + bounds (3.10-i) ─────────────────────────────────────
24
+ var email_js_1 = require("./email.js");
25
+ Object.defineProperty(exports, "EMAIL_TO_MAX", { enumerable: true, get: function () { return email_js_1.EMAIL_TO_MAX; } });
26
+ Object.defineProperty(exports, "EMAIL_SUBJECT_MAX", { enumerable: true, get: function () { return email_js_1.EMAIL_SUBJECT_MAX; } });
27
+ Object.defineProperty(exports, "EMAIL_TEXT_MAX", { enumerable: true, get: function () { return email_js_1.EMAIL_TEXT_MAX; } });
28
+ Object.defineProperty(exports, "EMAIL_HTML_MAX", { enumerable: true, get: function () { return email_js_1.EMAIL_HTML_MAX; } });
29
+ // ── manifest types + validators (single source of truth) ─────────────────────────
30
+ var manifest_js_1 = require("./manifest.js");
31
+ Object.defineProperty(exports, "MODULE_PERMISSION_ALLOWLIST", { enumerable: true, get: function () { return manifest_js_1.MODULE_PERMISSION_ALLOWLIST; } });
32
+ Object.defineProperty(exports, "MANIFEST_MAX_BYTES", { enumerable: true, get: function () { return manifest_js_1.MANIFEST_MAX_BYTES; } });
33
+ Object.defineProperty(exports, "MODULE_NAME_RE", { enumerable: true, get: function () { return manifest_js_1.MODULE_NAME_RE; } });
34
+ Object.defineProperty(exports, "SLOT_SLUG_RE", { enumerable: true, get: function () { return manifest_js_1.SLOT_SLUG_RE; } });
35
+ Object.defineProperty(exports, "TABLE_SUFFIX_RE", { enumerable: true, get: function () { return manifest_js_1.TABLE_SUFFIX_RE; } });
36
+ Object.defineProperty(exports, "moduleManifestSchema", { enumerable: true, get: function () { return manifest_js_1.moduleManifestSchema; } });
37
+ Object.defineProperty(exports, "parseAndVerifyManifest", { enumerable: true, get: function () { return manifest_js_1.parseAndVerifyManifest; } });
38
+ Object.defineProperty(exports, "assertCoreCompatible", { enumerable: true, get: function () { return manifest_js_1.assertCoreCompatible; } });
39
+ // ── core API contract version ───────────────────────────────────────────────────
40
+ var core_version_js_1 = require("./core-version.js");
41
+ Object.defineProperty(exports, "CORE_API_VERSION", { enumerable: true, get: function () { return core_version_js_1.CORE_API_VERSION; } });
42
+ // ── RPC error codes (for author error-handling) ──────────────────────────────────
43
+ var errors_js_1 = require("./errors.js");
44
+ Object.defineProperty(exports, "RpcErrorCode", { enumerable: true, get: function () { return errors_js_1.RpcErrorCode; } });
45
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;;AAEH,gFAAgF;AACnE,QAAA,kBAAkB,GAAG,YAAY,CAAC;AAE/C,mFAAmF;AACnF,yCAA2C;AAAlC,yGAAA,YAAY,OAAA;AAErB,uCAAyC;AAAhC,uGAAA,WAAW,OAAA;AACpB,yCAAoD;AAA3C,kHAAA,qBAAqB,OAAA;AAc9B,oFAAoF;AACpF,uCAA6F;AAApF,wGAAA,YAAY,OAAA;AAAE,6GAAA,iBAAiB,OAAA;AAAE,0GAAA,cAAc,OAAA;AAAE,0GAAA,cAAc,OAAA;AA6BxE,oFAAoF;AACpF,6CASuB;AARrB,0HAAA,2BAA2B,OAAA;AAC3B,iHAAA,kBAAkB,OAAA;AAClB,6GAAA,cAAc,OAAA;AACd,2GAAA,YAAY,OAAA;AACZ,8GAAA,eAAe,OAAA;AACf,mHAAA,oBAAoB,OAAA;AACpB,qHAAA,sBAAsB,OAAA;AACtB,mHAAA,oBAAoB,OAAA;AAItB,mFAAmF;AACnF,qDAAqD;AAA5C,mHAAA,gBAAgB,OAAA;AAEzB,oFAAoF;AACpF,yCAA2C;AAAlC,yGAAA,YAAY,OAAA"}
@@ -0,0 +1,82 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Module manifest verification — the single source of truth for manifest validation. Exported from
4
+ * `@sovecom/module-sdk`, so the published author SDK, the core runtime, and the contract-test suite
5
+ * all share ONE validator — it is structurally impossible for them to drift.
6
+ *
7
+ * PURE functions — no Nest, no DB, no filesystem, NO code execution. These parse and validate a
8
+ * `sovecom.module.json` already read off disk. The headline security properties are enforced here
9
+ * as data validation:
10
+ * - a closed, default-deny permission allowlist;
11
+ * - every declared table must be namespaced `mod_<name>_*`;
12
+ * - `.strict()` so unknown top-level keys are rejected (supply-chain hygiene);
13
+ * - a byte cap on the raw manifest;
14
+ * - a semver MAJOR gate against `CORE_API_VERSION`.
15
+ */
16
+ /**
17
+ * The closed module-capability permission vocabulary. DISTINCT from the
18
+ * RBAC `PERMISSIONS` that gate admin users — this is what a module may REQUEST. A manifest
19
+ * declaring anything outside this set is rejected at verification (default-deny). v1 set:
20
+ */
21
+ export declare const MODULE_PERMISSION_ALLOWLIST: readonly ["read:products", "read:categories", "read:orders", "read:customers", "write:own_tables", "emit:events", "subscribe:events", "http:outbound", "email:send"];
22
+ export type ModulePermission = (typeof MODULE_PERMISSION_ALLOWLIST)[number];
23
+ /** Hard cap on the raw `sovecom.module.json` byte length (64 KiB). */
24
+ export declare const MANIFEST_MAX_BYTES: number;
25
+ /** Module name: a slug that forms the `mod_<name>_*` prefix and a URL segment. */
26
+ export declare const MODULE_NAME_RE: RegExp;
27
+ /** A slot slug AND a component-id slug (same lowercase-slug shape). */
28
+ export declare const SLOT_SLUG_RE: RegExp;
29
+ /** The suffix after the `mod_<name>_` table prefix: lowercase, [a-z0-9_]. */
30
+ export declare const TABLE_SUFFIX_RE: RegExp;
31
+ /**
32
+ * A single slot DECLARATION: the slot this module FILLS + the component id the
33
+ * storefront maps to the module's UI. Fully declarative metadata; the slot registry is DERIVED from
34
+ * these across all ENABLED modules. `.strict()` so unknown keys are rejected; both fields are
35
+ * bounded lowercase slugs.
36
+ */
37
+ declare const slotEntrySchema: z.ZodObject<{
38
+ slot: z.ZodString;
39
+ component: z.ZodString;
40
+ }, z.core.$strict>;
41
+ export declare const moduleManifestSchema: z.ZodObject<{
42
+ name: z.ZodString;
43
+ displayName: z.ZodString;
44
+ version: z.ZodString;
45
+ compatibleCore: z.ZodString;
46
+ permissions: z.ZodArray<z.ZodEnum<{
47
+ "read:products": "read:products";
48
+ "read:categories": "read:categories";
49
+ "read:orders": "read:orders";
50
+ "read:customers": "read:customers";
51
+ "write:own_tables": "write:own_tables";
52
+ "emit:events": "emit:events";
53
+ "subscribe:events": "subscribe:events";
54
+ "http:outbound": "http:outbound";
55
+ "email:send": "email:send";
56
+ }>>;
57
+ slots: z.ZodOptional<z.ZodArray<z.ZodObject<{
58
+ slot: z.ZodString;
59
+ component: z.ZodString;
60
+ }, z.core.$strict>>>;
61
+ settings: z.ZodOptional<z.ZodObject<{
62
+ schema: z.ZodString;
63
+ }, z.core.$strict>>;
64
+ tables: z.ZodOptional<z.ZodArray<z.ZodString>>;
65
+ }, z.core.$strict>;
66
+ export type ModuleManifest = z.infer<typeof moduleManifestSchema>;
67
+ /** A single declared slot target — `{ slot, component }`. */
68
+ export type ModuleSlotEntry = z.infer<typeof slotEntrySchema>;
69
+ /**
70
+ * Parse + verify a raw `sovecom.module.json` string. Enforces the byte cap, parses JSON
71
+ * (mapping a parse failure to a clear error), then runs the Zod schema. Returns the typed
72
+ * manifest or throws a descriptive `Error`. PURE — no I/O, no code execution.
73
+ */
74
+ export declare function parseAndVerifyManifest(raw: string): ModuleManifest;
75
+ /**
76
+ * Semver gate. The module's `compatibleCore` range must accept the current `CORE_API_VERSION`, AND
77
+ * must do so on the SAME MAJOR — a major bump in core means modules pinned to an old major refuse
78
+ * to load. Throws a clear `Error` on mismatch; the caller maps it to HTTP 422.
79
+ */
80
+ export declare function assertCoreCompatible(manifest: Pick<ModuleManifest, 'compatibleCore'>): void;
81
+ export {};
82
+ //# sourceMappingURL=manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;;;;;;;;;;GAaG;AAEH;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,sKAU9B,CAAC;AAKX,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,2BAA2B,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5E,sEAAsE;AACtE,eAAO,MAAM,kBAAkB,QAAY,CAAC;AAY5C,kFAAkF;AAClF,eAAO,MAAM,cAAc,QAAsB,CAAC;AAClD,uEAAuE;AACvE,eAAO,MAAM,YAAY,QAAsB,CAAC;AAChD,6EAA6E;AAC7E,eAAO,MAAM,eAAe,QAAiB,CAAC;AAE9C;;;;;GAKG;AACH,QAAA,MAAM,eAAe;;;kBASV,CAAC;AAmCZ,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;kBAiC/B,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE,6DAA6D;AAC7D,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAuBlE;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,IAAI,CAAC,cAAc,EAAE,gBAAgB,CAAC,GAAG,IAAI,CAsB3F"}
@@ -0,0 +1,219 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.moduleManifestSchema = exports.TABLE_SUFFIX_RE = exports.SLOT_SLUG_RE = exports.MODULE_NAME_RE = exports.MANIFEST_MAX_BYTES = exports.MODULE_PERMISSION_ALLOWLIST = void 0;
37
+ exports.parseAndVerifyManifest = parseAndVerifyManifest;
38
+ exports.assertCoreCompatible = assertCoreCompatible;
39
+ const semver = __importStar(require("semver"));
40
+ const zod_1 = require("zod");
41
+ const core_version_js_1 = require("./core-version.js");
42
+ /**
43
+ * Module manifest verification — the single source of truth for manifest validation. Exported from
44
+ * `@sovecom/module-sdk`, so the published author SDK, the core runtime, and the contract-test suite
45
+ * all share ONE validator — it is structurally impossible for them to drift.
46
+ *
47
+ * PURE functions — no Nest, no DB, no filesystem, NO code execution. These parse and validate a
48
+ * `sovecom.module.json` already read off disk. The headline security properties are enforced here
49
+ * as data validation:
50
+ * - a closed, default-deny permission allowlist;
51
+ * - every declared table must be namespaced `mod_<name>_*`;
52
+ * - `.strict()` so unknown top-level keys are rejected (supply-chain hygiene);
53
+ * - a byte cap on the raw manifest;
54
+ * - a semver MAJOR gate against `CORE_API_VERSION`.
55
+ */
56
+ /**
57
+ * The closed module-capability permission vocabulary. DISTINCT from the
58
+ * RBAC `PERMISSIONS` that gate admin users — this is what a module may REQUEST. A manifest
59
+ * declaring anything outside this set is rejected at verification (default-deny). v1 set:
60
+ */
61
+ exports.MODULE_PERMISSION_ALLOWLIST = [
62
+ 'read:products',
63
+ 'read:categories',
64
+ 'read:orders',
65
+ 'read:customers',
66
+ 'write:own_tables',
67
+ 'emit:events',
68
+ 'subscribe:events',
69
+ 'http:outbound',
70
+ 'email:send',
71
+ ];
72
+ /** Hard cap on the raw `sovecom.module.json` byte length (64 KiB). */
73
+ exports.MANIFEST_MAX_BYTES = 64 * 1024;
74
+ /** Bounds — generous but finite, to reject pathological manifests up front. */
75
+ const MAX_NAME_LEN = 64;
76
+ const MAX_DISPLAY_NAME_LEN = 128;
77
+ const MAX_PERMISSIONS = exports.MODULE_PERMISSION_ALLOWLIST.length;
78
+ const MAX_SLOTS = 64;
79
+ const MAX_SLOT_LEN = 128;
80
+ const MAX_TABLES = 64;
81
+ const MAX_TABLE_LEN = 128;
82
+ const MAX_SETTINGS_SCHEMA_LEN = 256;
83
+ /** Module name: a slug that forms the `mod_<name>_*` prefix and a URL segment. */
84
+ exports.MODULE_NAME_RE = /^[a-z][a-z0-9-]*$/;
85
+ /** A slot slug AND a component-id slug (same lowercase-slug shape). */
86
+ exports.SLOT_SLUG_RE = /^[a-z][a-z0-9-]*$/;
87
+ /** The suffix after the `mod_<name>_` table prefix: lowercase, [a-z0-9_]. */
88
+ exports.TABLE_SUFFIX_RE = /^[a-z0-9_]+$/;
89
+ /**
90
+ * A single slot DECLARATION: the slot this module FILLS + the component id the
91
+ * storefront maps to the module's UI. Fully declarative metadata; the slot registry is DERIVED from
92
+ * these across all ENABLED modules. `.strict()` so unknown keys are rejected; both fields are
93
+ * bounded lowercase slugs.
94
+ */
95
+ const slotEntrySchema = zod_1.z
96
+ .object({
97
+ slot: zod_1.z.string().min(1).max(MAX_SLOT_LEN).regex(exports.SLOT_SLUG_RE, 'slot must be a lowercase slug'),
98
+ component: zod_1.z
99
+ .string()
100
+ .min(1)
101
+ .max(MAX_SLOT_LEN)
102
+ .regex(exports.SLOT_SLUG_RE, 'component must be a lowercase slug'),
103
+ })
104
+ .strict();
105
+ const permissionEnum = zod_1.z.enum(exports.MODULE_PERMISSION_ALLOWLIST);
106
+ /**
107
+ * The manifest Zod schema. `.strict()` rejects unknown top-level keys. `tables` are
108
+ * validated for shape here and re-checked against the parsed `name` in a `superRefine`
109
+ * below (the namespace prefix depends on the name).
110
+ */
111
+ const baseManifestSchema = zod_1.z
112
+ .object({
113
+ name: zod_1.z
114
+ .string()
115
+ .min(1)
116
+ .max(MAX_NAME_LEN)
117
+ .regex(exports.MODULE_NAME_RE, 'name must be a lowercase slug like "wishlist"'),
118
+ displayName: zod_1.z.string().min(1).max(MAX_DISPLAY_NAME_LEN),
119
+ version: zod_1.z
120
+ .string()
121
+ .max(64)
122
+ .refine((v) => semver.valid(v) !== null, 'version must be a valid semver'),
123
+ compatibleCore: zod_1.z
124
+ .string()
125
+ .max(256)
126
+ .refine((v) => semver.validRange(v) !== null, 'compatibleCore must be a valid semver range'),
127
+ permissions: zod_1.z.array(permissionEnum).max(MAX_PERMISSIONS),
128
+ slots: zod_1.z.array(slotEntrySchema).max(MAX_SLOTS).optional(),
129
+ settings: zod_1.z
130
+ .object({ schema: zod_1.z.string().min(1).max(MAX_SETTINGS_SCHEMA_LEN) })
131
+ .strict()
132
+ .optional(),
133
+ tables: zod_1.z.array(zod_1.z.string().min(1).max(MAX_TABLE_LEN)).max(MAX_TABLES).optional(),
134
+ })
135
+ .strict();
136
+ exports.moduleManifestSchema = baseManifestSchema.superRefine((manifest, ctx) => {
137
+ // Every declared table must be namespaced to THIS module: `mod_<name>_<suffix>` (modules never
138
+ // touch core tables). The prefix is derived from the parsed name, so this cross-field check lives
139
+ // here. We compare the prefix as a LITERAL string and validate the suffix with a STATIC regex —
140
+ // never build a dynamic `RegExp` from `manifest.name`. (The slug `MODULE_NAME_RE` already forbids
141
+ // regex metacharacters, so interpolation would be safe today, but a literal compare is
142
+ // injection-proof regardless of how the slug rule evolves.)
143
+ const prefix = `mod_${manifest.name}_`;
144
+ (manifest.tables ?? []).forEach((table, i) => {
145
+ if (!(table.startsWith(prefix) && exports.TABLE_SUFFIX_RE.test(table.slice(prefix.length)))) {
146
+ ctx.addIssue({
147
+ code: zod_1.z.ZodIssueCode.custom,
148
+ path: ['tables', i],
149
+ message: `table "${table}" must be namespaced "${prefix}<name>" (lowercase, [a-z0-9_])`,
150
+ });
151
+ }
152
+ });
153
+ // A module fills any given slot at most once. Declaring the same slot twice (with different
154
+ // components) is meaningless and would otherwise be read as a one-module self-"conflict" the
155
+ // registry can only resolve by silently keeping the first component (the registry derives one
156
+ // component per (module, slot)). Reject the duplicate at the boundary.
157
+ const seenSlots = new Set();
158
+ (manifest.slots ?? []).forEach((entry, i) => {
159
+ if (seenSlots.has(entry.slot)) {
160
+ ctx.addIssue({
161
+ code: zod_1.z.ZodIssueCode.custom,
162
+ path: ['slots', i, 'slot'],
163
+ message: `slot "${entry.slot}" is declared more than once; a module may target a slot at most once`,
164
+ });
165
+ }
166
+ seenSlots.add(entry.slot);
167
+ });
168
+ });
169
+ /**
170
+ * Parse + verify a raw `sovecom.module.json` string. Enforces the byte cap, parses JSON
171
+ * (mapping a parse failure to a clear error), then runs the Zod schema. Returns the typed
172
+ * manifest or throws a descriptive `Error`. PURE — no I/O, no code execution.
173
+ */
174
+ function parseAndVerifyManifest(raw) {
175
+ const byteLen = Buffer.byteLength(raw, 'utf8');
176
+ if (byteLen > exports.MANIFEST_MAX_BYTES) {
177
+ throw new Error(`module manifest too large: ${byteLen} bytes exceeds the ${exports.MANIFEST_MAX_BYTES}-byte cap`);
178
+ }
179
+ let parsed;
180
+ try {
181
+ parsed = JSON.parse(raw);
182
+ }
183
+ catch {
184
+ throw new Error('module manifest is not valid JSON');
185
+ }
186
+ const result = exports.moduleManifestSchema.safeParse(parsed);
187
+ if (!result.success) {
188
+ const detail = result.error.issues
189
+ .map((issue) => `${issue.path.join('.') || '(root)'}: ${issue.message}`)
190
+ .join('; ');
191
+ throw new Error(`invalid module manifest: ${detail}`);
192
+ }
193
+ return result.data;
194
+ }
195
+ /**
196
+ * Semver gate. The module's `compatibleCore` range must accept the current `CORE_API_VERSION`, AND
197
+ * must do so on the SAME MAJOR — a major bump in core means modules pinned to an old major refuse
198
+ * to load. Throws a clear `Error` on mismatch; the caller maps it to HTTP 422.
199
+ */
200
+ function assertCoreCompatible(manifest) {
201
+ const range = manifest.compatibleCore;
202
+ const satisfies = semver.satisfies(core_version_js_1.CORE_API_VERSION, range);
203
+ const coreMajor = semver.major(core_version_js_1.CORE_API_VERSION);
204
+ // Confirm the range's LOWER BOUND shares the core MAJOR. Combined with the `satisfies`
205
+ // check above this means: the current core must fall inside the range AND the range may not
206
+ // begin below/above the core major. So `^1.0.0`/`>=1.0.0`/`1.x` pass on a 1.x core, while a
207
+ // module pinned to an old major (`^0.x`, `<=0.9`) or a future-only major (`>=2`, `^2.0.0`)
208
+ // is refused. A range whose lower bound is the core major but whose UPPER bound spills into
209
+ // a later major (e.g. `>=1.0.0 <3.0.0`) is still accepted — the module legitimately supports
210
+ // this core; it will be re-gated (and refused) once core itself bumps to that later major,
211
+ // because the lower-bound-major check then no longer matches.
212
+ const min = semver.minVersion(range);
213
+ const sameMajor = min !== null && semver.major(min) === coreMajor;
214
+ if (!satisfies || !sameMajor) {
215
+ throw new Error(`module is not compatible with this core: requires "${range}" but core API is ` +
216
+ `${core_version_js_1.CORE_API_VERSION} (major ${coreMajor})`);
217
+ }
218
+ }
219
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * the author-facing module contract + `defineModule` helper.
3
+ *
4
+ * `SovecomModule` is the shape the forked worker loader validates
5
+ * (`apps/api/src/modules/runtime/worker-entry.ts`: `typeof resolved.activate === 'function'`).
6
+ * It is EXTRACTED here so authors and the loader share one definition.
7
+ *
8
+ * `defineModule(config)` is the ergonomic entrypoint: the author writes their `activate(sdk)`
9
+ * body and this returns the exact `{ activate }` object the loader expects. The shape is
10
+ * validated at the boundary (defence in depth — the loader also re-checks) so a misconfigured
11
+ * module fails fast with a clear message at author build time, not as an opaque worker crash.
12
+ */
13
+ import type { ModuleSdk } from './capabilities.js';
14
+ /** The contract a module must satisfy: a single `activate(sdk)` entrypoint. */
15
+ export interface SovecomModule {
16
+ activate(sdk: ModuleSdk): void | Promise<void>;
17
+ }
18
+ /** Author-supplied config for {@link defineModule}. v1: just the `activate` body. */
19
+ export interface DefineModuleConfig {
20
+ /** The module body. Called once by core, inside the worker, with the capability SDK. */
21
+ activate(sdk: ModuleSdk): void | Promise<void>;
22
+ }
23
+ /**
24
+ * Wrap an author's `activate` into the `{ activate }` object the worker loader expects.
25
+ * Validates the config shape at the boundary and throws a clear error if `activate` is missing
26
+ * or not a function — never let a malformed module reach the runtime as a silent no-op.
27
+ */
28
+ export declare function defineModule(config: DefineModuleConfig): SovecomModule;
29
+ //# sourceMappingURL=module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEnD,+EAA+E;AAC/E,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD;AAED,qFAAqF;AACrF,MAAM,WAAW,kBAAkB;IACjC,wFAAwF;IACxF,QAAQ,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,kBAAkB,GAAG,aAAa,CAStE"}
package/dist/module.js ADDED
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.defineModule = defineModule;
4
+ /**
5
+ * Wrap an author's `activate` into the `{ activate }` object the worker loader expects.
6
+ * Validates the config shape at the boundary and throws a clear error if `activate` is missing
7
+ * or not a function — never let a malformed module reach the runtime as a silent no-op.
8
+ */
9
+ function defineModule(config) {
10
+ if (config === null || typeof config !== 'object') {
11
+ throw new TypeError('defineModule(config): config must be an object');
12
+ }
13
+ if (typeof config.activate !== 'function') {
14
+ throw new TypeError('defineModule(config): config.activate must be a function');
15
+ }
16
+ const { activate } = config;
17
+ return { activate: (sdk) => activate(sdk) };
18
+ }
19
+ //# sourceMappingURL=module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module.js","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":";;AA8BA,oCASC;AAdD;;;;GAIG;AACH,SAAgB,YAAY,CAAC,MAA0B;IACrD,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAClD,MAAM,IAAI,SAAS,CAAC,gDAAgD,CAAC,CAAC;IACxE,CAAC;IACD,IAAI,OAAO,MAAM,CAAC,QAAQ,KAAK,UAAU,EAAE,CAAC;QAC1C,MAAM,IAAI,SAAS,CAAC,0DAA0D,CAAC,CAAC;IAClF,CAAC;IACD,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC;IAC5B,OAAO,EAAE,QAAQ,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;AAC9C,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * a typed AUTHORING helper for the manifest's
3
+ * declarative `slots: { slot, component }[]` array.
4
+ *
5
+ * Slots are DECLARATIVE metadata; the slot registry is DERIVED from enabled modules' manifests.
6
+ * This helper is NOT an RPC and there is NO runtime `registerSlot()` (the `register:slot`
7
+ * capability was removed). It exists purely so an author can build a typed, validated slot array
8
+ * at author time, mirroring the same rules the manifest schema enforces (lowercase slug shape;
9
+ * a module fills any given slot at most once). Its output is dropped verbatim into the manifest.
10
+ */
11
+ import { type ModuleSlotEntry } from './manifest.js';
12
+ /**
13
+ * Validate + return a slot-declaration array for the manifest. Enforces the SAME rules as
14
+ * `moduleManifestSchema`:
15
+ * - `slot` and `component` are non-empty lowercase slugs (`^[a-z][a-z0-9-]*$`);
16
+ * - no slot is declared more than once.
17
+ * Throws a clear `Error` on the first violation. Returns a fresh, frozen array.
18
+ */
19
+ export declare function defineSlots(entries: ReadonlyArray<ModuleSlotEntry>): readonly ModuleSlotEntry[];
20
+ //# sourceMappingURL=slots.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slots.d.ts","sourceRoot":"","sources":["../src/slots.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAgB,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AAEnE;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,aAAa,CAAC,eAAe,CAAC,GAAG,SAAS,eAAe,EAAE,CA0B/F"}
package/dist/slots.js ADDED
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.defineSlots = defineSlots;
4
+ /**
5
+ * a typed AUTHORING helper for the manifest's
6
+ * declarative `slots: { slot, component }[]` array.
7
+ *
8
+ * Slots are DECLARATIVE metadata; the slot registry is DERIVED from enabled modules' manifests.
9
+ * This helper is NOT an RPC and there is NO runtime `registerSlot()` (the `register:slot`
10
+ * capability was removed). It exists purely so an author can build a typed, validated slot array
11
+ * at author time, mirroring the same rules the manifest schema enforces (lowercase slug shape;
12
+ * a module fills any given slot at most once). Its output is dropped verbatim into the manifest.
13
+ */
14
+ const manifest_js_1 = require("./manifest.js");
15
+ /**
16
+ * Validate + return a slot-declaration array for the manifest. Enforces the SAME rules as
17
+ * `moduleManifestSchema`:
18
+ * - `slot` and `component` are non-empty lowercase slugs (`^[a-z][a-z0-9-]*$`);
19
+ * - no slot is declared more than once.
20
+ * Throws a clear `Error` on the first violation. Returns a fresh, frozen array.
21
+ */
22
+ function defineSlots(entries) {
23
+ if (!Array.isArray(entries)) {
24
+ throw new TypeError('defineSlots(entries): entries must be an array');
25
+ }
26
+ const seen = new Set();
27
+ const out = [];
28
+ for (const entry of entries) {
29
+ if (entry === null || typeof entry !== 'object') {
30
+ throw new TypeError('defineSlots: each entry must be an object { slot, component }');
31
+ }
32
+ const { slot, component } = entry;
33
+ if (typeof slot !== 'string' || !manifest_js_1.SLOT_SLUG_RE.test(slot)) {
34
+ throw new Error(`defineSlots: slot "${String(slot)}" must be a lowercase slug`);
35
+ }
36
+ if (typeof component !== 'string' || !manifest_js_1.SLOT_SLUG_RE.test(component)) {
37
+ throw new Error(`defineSlots: component "${String(component)}" must be a lowercase slug`);
38
+ }
39
+ if (seen.has(slot)) {
40
+ throw new Error(`defineSlots: slot "${slot}" is declared more than once; a module may target a slot at most once`);
41
+ }
42
+ seen.add(slot);
43
+ out.push({ slot, component });
44
+ }
45
+ return Object.freeze(out);
46
+ }
47
+ //# sourceMappingURL=slots.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slots.js","sourceRoot":"","sources":["../src/slots.ts"],"names":[],"mappings":";;AAmBA,kCA0BC;AA7CD;;;;;;;;;GASG;AACH,+CAAmE;AAEnE;;;;;;GAMG;AACH,SAAgB,WAAW,CAAC,OAAuC;IACjE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,SAAS,CAAC,gDAAgD,CAAC,CAAC;IACxE,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,GAAG,GAAsB,EAAE,CAAC;IAClC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAChD,MAAM,IAAI,SAAS,CAAC,+DAA+D,CAAC,CAAC;QACvF,CAAC;QACD,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;QAClC,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,0BAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACzD,MAAM,IAAI,KAAK,CAAC,sBAAsB,MAAM,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;QAClF,CAAC;QACD,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,CAAC,0BAAY,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;YACnE,MAAM,IAAI,KAAK,CAAC,2BAA2B,MAAM,CAAC,SAAS,CAAC,4BAA4B,CAAC,CAAC;QAC5F,CAAC;QACD,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CACb,sBAAsB,IAAI,uEAAuE,CAClG,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACf,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Build the namespaced DDL identifier `mod_<module>_<suffix>` for an author's own table.
3
+ *
4
+ * @param moduleName the module's manifest `name` (lowercase slug, `^[a-z][a-z0-9-]*$`).
5
+ * @param suffix the table's local name within the module (lowercase, `[a-z0-9_]+`).
6
+ * @returns the fully-namespaced table name, e.g. `createNamespacedTable('wishlist', 'items')`
7
+ * → `'mod_wishlist_items'`.
8
+ * @throws if `moduleName` is not a valid module slug or `suffix` is empty/invalid.
9
+ */
10
+ export declare function createNamespacedTable(moduleName: string, suffix: string): string;
11
+ //# sourceMappingURL=tables.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../src/tables.ts"],"names":[],"mappings":"AAgBA;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAkBhF"}