@izumisy-tailor/omakase-modules 0.1.0 → 0.2.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/README.md +59 -0
- package/docs/examples/data-models/core/inventory-module.md +230 -0
- package/docs/examples/data-models/core/order-module.md +132 -0
- package/docs/examples/data-models/scenarios/inventory-reservation-scenario.md +73 -0
- package/docs/examples/data-models/scenarios/multi-storefront-order-scenario.md +99 -0
- package/docs/examples/data-models/scenarios/order-payment-status-scenario.md +92 -0
- package/docs/examples/data-models/scenarios/procurement-order-scenario.md +95 -0
- package/docs/tutorials/creating-modules.md +256 -0
- package/docs/tutorials/using-modules.md +174 -0
- package/package.json +12 -18
- package/src/builder/helpers.ts +51 -0
- package/src/builder/index.ts +7 -0
- package/src/builder/register.ts +34 -0
- package/src/config/index.ts +1 -0
- package/src/config/module-loader.ts +85 -0
- package/src/config/module-registry.ts +22 -0
- package/src/config/sdk/index.ts +1 -0
- package/src/config/sdk/paths.ts +20 -0
- package/src/stub-loader/index.ts +3 -0
- package/src/stub-loader/interface.ts +40 -0
- package/dist/builder/index.d.mts +0 -3
- package/dist/builder/index.mjs +0 -35
- package/dist/config/index.d.mts +0 -2
- package/dist/config/index.mjs +0 -62
- package/dist/config/sdk/index.d.mts +0 -10
- package/dist/config/sdk/index.mjs +0 -14
- package/dist/helpers-CNjRbrYN.d.mts +0 -29
- package/dist/index-BoAL29Di.d.mts +0 -38
- package/dist/module-loader-B4sA1i0A.d.mts +0 -38
- package/dist/stub-loader/index.d.mts +0 -2
- package/dist/stub-loader/index.mjs +0 -31
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Order Payment Status Scenario
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
This document captures a reference flow where the order module owns the canonical order status while collaborating with optional billing, authorization, and cash receipt modules. The order module remains usable as a standalone component—for example, back-office staff can record invoice or payment outcomes manually—while downstream modules add automation by reacting to order events and pushing their results back through status history entries. The shared contract that keeps order independent is defined in [Order Module Core Contract](../order-module-core-contract.md).
|
|
5
|
+
|
|
6
|
+
## Module Dependencies
|
|
7
|
+
|
|
8
|
+
| Module (package path) | Declared dependencies | Purpose |
|
|
9
|
+
| --- | --- | --- |
|
|
10
|
+
| Order (`packages/order-module`) | — | Publishes the `order.orderIntent` contract, stores canonical orders, and exposes status history consumed by downstream financial modules. |
|
|
11
|
+
| Payment authorization (`packages/payment-authorization-module`) | Order | Manages authorization holds/captures against external PSPs and appends payment statuses to `orderStatusHistory`. |
|
|
12
|
+
| Billing (`packages/billing-module`) | Order, Payment authorization | Generates invoices (manually or automatically), links them to authorizations, and writes billing milestones into `orderStatusHistory`. |
|
|
13
|
+
| Cash receipt (`packages/cash-receipt-module`) | Order, Billing | Records cash inflows, allocates them to invoices, and publishes settlement statuses into `orderStatusHistory`. |
|
|
14
|
+
|
|
15
|
+
## TailorDB Tables and Views
|
|
16
|
+
### Payment authorization module
|
|
17
|
+
- `authorization.request`: Outbound authorization attempts for a given order.
|
|
18
|
+
- `authorization.result`: Holds/voids/captures returned from the PSP.
|
|
19
|
+
- Appends `payment:*` entries (e.g., `payment:authorized`, `payment:capture_failed`) to `orderStatusHistory` via the order dependency.
|
|
20
|
+
|
|
21
|
+
### Billing module
|
|
22
|
+
- `billing.invoice`: Invoices generated for orders.
|
|
23
|
+
- `billing.invoiceStatus`: Derived view summarizing invoice state (issued, posted, settled).
|
|
24
|
+
- Appends `billing:*` milestones (e.g., `billing:invoice_issued`, `billing:settled`) to `orderStatusHistory` and maintains projections for finance dashboards.
|
|
25
|
+
|
|
26
|
+
### Cash receipt module
|
|
27
|
+
- `cash.receipt`: Records individual cash inflows.
|
|
28
|
+
- `cash.receiptAllocation`: Maps receipts to invoices.
|
|
29
|
+
- Appends `cash:*` history entries (`cash:payment_applied`, `cash:refund_requested`) to provide audit traces within `orderStatusHistory`.
|
|
30
|
+
|
|
31
|
+
## Order Status
|
|
32
|
+
|
|
33
|
+
Baseline `order:*` statuses are documented in [Order Module Core Contract](../order-module-core-contract.md#order-status-vocabulary). Financial automation contributes the following additional entries:
|
|
34
|
+
|
|
35
|
+
| Label | Writer | Trigger timing | Notes |
|
|
36
|
+
| --- | --- | --- | --- |
|
|
37
|
+
| `payment:authorization_pending` | Payment authorization | Immediately after `paymentAuthorizationIntake` enqueues an authorization attempt. | Communicates that an authorization request is in flight. |
|
|
38
|
+
| `payment:authorized` | Payment authorization | When the PSP returns a successful authorization or capture. | Used to gate invoice issuance or fulfilment release. |
|
|
39
|
+
| `payment:capture_failed` | Payment authorization | Upon PSP decline or technical failure. | Prompts support teams to investigate or retry. |
|
|
40
|
+
| `billing:invoice_requested` | Billing | When `billingOrderIntake` decides to create an invoice. | Marks the start of the billing workflow even before the invoice record exists. |
|
|
41
|
+
| `billing:invoice_issued` | Billing | After an invoice is generated. | Downstream consumers can inform customers or trigger collections. |
|
|
42
|
+
| `billing:settled` | Billing | Once the invoice is fully settled in billing systems. | Often paired with cash entries for complete reconciliation. |
|
|
43
|
+
| `cash:payment_applied` | Cash receipt | When cash is allocated to the invoice covering the order. | Confirms funds have been received and matched. |
|
|
44
|
+
| `cash:refund_requested` | Cash receipt | If a refund or reversal is initiated. | Allows order timelines to reflect outstanding customer adjustments. |
|
|
45
|
+
|
|
46
|
+
## Executor Triggers
|
|
47
|
+
|
|
48
|
+
| Trigger table (module) | Writers (modules) | Activation timing | Target executor (module) | Responsibility |
|
|
49
|
+
| --- | --- | --- | --- | --- |
|
|
50
|
+
| `order.order` (order) | Order (owner), back-office operators | Record creation or update (CDC) | `billingOrderIntake` (billing) | React to new or updated orders, create invoices when automation is enabled, and append `billing:invoice_requested` statuses. |
|
|
51
|
+
| `order.order` (order) | Order (owner), back-office operators | Record creation or update (CDC) | `paymentAuthorizationIntake` (payment authorization) | Initiate or refresh authorization attempts based on order state or manual triggers; append `payment:authorization_pending` status. |
|
|
52
|
+
| `authorization.result` (payment authorization) | Payment authorization (owner) | Record creation or update (CDC) | `authorizationStatusWriter` (payment authorization) | Append authorization outcomes to `orderStatusHistory` (`payment:authorized`, `payment:capture_failed`) and refresh module projections. |
|
|
53
|
+
| `billing.invoice` (billing) | Billing (owner), operators | Record creation or update (CDC) | `billingStatusWriter` (billing) | Append invoice lifecycle entries (`billing:invoice_issued`, `billing:invoice_posted`) to `orderStatusHistory`. |
|
|
54
|
+
| `billing.invoiceStatus` (billing) | Billing (derived) | View refresh | `billingSettlementWriter` (billing) | Append settlement milestones (`billing:settled`) and update finance dashboards. |
|
|
55
|
+
| `cash.receiptAllocation` (cash receipt) | Cash receipt (owner) | Record creation or update (CDC) | `cashReceiptOrderWriter` (cash receipt) | Append `cash:payment_applied` entries and emit reconciliation tasks when allocations differ from expectations. |
|
|
56
|
+
|
|
57
|
+
## Flow
|
|
58
|
+
1. A new order intent is submitted (automated integration or operator input). The order module validates it, persists `order.order`, and emits a CDC event.
|
|
59
|
+
2. The billing and payment authorization modules listen to that CDC stream. If enabled, their executors (`billingOrderIntake`, `paymentAuthorizationIntake`) create invoices or authorization requests and append pending statuses (`billing:invoice_requested`, `payment:authorization_pending`). Otherwise they ignore the event, allowing manual workflows.
|
|
60
|
+
3. Authorization results are written to `authorization.result`. The `authorizationStatusWriter` executor appends outcomes such as `payment:authorized` or `payment:capture_failed` to `orderStatusHistory`, enabling order consumers to track financial progress without exposing PSP details.
|
|
61
|
+
4. Billing lifecycle changes (automation or manual invoice entry) update `billing.invoice` / `billing.invoiceStatus`. Billing executors append milestones (`billing:invoice_issued`, `billing:settled`) to `orderStatusHistory` and refresh their projections.
|
|
62
|
+
5. Cash receipts allocate payments to invoices via `cash.receiptAllocation`. The cash receipt executor appends settlement statuses (`cash:payment_applied`) so customer service teams can view payment confirmations from the order timeline.
|
|
63
|
+
6. Operators can always append manual entries to `orderStatusHistory` (e.g., `manual:write_off`) to reflect adjustments; downstream modules treat those entries as authoritative signals and reconcile their own records accordingly.
|
|
64
|
+
|
|
65
|
+
```mermaid
|
|
66
|
+
sequenceDiagram
|
|
67
|
+
actor Operator
|
|
68
|
+
participant Order
|
|
69
|
+
participant Billing
|
|
70
|
+
participant Auth as PaymentAuth
|
|
71
|
+
participant Cash
|
|
72
|
+
|
|
73
|
+
Operator->>Order: submit orderIntent (manual or automated)
|
|
74
|
+
Order->>Order: orderIntentIngestor creates order.order
|
|
75
|
+
Order-->>Billing: order.order CDC event
|
|
76
|
+
Billing->>Billing: billingOrderIntake decides on automation
|
|
77
|
+
Billing->>Order: append billing:* status history entries
|
|
78
|
+
Order-->>Auth: order.order CDC event
|
|
79
|
+
Auth->>Auth: paymentAuthorizationIntake initiates auth request
|
|
80
|
+
Auth->>Order: append payment:* status history entries
|
|
81
|
+
Billing->>Billing: invoice lifecycle progresses
|
|
82
|
+
Billing->>Order: append billing:settled when complete
|
|
83
|
+
Cash-->>Billing: cash.receiptAllocation links receipt to invoice
|
|
84
|
+
Cash->>Order: append cash:payment_applied status
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Design Considerations
|
|
88
|
+
- Keep configuration dependencies directional: downstream financial modules depend on order, not the other way around, following the guidance in `../order-module-core-contract.md`.
|
|
89
|
+
- Downstream modules append to `orderStatusHistory` using namespace-qualified status codes instead of mutating `order.order` fields, preserving the clean core.
|
|
90
|
+
- CDC-driven automation should be idempotent and tolerant of manual changes, reconciling rather than overwriting operator inputs.
|
|
91
|
+
- Maintain documented vocabularies for `payment:*`, `billing:*`, and `cash:*` statuses so shared dashboards interpret them consistently.
|
|
92
|
+
- Leverage projections (e.g., `order.financialStatusProjection`) for read-heavy contexts while keeping `orderStatusHistory` as the authoritative audit trail.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Procurement Order Scenario
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
This document illustrates how an order module cooperates with a procurement module when customer demand is fulfilled through supplier purchase orders rather than internal stock. The order module remains a clean, customer-facing source of truth, while procurement owns supplier relationships, purchase orders, and inbound logistics. The goal is to let organizations operate mixed models (stocked items vs. drop-ship) without embedding procurement concerns into the core order schema. The shared order contract that keeps the core independent is described in [Order Module Core Contract](../order-module-core-contract.md).
|
|
5
|
+
|
|
6
|
+
## Module Dependencies
|
|
7
|
+
|
|
8
|
+
| Module (package path) | Declared dependencies | Purpose |
|
|
9
|
+
| --- | --- | --- |
|
|
10
|
+
| Order (`packages/order-module`) | — | Publishes the `order.orderIntent` contract, stores canonical orders, and exposes status history for downstream modules. |
|
|
11
|
+
| Procurement (`packages/procurement-module`) | Order, Commerce core | Generates and tracks supplier purchase orders, manages inbound shipments, and appends procurement milestones to `orderStatusHistory`. |
|
|
12
|
+
| Commerce core (`packages/commerce-core-module`) | — | Provides product master data, supplier catalogs, and lead-time metadata consumed by procurement. |
|
|
13
|
+
| Inventory (`packages/inventory-module`) | Procurement, Commerce core | Optionally tracks goods received into stock or cross-docking locations. |
|
|
14
|
+
| Billing (`packages/billing-module`) | Order | Handles customer invoicing; may wait for procurement confirmation before finalizing revenue. |
|
|
15
|
+
|
|
16
|
+
## TailorDB Tables and Views
|
|
17
|
+
### Procurement module
|
|
18
|
+
- `procurement.purchaseOrder`: Supplier-facing purchase orders referencing `orderId` and line allocations.
|
|
19
|
+
- `procurement.supplier`: Master data for suppliers, including lead times, payment terms, and partner IDs.
|
|
20
|
+
- `procurement.inboundShipment`: Tracks ASN (advance shipping notice), shipment status, and expected arrival.
|
|
21
|
+
- `procurement.exceptions`: Captures issues such as supplier delays, quantity shortages, or quality holds.
|
|
22
|
+
- `procurement.orderStatus`: Projection of procurement milestones per order (PO issued, supplier confirmed, shipment dispatched, received).
|
|
23
|
+
- Appends `procurement:*` entries to `orderStatusHistory` for shared visibility while keeping detailed supplier data inside procurement-owned tables.
|
|
24
|
+
|
|
25
|
+
### Inventory module (optional)
|
|
26
|
+
- Uses core inventory tables (`inventory.stock`, `inventory.stockLedger`, etc.) as defined in `../core/inventory-module.md` to reflect inbound receipts and stock updates; not redefined here.
|
|
27
|
+
- `inventory.receipt`: Scenario-specific record for booking inbound shipments into stock or cross-dock locations.
|
|
28
|
+
|
|
29
|
+
## Order Status
|
|
30
|
+
|
|
31
|
+
Baseline `order:*` statuses are defined in [Order Module Core Contract](../order-module-core-contract.md#order-status-vocabulary). Procurement adds the following milestones:
|
|
32
|
+
|
|
33
|
+
| Label | Writer | Trigger timing | Notes |
|
|
34
|
+
| --- | --- | --- | --- |
|
|
35
|
+
| `procurement:po_issued` | Procurement module | Immediately after `procurementOrderIntake` creates a purchase order. | Indicates supplier engagement has started. |
|
|
36
|
+
| `procurement:po_confirmed` | Procurement module | When the supplier confirms the purchase order. | Confirms expected ship dates and quantities. |
|
|
37
|
+
| `procurement:asn_received` | Procurement module | Upon receiving an ASN or shipment notice. | Signals inbound logistics to prepare for receipt. |
|
|
38
|
+
| `procurement:received` | Procurement module / Inventory module | When goods are fully received and reconciled. | Allows billing or fulfilment to proceed confidently. |
|
|
39
|
+
| `procurement:short_shipment` | Procurement module | If received quantity is less than ordered. | Highlights the need for back-order or customer communication. |
|
|
40
|
+
| `procurement:blocking_exception` | Procurement module | When an exception (delay, quality hold) blocks fulfilment. | Downstream modules should pause until resolved. |
|
|
41
|
+
|
|
42
|
+
## Executor Triggers
|
|
43
|
+
|
|
44
|
+
| Trigger table (module) | Writers (modules) | Activation timing | Target executor (module) | Responsibility |
|
|
45
|
+
| --- | --- | --- | --- | --- |
|
|
46
|
+
| `order.order` (order) | Order (owner), operators | Record creation or update (CDC) | `procurementOrderIntake` (procurement) | Decide whether a purchase order is needed based on product supply policies and current stock; create or update `procurement.purchaseOrder` rows. |
|
|
47
|
+
| `procurement.purchaseOrder` (procurement) | Procurement (owner) | Record creation or update (CDC) | `procurementPOStatusProjector` (procurement) | Update supplier confirmations, expected ship dates, and project milestones into `procurement.orderStatus`. |
|
|
48
|
+
| `procurement.inboundShipment` (procurement) | Procurement (owner), suppliers via EDI | Record creation or update (CDC) | `procurementInboundCoordinator` (inventory) | Trigger inbound logistics tasks, e.g., allocate receiving docks or generate `inventory.receipt` placeholders. |
|
|
49
|
+
| `inventory.receipt` (inventory) | Inventory (owner) | Record creation (CDC) | `procurementReceiptReconciler` (procurement) | Match receipts to purchase orders, update quantities received, and close out procurement exceptions. |
|
|
50
|
+
| `procurement.exceptions` (procurement) | Procurement (owner), operators | Record creation or update (CDC) | `procurementExceptionProjector` (procurement) | Reflect exception state in `procurement.orderStatus` and expose signals (e.g., blocks) for downstream modules. |
|
|
51
|
+
|
|
52
|
+
## Flow
|
|
53
|
+
1. An order intent is submitted. After validation, the order module persists `order.order` and emits a CDC event that triggers `procurementOrderIntake`.
|
|
54
|
+
2. Procurement evaluates supply policies, available stock, and supplier contracts. If a supplier order is required, it creates `procurement.purchaseOrder` entries linking each order line to a supplier and expected ship date, and appends `procurement:po_issued` to `orderStatusHistory`.
|
|
55
|
+
3. Suppliers confirm the purchase order through EDI/API or operator input. `procurement.purchaseOrder` updates drive `procurementPOStatusProjector`, which writes milestones (confirmed, delayed, partial) into `procurement.orderStatus` and appends corresponding entries (e.g., `procurement:po_confirmed`) to `orderStatusHistory`.
|
|
56
|
+
4. When suppliers send advance shipping notices, `procurement.inboundShipment` records are created. `procurementInboundCoordinator` may allocate receiving resources or pre-create `inventory.receipt` placeholders while recording `procurement:asn_received` in the shared history.
|
|
57
|
+
5. Upon physical receipt, the inventory module records `inventory.receipt`, triggering `procurementReceiptReconciler` to update received quantities, close or escalate procurement exceptions, and append `procurement:received` or `procurement:short_shipment` statuses for order-facing consumers.
|
|
58
|
+
6. Any exceptions (short shipments, quality holds, supplier delays) are logged in `procurement.exceptions`. `procurementExceptionProjector` updates `procurement.orderStatus` and surfaces blocking flags via `orderStatusHistory` so downstream modules can pause fulfilment or billing.
|
|
59
|
+
7. Once procurement confirms completion, order and billing experiences reference `procurement.orderStatus` alongside the shared history to release fulfilment and issue invoices when appropriate.
|
|
60
|
+
|
|
61
|
+
```mermaid
|
|
62
|
+
sequenceDiagram
|
|
63
|
+
actor Customer
|
|
64
|
+
participant Order
|
|
65
|
+
participant Procurement
|
|
66
|
+
actor Supplier
|
|
67
|
+
participant Inventory
|
|
68
|
+
participant Billing
|
|
69
|
+
|
|
70
|
+
Customer->>Order: submit orderIntent
|
|
71
|
+
Order->>Order: orderIntentIngestor creates order.order
|
|
72
|
+
Order-->>Procurement: order.order CDC event
|
|
73
|
+
Procurement->>Procurement: procurementOrderIntake creates purchaseOrder
|
|
74
|
+
Procurement->>Order: append procurement:po_issued status
|
|
75
|
+
Procurement->>Supplier: send purchase order
|
|
76
|
+
Supplier-->>Procurement: confirmation updates purchaseOrder
|
|
77
|
+
Procurement->>Order: append procurement:po_confirmed status
|
|
78
|
+
Supplier-->>Procurement: send inboundShipment/ASN
|
|
79
|
+
Procurement->>Order: append procurement:asn_received status
|
|
80
|
+
Procurement->>Inventory: procurementInboundCoordinator prepares receipt
|
|
81
|
+
Inventory->>Inventory: record inventory.receipt
|
|
82
|
+
Inventory-->>Order: append procurement:received / procurement:short_shipment statuses
|
|
83
|
+
Procurement->>Procurement: procurementExceptionProjector updates orderStatus view
|
|
84
|
+
Procurement-->>Order: expose orderStatus for fulfillment/billing decisions
|
|
85
|
+
Order-->>Billing: release for invoicing once procurement complete
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Design Considerations
|
|
89
|
+
- Keep procurement logic modular so order can operate without it (e.g., stocked items). The intake executor should be configuration-driven (per SKU, channel, or merchant) and must adhere to `../order-module-core-contract.md`.
|
|
90
|
+
- Purchase orders may aggregate multiple customer orders; ensure `procurement.purchaseOrder` links to both supplier PO numbers and individual order line allocations.
|
|
91
|
+
- Exceptions should be first-class records with lifecycle management, enabling reporting and SLA tracking.
|
|
92
|
+
- Append procurement milestones to `orderStatusHistory` with namespace-qualified codes (`procurement:*`) so order-facing teams share a single timeline.
|
|
93
|
+
- Inventory integration is optional: drop-ship flows might bypass inventory entirely, while cross-docking requires temporary receipts.
|
|
94
|
+
- Billing should respect procurement status, especially when revenue recognition depends on supplier confirmation or goods receipt.
|
|
95
|
+
- Expose `procurement.orderStatus` as a read-only interface for order, billing, or customer-facing channels rather than duplicating timelines inside the order schema.
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
# Creating Modules
|
|
2
|
+
|
|
3
|
+
This guide explains how to create reusable Omakase Modules that can be shared across applications.
|
|
4
|
+
|
|
5
|
+
## Module Structure
|
|
6
|
+
|
|
7
|
+
A typical module package has this structure:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
my-module/
|
|
11
|
+
package.json
|
|
12
|
+
tsconfig.json
|
|
13
|
+
src/
|
|
14
|
+
module.ts # Module definition
|
|
15
|
+
types.ts # Configuration types
|
|
16
|
+
tailordb/
|
|
17
|
+
index.ts # TailorDB exports
|
|
18
|
+
context.ts # Type definitions for tables
|
|
19
|
+
my-table.ts # Table builders
|
|
20
|
+
resolvers/
|
|
21
|
+
my-resolver.ts
|
|
22
|
+
executors/
|
|
23
|
+
my-executor.ts
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## 1. Define the Module
|
|
27
|
+
|
|
28
|
+
Create the module definition with `defineModule`:
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
// src/module.ts
|
|
32
|
+
import { defineModule } from "@izumisy-tailor/omakase-modules/builder";
|
|
33
|
+
import type { ModuleConfig } from "./types";
|
|
34
|
+
import type { MyModuleTables } from "./tailordb/context";
|
|
35
|
+
import * as pkg from "../package.json";
|
|
36
|
+
|
|
37
|
+
export default defineModule<ModuleConfig, MyModuleTables>({
|
|
38
|
+
packageName: pkg.name,
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The `packageName` must match the `name` field in your `package.json`.
|
|
43
|
+
|
|
44
|
+
## 2. Define Configuration Types
|
|
45
|
+
|
|
46
|
+
Define what configuration options your module accepts:
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
// src/types.ts
|
|
50
|
+
import { TailorField } from "@tailor-platform/sdk";
|
|
51
|
+
|
|
52
|
+
type DataModelConfiguration = {
|
|
53
|
+
docNumberPrefix?: string;
|
|
54
|
+
customAttributes?: Record<string, TailorField<any>>;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export type ModuleConfig = {
|
|
58
|
+
dataModel?: {
|
|
59
|
+
product?: DataModelConfiguration;
|
|
60
|
+
category?: DataModelConfiguration;
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## 3. Define Table Types
|
|
66
|
+
|
|
67
|
+
Create type definitions for your module's tables:
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
// src/tailordb/context.ts
|
|
71
|
+
import type { TailorDBType } from "@tailor-platform/sdk";
|
|
72
|
+
import type { ModuleConfig } from "../types";
|
|
73
|
+
|
|
74
|
+
export type MyModuleTables = {
|
|
75
|
+
product: TailorDBType;
|
|
76
|
+
category: TailorDBType;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export type ModuleFactoryContext = {
|
|
80
|
+
config: ModuleConfig;
|
|
81
|
+
};
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## 4. Build Tables with Configuration
|
|
85
|
+
|
|
86
|
+
Use `withModuleConfiguration` to access configuration when building tables:
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
// src/tailordb/index.ts
|
|
90
|
+
import { withModuleConfiguration } from "@izumisy-tailor/omakase-modules/builder";
|
|
91
|
+
import type { MyModuleTables } from "./context";
|
|
92
|
+
import moduleDef from "../module";
|
|
93
|
+
import { buildProductTable } from "./product";
|
|
94
|
+
import { buildCategoryTable } from "./category";
|
|
95
|
+
|
|
96
|
+
const tables = await withModuleConfiguration(moduleDef, (context) => {
|
|
97
|
+
const category = buildCategoryTable(context);
|
|
98
|
+
const product = buildProductTable(context, { category });
|
|
99
|
+
|
|
100
|
+
return { product, category } satisfies MyModuleTables;
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
export const productTable = tables.product;
|
|
104
|
+
export const categoryTable = tables.category;
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
// src/tailordb/product.ts
|
|
109
|
+
import { db } from "@tailor-platform/sdk";
|
|
110
|
+
import type { ModuleFactoryContext } from "./context";
|
|
111
|
+
|
|
112
|
+
export const buildProductTable = (
|
|
113
|
+
{ config }: ModuleFactoryContext,
|
|
114
|
+
deps: { category: TailorDBType }
|
|
115
|
+
) => {
|
|
116
|
+
const customAttributes = config.dataModel?.product?.customAttributes || {};
|
|
117
|
+
const docNumberPrefix = config.dataModel?.product?.docNumberPrefix ?? "PROD";
|
|
118
|
+
|
|
119
|
+
return db.type("Product", {
|
|
120
|
+
name: db.string(),
|
|
121
|
+
sku: db.string().unique(),
|
|
122
|
+
categoryId: db.uuid({ optional: true }).relation({
|
|
123
|
+
type: "keyOnly",
|
|
124
|
+
toward: { type: deps.category },
|
|
125
|
+
}),
|
|
126
|
+
docNumber: db.docNumber({ prefix: docNumberPrefix }),
|
|
127
|
+
...customAttributes,
|
|
128
|
+
...db.fields.timestamps(),
|
|
129
|
+
});
|
|
130
|
+
};
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## 5. Declare Dependencies on Other Modules
|
|
134
|
+
|
|
135
|
+
If your module depends on other modules, use `ModuleDependency`:
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
// src/types.ts
|
|
139
|
+
import type { ModuleDependency } from "@izumisy-tailor/omakase-modules/builder";
|
|
140
|
+
import type commerceCoreModule from "omakase-module-commerce-core";
|
|
141
|
+
|
|
142
|
+
export type ModuleConfig = {
|
|
143
|
+
dataModel?: { /* ... */ };
|
|
144
|
+
dependencies: {
|
|
145
|
+
commerce: ModuleDependency<typeof commerceCoreModule>;
|
|
146
|
+
};
|
|
147
|
+
};
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
This ensures type-safe dependency injection when users configure your module.
|
|
151
|
+
|
|
152
|
+
## File Export Requirements
|
|
153
|
+
|
|
154
|
+
Due to Tailor SDK API requirements, **TailorDB** and **Resolvers/Executors** have different export patterns:
|
|
155
|
+
|
|
156
|
+
**TailorDB**: Should use a single `index.ts` barrel export. All table definitions should be exported from one file. This is **strongly recommended** because it allows you to use `satisfies` to verify that all required tables are defined for the module:
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
// src/tailordb/index.ts
|
|
160
|
+
const tables = await withModuleConfiguration(moduleDef, (context) => {
|
|
161
|
+
const category = buildCategoryTable(context);
|
|
162
|
+
const product = buildProductTable(context, { category });
|
|
163
|
+
|
|
164
|
+
// Type error if any required table is missing
|
|
165
|
+
return { product, category } satisfies MyModuleTables;
|
|
166
|
+
});
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
```
|
|
170
|
+
src/tailordb/
|
|
171
|
+
index.ts # Exports all tables
|
|
172
|
+
product.ts # Table builder (not directly exported)
|
|
173
|
+
category.ts # Table builder (not directly exported)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**Resolvers/Executors**: Must be exported as **separate files**. Each resolver or executor must be in its own file and cannot be barrel-exported from an `index.ts`:
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
src/resolvers/
|
|
180
|
+
getProduct.ts # Each resolver in its own file
|
|
181
|
+
listProducts.ts
|
|
182
|
+
src/executors/
|
|
183
|
+
onProductCreated.ts # Each executor in its own file
|
|
184
|
+
onOrderPlaced.ts
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
This is because the Tailor SDK processes resolver and executor files individually, expecting each file to contain a single definition with a default export.
|
|
188
|
+
|
|
189
|
+
## 6. Configure Package Exports
|
|
190
|
+
|
|
191
|
+
Set up your `package.json` to export the built files.
|
|
192
|
+
|
|
193
|
+
> **Important**: The export paths `./backend/tailordb`, `./backend/resolvers/*`, and `./backend/executors/*` are **required** and must not be changed. The module system expects these exact paths to locate module files.
|
|
194
|
+
|
|
195
|
+
```json
|
|
196
|
+
{
|
|
197
|
+
"name": "my-module",
|
|
198
|
+
"type": "module",
|
|
199
|
+
"exports": {
|
|
200
|
+
".": {
|
|
201
|
+
"default": "./dist/module.mjs",
|
|
202
|
+
"types": "./dist/module.d.mts"
|
|
203
|
+
},
|
|
204
|
+
"./backend/tailordb": {
|
|
205
|
+
"default": "./dist/tailordb/index.mjs",
|
|
206
|
+
"types": "./dist/tailordb/index.d.mts"
|
|
207
|
+
},
|
|
208
|
+
"./backend/resolvers/*": {
|
|
209
|
+
"default": "./dist/resolvers/*.mjs",
|
|
210
|
+
"types": "./dist/resolvers/*.d.mts"
|
|
211
|
+
},
|
|
212
|
+
"./backend/executors/*": {
|
|
213
|
+
"default": "./dist/executors/*.mjs",
|
|
214
|
+
"types": "./dist/executors/*.d.mts"
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Note that TailorDB uses an `index.mjs` barrel export, while resolvers and executors use wildcard patterns to export individual files.
|
|
221
|
+
|
|
222
|
+
## Best Practices
|
|
223
|
+
|
|
224
|
+
### Use Factory Functions for Tables
|
|
225
|
+
|
|
226
|
+
Always use factory functions (like `buildProductTable`) instead of directly exporting table definitions. This allows configuration to be injected.
|
|
227
|
+
|
|
228
|
+
### Type Your Tables
|
|
229
|
+
|
|
230
|
+
Use `satisfies` to ensure your returned tables match the expected type:
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
return { product, category } satisfies MyModuleTables;
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Provide Sensible Defaults
|
|
237
|
+
|
|
238
|
+
Always provide default values for optional configuration:
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
const prefix = config.dataModel?.product?.docNumberPrefix ?? "PROD";
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Export Table References
|
|
245
|
+
|
|
246
|
+
Export individual table references for use in other modules:
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
export const productTable = tables.product;
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
This allows other modules to reference your tables in relations and executors.
|
|
253
|
+
|
|
254
|
+
## Next Steps
|
|
255
|
+
|
|
256
|
+
- See [Using Modules](./using-modules.md) for how applications consume your module
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# Using Modules in Your Application
|
|
2
|
+
|
|
3
|
+
This guide explains how to use pre-built Omakase Modules in your Tailor Platform application.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- A Tailor Platform application
|
|
8
|
+
- Modules installed as dependencies (e.g., `omakase-module-commerce-core`)
|
|
9
|
+
|
|
10
|
+
## 1. Configure Modules
|
|
11
|
+
|
|
12
|
+
Use `loadModules` to configure the modules your application needs:
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
// modules.ts
|
|
16
|
+
import { loadModules } from "@izumisy-tailor/omakase-modules";
|
|
17
|
+
import { db } from "@tailor-platform/sdk";
|
|
18
|
+
import ecommerceCoreModule from "omakase-module-commerce-core";
|
|
19
|
+
|
|
20
|
+
export default loadModules((loader) => {
|
|
21
|
+
loader.add(
|
|
22
|
+
ecommerceCoreModule.configure({
|
|
23
|
+
config: {
|
|
24
|
+
dataModel: {
|
|
25
|
+
product: {
|
|
26
|
+
docNumberPrefix: "MY-PROD",
|
|
27
|
+
customAttributes: {
|
|
28
|
+
customStatus: db.enum(["new", "used", "refurbished"]),
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
})
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
return loader;
|
|
37
|
+
});
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Each module exposes a `configure()` method that accepts a type-safe configuration object. The available options are defined by the module author.
|
|
41
|
+
|
|
42
|
+
## 2. Set Up tsconfig.json
|
|
43
|
+
|
|
44
|
+
Configure path aliases in `tsconfig.json` to enable module configuration loading:
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"compilerOptions": {
|
|
49
|
+
"paths": {
|
|
50
|
+
"@izumisy-tailor/omakase-modules/config/loader": ["./modules.ts"]
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
This tells the module system where to find your configuration file.
|
|
57
|
+
|
|
58
|
+
## 3. Reference Modules in tailor.config.ts
|
|
59
|
+
|
|
60
|
+
Use `getModulesReference` to include module files in your Tailor configuration:
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
// tailor.config.ts
|
|
64
|
+
import { defineConfig } from "@tailor-platform/sdk";
|
|
65
|
+
import { getModulesReference } from "@izumisy-tailor/omakase-modules/config/sdk";
|
|
66
|
+
import modules from "./modules";
|
|
67
|
+
|
|
68
|
+
const moduleReference = getModulesReference(modules);
|
|
69
|
+
|
|
70
|
+
export default defineConfig({
|
|
71
|
+
name: "my-app",
|
|
72
|
+
db: {
|
|
73
|
+
main: {
|
|
74
|
+
files: ["./src/tailordb/*.ts", ...moduleReference.tailordb],
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
resolver: {
|
|
78
|
+
"main-pipeline": {
|
|
79
|
+
files: ["./src/resolvers/*.ts", ...moduleReference.resolver],
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
executor: {
|
|
83
|
+
files: ["./src/executors/**/*.ts", ...moduleReference.executor],
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## 4. Handle Module Dependencies
|
|
89
|
+
|
|
90
|
+
When modules depend on other modules, you need to wire them together:
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
// modules.ts
|
|
94
|
+
import { loadModules } from "@izumisy-tailor/omakase-modules";
|
|
95
|
+
import ecommerceCoreModule from "omakase-module-commerce-core";
|
|
96
|
+
import orderModule from "omakase-module-order";
|
|
97
|
+
import inventoryModule from "omakase-module-inventory";
|
|
98
|
+
|
|
99
|
+
export default loadModules((loader) => {
|
|
100
|
+
// Add the base module first
|
|
101
|
+
const $commerce = loader.add(
|
|
102
|
+
ecommerceCoreModule.configure({
|
|
103
|
+
config: { /* ... */ },
|
|
104
|
+
})
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
// Pass the commerce module as a dependency
|
|
108
|
+
const $order = loader.add(
|
|
109
|
+
orderModule.configure({
|
|
110
|
+
config: {
|
|
111
|
+
dependencies: {
|
|
112
|
+
commerce: $commerce,
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
})
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
// Inventory depends on both commerce and order
|
|
119
|
+
loader.add(
|
|
120
|
+
inventoryModule.configure({
|
|
121
|
+
config: {
|
|
122
|
+
dbNamespace: "main",
|
|
123
|
+
dependencies: {
|
|
124
|
+
commerce: $commerce,
|
|
125
|
+
order: $order,
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
})
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
return loader;
|
|
132
|
+
});
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
The `loader.add()` method returns the configured module, which can then be passed as a dependency to other modules.
|
|
136
|
+
|
|
137
|
+
## Common Configuration Options
|
|
138
|
+
|
|
139
|
+
Most modules support these common configuration patterns:
|
|
140
|
+
|
|
141
|
+
### Custom Attributes
|
|
142
|
+
|
|
143
|
+
Add custom fields to module tables:
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
config: {
|
|
147
|
+
dataModel: {
|
|
148
|
+
product: {
|
|
149
|
+
customAttributes: {
|
|
150
|
+
myCustomField: db.string(),
|
|
151
|
+
priority: db.int(),
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Document Number Prefixes
|
|
159
|
+
|
|
160
|
+
Customize document number prefixes:
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
config: {
|
|
164
|
+
dataModel: {
|
|
165
|
+
order: {
|
|
166
|
+
docNumberPrefix: "ORD-2024",
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Next Steps
|
|
173
|
+
|
|
174
|
+
- See [Creating Modules](./creating-modules.md) if you want to build your own reusable modules
|
package/package.json
CHANGED
|
@@ -1,35 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@izumisy-tailor/omakase-modules",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.0",
|
|
5
5
|
"description": "Modularization mechanism for Tailor Platform application powered by Tailor SDK",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"files": [
|
|
8
|
-
"
|
|
8
|
+
"src",
|
|
9
|
+
"docs"
|
|
9
10
|
],
|
|
10
11
|
"exports": {
|
|
11
12
|
".": {
|
|
12
|
-
"default": "./
|
|
13
|
-
"types": "./dist/config/index.d.mts"
|
|
13
|
+
"default": "./src/config/index.ts"
|
|
14
14
|
},
|
|
15
15
|
"./config/sdk": {
|
|
16
|
-
"default": "./
|
|
17
|
-
"types": "./dist/config/sdk/index.d.mts"
|
|
16
|
+
"default": "./src/config/sdk/index.ts"
|
|
18
17
|
},
|
|
19
18
|
"./builder": {
|
|
20
|
-
"default": "./
|
|
21
|
-
"types": "./dist/builder/index.d.mts"
|
|
19
|
+
"default": "./src/builder/index.ts"
|
|
22
20
|
},
|
|
23
21
|
"./config/loader": {
|
|
24
|
-
"default": "./
|
|
25
|
-
"types": "./dist/stub-loader/index.d.mts"
|
|
22
|
+
"default": "./src/stub-loader/index.ts"
|
|
26
23
|
}
|
|
27
24
|
},
|
|
28
|
-
"scripts": {
|
|
29
|
-
"build": "tsdown",
|
|
30
|
-
"dev": "tsdown --watch",
|
|
31
|
-
"type-check": "tsc"
|
|
32
|
-
},
|
|
33
25
|
"keywords": [
|
|
34
26
|
"tailor-platform",
|
|
35
27
|
"tailor-sdk",
|
|
@@ -38,13 +30,15 @@
|
|
|
38
30
|
],
|
|
39
31
|
"author": "Tailor Inc.",
|
|
40
32
|
"license": "MIT",
|
|
41
|
-
"packageManager": "pnpm@10.22.0",
|
|
42
33
|
"devDependencies": {
|
|
43
34
|
"@types/node": "^25.0.3",
|
|
44
35
|
"tsdown": "^0.18.0",
|
|
45
36
|
"typescript": "^5"
|
|
46
37
|
},
|
|
47
38
|
"peerDependencies": {
|
|
48
|
-
"@tailor-platform/sdk": "^0.
|
|
39
|
+
"@tailor-platform/sdk": "^0.20.0"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"type-check": "tsc"
|
|
49
43
|
}
|
|
50
|
-
}
|
|
44
|
+
}
|