@comity/order 0.9.0 → 0.9.2

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 (47) hide show
  1. package/README.md +1 -4
  2. package/dist/{types/contracts → contracts}/item.d.ts +8 -1
  3. package/dist/{types/contracts → contracts}/order.d.ts +14 -2
  4. package/dist/{types/entities → entities}/order.d.ts +40 -10
  5. package/dist/{esm/entities → entities}/order.js +120 -28
  6. package/dist/{types/errors → errors}/order.d.ts +1 -1
  7. package/dist/{esm/errors → errors}/order.js +2 -0
  8. package/dist/{types/index.d.ts → index.d.ts} +1 -1
  9. package/package.json +17 -36
  10. package/dist/cjs/contracts/address-snapshot.js +0 -3
  11. package/dist/cjs/contracts/contact.js +0 -3
  12. package/dist/cjs/contracts/customer-snapshot.js +0 -3
  13. package/dist/cjs/contracts/item.js +0 -3
  14. package/dist/cjs/contracts/order-repository.js +0 -3
  15. package/dist/cjs/contracts/order.js +0 -3
  16. package/dist/cjs/contracts/payment-snapshot.js +0 -3
  17. package/dist/cjs/domain/order-transitions.js +0 -52
  18. package/dist/cjs/entities/order.js +0 -459
  19. package/dist/cjs/errors/index.js +0 -6
  20. package/dist/cjs/errors/order.js +0 -39
  21. package/dist/cjs/index.js +0 -8
  22. package/dist/cjs/repositories/index.js +0 -6
  23. package/dist/cjs/repositories/memory.js +0 -69
  24. package/dist/cjs/value-objects/order-id.js +0 -70
  25. /package/dist/{types/contracts → contracts}/address-snapshot.d.ts +0 -0
  26. /package/dist/{esm/contracts → contracts}/address-snapshot.js +0 -0
  27. /package/dist/{types/contracts → contracts}/contact.d.ts +0 -0
  28. /package/dist/{esm/contracts → contracts}/contact.js +0 -0
  29. /package/dist/{types/contracts → contracts}/customer-snapshot.d.ts +0 -0
  30. /package/dist/{esm/contracts → contracts}/customer-snapshot.js +0 -0
  31. /package/dist/{esm/contracts → contracts}/item.js +0 -0
  32. /package/dist/{types/contracts → contracts}/order-repository.d.ts +0 -0
  33. /package/dist/{esm/contracts → contracts}/order-repository.js +0 -0
  34. /package/dist/{esm/contracts → contracts}/order.js +0 -0
  35. /package/dist/{types/contracts → contracts}/payment-snapshot.d.ts +0 -0
  36. /package/dist/{esm/contracts → contracts}/payment-snapshot.js +0 -0
  37. /package/dist/{types/domain → domain}/order-transitions.d.ts +0 -0
  38. /package/dist/{esm/domain → domain}/order-transitions.js +0 -0
  39. /package/dist/{types/errors → errors}/index.d.ts +0 -0
  40. /package/dist/{esm/errors → errors}/index.js +0 -0
  41. /package/dist/{esm/index.js → index.js} +0 -0
  42. /package/dist/{types/repositories → repositories}/index.d.ts +0 -0
  43. /package/dist/{esm/repositories → repositories}/index.js +0 -0
  44. /package/dist/{types/repositories → repositories}/memory.d.ts +0 -0
  45. /package/dist/{esm/repositories → repositories}/memory.js +0 -0
  46. /package/dist/{types/value-objects → value-objects}/order-id.d.ts +0 -0
  47. /package/dist/{esm/value-objects → value-objects}/order-id.js +0 -0
package/README.md CHANGED
@@ -30,7 +30,7 @@ This package does NOT:
30
30
 
31
31
  ## Public API
32
32
 
33
- - `Order` — order entity with lifecycle and item mutations (`addItem`, `removeItem`, `updateItemQuantity`, `submit`, `confirm`, `fulfill`, `cancel`)
33
+ - `Order` — order entity with lifecycle and item mutations (`setItems`, `addItem`, `removeItem`, `updateItemQuantity`, `submit`, `confirm`, `fulfill`, `cancel`)
34
34
  - `OrderId` — order identifier value object
35
35
  - `OrderRepository` — persistence-boundary contract (`getById`, `search`, `save`)
36
36
  - `OrderState`, `OrderCreate`, `OrderUpdate`, `OrderSnapshot`, `OrderData`, `OrderStatus` — domain contracts
@@ -68,6 +68,3 @@ No exhaustive reference; see docs for constraints.
68
68
  ## Status
69
69
 
70
70
  Stable
71
-
72
- _Review Completed: 2026-08-01_
73
- _Compliance Score: N/A% (Green)_
@@ -81,7 +81,14 @@ export interface OrderProductSnapshot {
81
81
  * identity, lifecycle, or repository.
82
82
  */
83
83
  export interface OrderItem {
84
- /** Unique item identifier within the order. */
84
+ /**
85
+ * Stable technical key for one occurrence within the order.
86
+ *
87
+ * The key identifies the occurrence, not the product: it carries no
88
+ * commercial equivalence meaning and must never be derived by comparing
89
+ * product data. Occurrence keys are unique within the order, preserved
90
+ * across retention/updates, and never reassigned after removal.
91
+ */
85
92
  readonly id: string;
86
93
  /** Associated product snapshot. */
87
94
  readonly product: OrderProductSnapshot;
@@ -63,7 +63,9 @@ export type OrderSnapshot = Readonly<OrderState & {
63
63
  /**
64
64
  * Data required to create a new order.
65
65
  *
66
- * The identifier and the initial status are managed by the entity: an order
66
+ * The identifier is supplied separately as the entity constructor's `id`
67
+ * argument: the caller (application service or hydration path) owns ID
68
+ * generation and the Order never generates its own aggregate ID. An order
67
69
  * without a supplied status starts as {@link OrderStatus."draft"}. Lifecycle
68
70
  * timestamps are optional so the same contract supports both creation and
69
71
  * hydration from persisted state (ADR-001).
@@ -81,7 +83,7 @@ export type OrderCreate = OrderData & {
81
83
  *
82
84
  * Status transitions are not part of the update contract: they must pass
83
85
  * through the entity's domain methods (`submit`, `confirm`, `fulfill`,
84
- * `cancel`). Items are mutated through `addItem`/`removeItem`/
86
+ * `cancel`). Items are mutated through `setItems`/`addItem`/`removeItem`/
85
87
  * `updateItemQuantity`, which protect the order's invariants.
86
88
  */
87
89
  export interface OrderUpdate {
@@ -99,6 +101,16 @@ export interface OrderUpdate {
99
101
  * from `ProductProjection`: the Order never depends on `@comity/catalog`.
100
102
  */
101
103
  export interface OrderItemInput {
104
+ /**
105
+ * Caller-assigned technical key for the new occurrence within the order.
106
+ *
107
+ * The ID identifies one occurrence, not a product: two occurrences may
108
+ * carry identical product/configuration data under distinct IDs. It must be
109
+ * unique within the order; the aggregate rejects duplicates. The ID is
110
+ * generated outside the Order (application service or caller); the Order
111
+ * never mints occurrence IDs itself.
112
+ */
113
+ readonly id: string;
102
114
  /** Product snapshot to add to the order. */
103
115
  readonly product: OrderProductSnapshot;
104
116
  /** Quantity to add. */
@@ -23,13 +23,15 @@ export declare class Order {
23
23
  #private;
24
24
  /**
25
25
  * @param fields - The fields used to create or hydrate the order.
26
- * @param id - The unique identifier of the order, if it has been assigned.
26
+ * @param id - The unique identifier of the order, supplied by the caller.
27
+ * The Order never generates its own aggregate ID; hydration preserves the
28
+ * persisted ID verbatim.
27
29
  */
28
- constructor(fields: OrderCreate, id?: OrderId);
30
+ constructor(fields: OrderCreate, id: OrderId);
29
31
  /**
30
- * @returns The unique identifier of the order, if it has been assigned.
32
+ * @returns The unique identifier of the order.
31
33
  */
32
- get id(): OrderId | undefined;
34
+ get id(): OrderId;
33
35
  /**
34
36
  * @returns The commercial channel through which the order was placed.
35
37
  */
@@ -74,7 +76,7 @@ export declare class Order {
74
76
  * Updates the applied pricing result and metadata.
75
77
  *
76
78
  * Status transitions are performed through the domain methods; items are
77
- * mutated through `addItem`/`removeItem`/`updateItemQuantity`.
79
+ * mutated through `setItems`/`addItem`/`removeItem`/`updateItemQuantity`.
78
80
  *
79
81
  * @param changes - The changes to apply to the order.
80
82
  */
@@ -109,12 +111,41 @@ export declare class Order {
109
111
  */
110
112
  changeShippingDestination(destination: Omit<OrderAddressSnapshot, "role">): Result<void, OrderError>;
111
113
  /**
112
- * Adds an item to the order.
114
+ * Replaces the entire item collection with the supplied one (ADR-030).
113
115
  *
114
- * @param input - The item data to add.
116
+ * The supplied collection is the complete desired collection: items omitted
117
+ * from it are removed, and no merging, appending, or quantity aggregation
118
+ * occurs. The supplied order is the resulting order and an empty collection
119
+ * is valid. Every entry is validated before any state changes — quantity
120
+ * (integer `>= 1`) first, then ID uniqueness against the IDs already seen —
121
+ * and the first violation rejects the whole operation, leaving the aggregate
122
+ * unchanged. Every successful replacement refreshes `updatedAt`, including
123
+ * identical replacements. Supplied item IDs are preserved verbatim and
124
+ * caller-owned data is defensively copied. Item mutation is not status-gated.
115
125
  *
116
- * @returns The created item, or an `invalid_quantity` error when the
117
- * quantity is not a positive integer.
126
+ * @param items - The complete desired item collection.
127
+ *
128
+ * @returns A result indicating the success or failure of the replacement.
129
+ */
130
+ setItems(items: ReadonlyArray<OrderItem>): Result<void, OrderError>;
131
+ /**
132
+ * Appends one item occurrence to the order.
133
+ *
134
+ * The occurrence ID is supplied by the caller in the input: the Order never
135
+ * mints occurrence IDs itself. The ID must be unique within the order —
136
+ * appending an already-used ID is rejected so the `setItems` duplicate-ID
137
+ * invariant cannot be bypassed through this path. Validation runs before
138
+ * any mutation — quantity (integer `>= 1`) first, then ID uniqueness — and
139
+ * failures leave the aggregate unchanged. A successful append refreshes
140
+ * `updatedAt`. Occurrence IDs carry no product meaning: identical
141
+ * product/configuration data may coexist under distinct IDs.
142
+ *
143
+ * @param input - The item data to add, including the caller-assigned
144
+ * occurrence ID.
145
+ *
146
+ * @returns The created item copy, an `invalid_quantity` error when the
147
+ * quantity is not a positive integer, or a `duplicate_item_id` error when
148
+ * the ID is already used within the order.
118
149
  */
119
150
  addItem(input: OrderItemInput): Result<OrderItem, OrderError>;
120
151
  /**
@@ -160,7 +191,6 @@ export declare class Order {
160
191
  cancel(): Result<void, OrderError>;
161
192
  /**
162
193
  * Creates a snapshot of the current state of the order.
163
- * Requires the order to have an assigned identifier.
164
194
  *
165
195
  * @returns A snapshot representing the current state of the order.
166
196
  */
@@ -39,6 +39,32 @@ function copyProductSnapshot(snapshot) {
39
39
  ...(snapshot.metadata !== undefined ? { metadata: { ...snapshot.metadata } } : {}),
40
40
  };
41
41
  }
42
+ /**
43
+ * Returns a defensive copy of an order item so later mutations of the input
44
+ * cannot leak into the order.
45
+ *
46
+ * @param item - The order item to copy.
47
+ *
48
+ * @returns A defensive copy of the item.
49
+ */
50
+ function copyOrderItem(item) {
51
+ return {
52
+ ...item,
53
+ product: copyProductSnapshot(item.product),
54
+ ...(item.meta !== undefined ? { meta: { ...item.meta } } : {}),
55
+ };
56
+ }
57
+ /**
58
+ * Returns whether a quantity is a valid order item quantity: an integer
59
+ * greater than or equal to 1.
60
+ *
61
+ * @param quantity - The quantity to validate.
62
+ *
63
+ * @returns `true` when the quantity is a positive integer, otherwise `false`.
64
+ */
65
+ function isValidItemQuantity(quantity) {
66
+ return Number.isInteger(quantity) && quantity >= 1;
67
+ }
42
68
  /**
43
69
  * Returns a defensive copy of a contact so later mutations of the input cannot
44
70
  * leak into the order.
@@ -123,17 +149,22 @@ export class Order {
123
149
  #updatedAt;
124
150
  /**
125
151
  * @param fields - The fields used to create or hydrate the order.
126
- * @param id - The unique identifier of the order, if it has been assigned.
152
+ * @param id - The unique identifier of the order, supplied by the caller.
153
+ * The Order never generates its own aggregate ID; hydration preserves the
154
+ * persisted ID verbatim.
127
155
  */
128
156
  constructor(fields, id) {
129
157
  this.#id = id;
130
158
  this.#status = fields.status ?? "draft";
131
- this.#items = [...fields.items];
159
+ this.#items = fields.items.map(copyOrderItem);
132
160
  this.#price = fields.price;
133
161
  this.#channelId = fields.channelId;
134
- this.#customer = fields.customer !== undefined ? copyCustomerSnapshot(fields.customer) : undefined;
162
+ this.#customer =
163
+ fields.customer !== undefined ? copyCustomerSnapshot(fields.customer) : undefined;
135
164
  this.#addresses =
136
- fields.addresses !== undefined ? fields.addresses.map((address) => copyAddressSnapshot(address)) : undefined;
165
+ fields.addresses !== undefined
166
+ ? fields.addresses.map((address) => copyAddressSnapshot(address))
167
+ : undefined;
137
168
  this.#payments =
138
169
  fields.payments !== undefined
139
170
  ? fields.payments.map((payment) => ({ ...payment }))
@@ -143,7 +174,7 @@ export class Order {
143
174
  this.#updatedAt = fields.updatedAt ?? this.#createdAt;
144
175
  }
145
176
  /**
146
- * @returns The unique identifier of the order, if it has been assigned.
177
+ * @returns The unique identifier of the order.
147
178
  */
148
179
  get id() {
149
180
  return this.#id;
@@ -164,7 +195,7 @@ export class Order {
164
195
  * @returns The order items.
165
196
  */
166
197
  get items() {
167
- return [...this.#items];
198
+ return this.#items.map(copyOrderItem);
168
199
  }
169
200
  /**
170
201
  * @returns The order price.
@@ -221,7 +252,7 @@ export class Order {
221
252
  * Updates the applied pricing result and metadata.
222
253
  *
223
254
  * Status transitions are performed through the domain methods; items are
224
- * mutated through `addItem`/`removeItem`/`updateItemQuantity`.
255
+ * mutated through `setItems`/`addItem`/`removeItem`/`updateItemQuantity`.
225
256
  *
226
257
  * @param changes - The changes to apply to the order.
227
258
  */
@@ -245,10 +276,7 @@ export class Order {
245
276
  * @param snapshot - The payment fact to record.
246
277
  */
247
278
  attachPayment(snapshot) {
248
- this.#payments = [
249
- ...(this.#payments ?? []),
250
- { ...snapshot },
251
- ];
279
+ this.#payments = [...(this.#payments ?? []), { ...snapshot }];
252
280
  this.#updatedAt = Instant.now();
253
281
  }
254
282
  /**
@@ -272,7 +300,7 @@ export class Order {
272
300
  if (this.#status !== "draft" && this.#status !== "pending") {
273
301
  return failure(new OrderError("shipping_destination_immutable", {
274
302
  details: {
275
- ...(this.#id !== undefined ? { orderId: this.#id.toString() } : {}),
303
+ orderId: this.#id.toString(),
276
304
  },
277
305
  }));
278
306
  }
@@ -282,43 +310,108 @@ export class Order {
282
310
  if (current === undefined || addresses === undefined) {
283
311
  return failure(new OrderError("ambiguous_shipping_destination", {
284
312
  details: {
285
- ...(this.#id !== undefined ? { orderId: this.#id.toString() } : {}),
313
+ orderId: this.#id.toString(),
286
314
  },
287
315
  }));
288
316
  }
289
317
  if (isSameShippingDestination(current, destination)) {
290
318
  return success(undefined);
291
319
  }
292
- this.#addresses = addresses.map((address) => address.role === "shipping" ? copyAddressSnapshot({ ...destination, role: "shipping" }) : address);
320
+ this.#addresses = addresses.map((address) => address.role === "shipping"
321
+ ? copyAddressSnapshot({ ...destination, role: "shipping" })
322
+ : address);
323
+ this.#updatedAt = Instant.now();
324
+ return success(undefined);
325
+ }
326
+ /**
327
+ * Replaces the entire item collection with the supplied one (ADR-030).
328
+ *
329
+ * The supplied collection is the complete desired collection: items omitted
330
+ * from it are removed, and no merging, appending, or quantity aggregation
331
+ * occurs. The supplied order is the resulting order and an empty collection
332
+ * is valid. Every entry is validated before any state changes — quantity
333
+ * (integer `>= 1`) first, then ID uniqueness against the IDs already seen —
334
+ * and the first violation rejects the whole operation, leaving the aggregate
335
+ * unchanged. Every successful replacement refreshes `updatedAt`, including
336
+ * identical replacements. Supplied item IDs are preserved verbatim and
337
+ * caller-owned data is defensively copied. Item mutation is not status-gated.
338
+ *
339
+ * @param items - The complete desired item collection.
340
+ *
341
+ * @returns A result indicating the success or failure of the replacement.
342
+ */
343
+ setItems(items) {
344
+ const seen = new Set();
345
+ for (const item of items) {
346
+ if (!isValidItemQuantity(item.quantity)) {
347
+ return failure(new OrderError("invalid_quantity", {
348
+ details: {
349
+ orderId: this.#id.toString(),
350
+ itemId: item.id,
351
+ field: "quantity",
352
+ },
353
+ }));
354
+ }
355
+ if (seen.has(item.id)) {
356
+ return failure(new OrderError("duplicate_item_id", {
357
+ details: {
358
+ orderId: this.#id.toString(),
359
+ itemId: item.id,
360
+ },
361
+ }));
362
+ }
363
+ seen.add(item.id);
364
+ }
365
+ this.#items = items.map(copyOrderItem);
293
366
  this.#updatedAt = Instant.now();
294
367
  return success(undefined);
295
368
  }
296
369
  /**
297
- * Adds an item to the order.
370
+ * Appends one item occurrence to the order.
298
371
  *
299
- * @param input - The item data to add.
372
+ * The occurrence ID is supplied by the caller in the input: the Order never
373
+ * mints occurrence IDs itself. The ID must be unique within the order —
374
+ * appending an already-used ID is rejected so the `setItems` duplicate-ID
375
+ * invariant cannot be bypassed through this path. Validation runs before
376
+ * any mutation — quantity (integer `>= 1`) first, then ID uniqueness — and
377
+ * failures leave the aggregate unchanged. A successful append refreshes
378
+ * `updatedAt`. Occurrence IDs carry no product meaning: identical
379
+ * product/configuration data may coexist under distinct IDs.
300
380
  *
301
- * @returns The created item, or an `invalid_quantity` error when the
302
- * quantity is not a positive integer.
381
+ * @param input - The item data to add, including the caller-assigned
382
+ * occurrence ID.
383
+ *
384
+ * @returns The created item copy, an `invalid_quantity` error when the
385
+ * quantity is not a positive integer, or a `duplicate_item_id` error when
386
+ * the ID is already used within the order.
303
387
  */
304
388
  addItem(input) {
305
- if (!Number.isInteger(input.quantity) || input.quantity < 1) {
389
+ if (!isValidItemQuantity(input.quantity)) {
306
390
  return failure(new OrderError("invalid_quantity", {
307
391
  details: {
308
- ...(this.#id !== undefined ? { orderId: this.#id.toString() } : {}),
392
+ orderId: this.#id.toString(),
393
+ itemId: input.id,
309
394
  field: "quantity",
310
395
  },
311
396
  }));
312
397
  }
398
+ if (this.#items.some((existing) => existing.id === input.id)) {
399
+ return failure(new OrderError("duplicate_item_id", {
400
+ details: {
401
+ orderId: this.#id.toString(),
402
+ itemId: input.id,
403
+ },
404
+ }));
405
+ }
313
406
  const item = {
314
- id: crypto.randomUUID(),
407
+ id: input.id,
315
408
  product: copyProductSnapshot(input.product),
316
409
  quantity: input.quantity,
317
410
  price: input.price,
318
411
  };
319
412
  this.#items.push(item);
320
413
  this.#updatedAt = Instant.now();
321
- return success(item);
414
+ return success(copyOrderItem(item));
322
415
  }
323
416
  /**
324
417
  * Removes an item from the order.
@@ -332,7 +425,7 @@ export class Order {
332
425
  if (index === -1) {
333
426
  return failure(new OrderError("invalid_item", {
334
427
  details: {
335
- ...(this.#id !== undefined ? { orderId: this.#id.toString() } : {}),
428
+ orderId: this.#id.toString(),
336
429
  itemId,
337
430
  },
338
431
  }));
@@ -350,10 +443,10 @@ export class Order {
350
443
  * @returns A result indicating the success or failure of the update.
351
444
  */
352
445
  updateItemQuantity(itemId, quantity) {
353
- if (!Number.isInteger(quantity) || quantity < 1) {
446
+ if (!isValidItemQuantity(quantity)) {
354
447
  return failure(new OrderError("invalid_quantity", {
355
448
  details: {
356
- ...(this.#id !== undefined ? { orderId: this.#id.toString() } : {}),
449
+ orderId: this.#id.toString(),
357
450
  itemId,
358
451
  field: "quantity",
359
452
  },
@@ -363,7 +456,7 @@ export class Order {
363
456
  if (index === -1) {
364
457
  return failure(new OrderError("invalid_item", {
365
458
  details: {
366
- ...(this.#id !== undefined ? { orderId: this.#id.toString() } : {}),
459
+ orderId: this.#id.toString(),
367
460
  itemId,
368
461
  },
369
462
  }));
@@ -409,7 +502,6 @@ export class Order {
409
502
  }
410
503
  /**
411
504
  * Creates a snapshot of the current state of the order.
412
- * Requires the order to have an assigned identifier.
413
505
  *
414
506
  * @returns A snapshot representing the current state of the order.
415
507
  */
@@ -417,7 +509,7 @@ export class Order {
417
509
  return {
418
510
  id: this.#id,
419
511
  status: this.#status,
420
- items: [...this.#items],
512
+ items: this.#items.map(copyOrderItem),
421
513
  price: this.#price,
422
514
  channelId: this.#channelId,
423
515
  ...(this.#customer !== undefined ? { customer: copyCustomerSnapshot(this.#customer) } : {}),
@@ -8,7 +8,7 @@ import { BaseError } from "@comity/primitives/errors";
8
8
  * cross-module failures (`repository_error`, `insufficient_stock`,
9
9
  * `product_not_available`, coupon validation) belong to other layers/modules.
10
10
  */
11
- export type OrderErrorReason = "invalid_quantity" | "invalid_item" | "invalid_status_transition" | "shipping_destination_immutable" | "ambiguous_shipping_destination";
11
+ export type OrderErrorReason = "invalid_quantity" | "invalid_item" | "invalid_status_transition" | "shipping_destination_immutable" | "ambiguous_shipping_destination" | "duplicate_item_id";
12
12
  /**
13
13
  * Order error metadata.
14
14
  */
@@ -5,6 +5,7 @@ const REASON_MESSAGES = {
5
5
  invalid_status_transition: "Invalid order status transition",
6
6
  shipping_destination_immutable: "Shipping destination cannot be changed in the current order status",
7
7
  ambiguous_shipping_destination: "Order must contain exactly one shipping destination",
8
+ duplicate_item_id: "Duplicate order item identifier",
8
9
  };
9
10
  const REASON_HTTP_STATUS = {
10
11
  invalid_quantity: 400,
@@ -12,6 +13,7 @@ const REASON_HTTP_STATUS = {
12
13
  invalid_status_transition: 409,
13
14
  shipping_destination_immutable: 409,
14
15
  ambiguous_shipping_destination: 409,
16
+ duplicate_item_id: 400,
15
17
  };
16
18
  /**
17
19
  * Order operation error with typed reasons.
@@ -2,7 +2,7 @@ export type { OrderAddressRole, OrderAddressSnapshot } from "./contracts/address
2
2
  export type { OrderContact } from "./contracts/contact.js";
3
3
  export type { OrderCustomerSnapshot } from "./contracts/customer-snapshot.js";
4
4
  export type { OrderItem, OrderProductAttribute, OrderProductOption, OrderProductSnapshot, OrderVariantSnapshot, } from "./contracts/item.js";
5
- export type { OrderRepository, OrderRepositoryContext, OrderSearchCriteria, OrderSearchResult } from "./contracts/order-repository.js";
5
+ export type { OrderRepository, OrderRepositoryContext, OrderSearchCriteria, OrderSearchResult, } from "./contracts/order-repository.js";
6
6
  export type { OrderCreate, OrderData, OrderItemInput, OrderSnapshot, OrderState, OrderStatus, OrderUpdate, } from "./contracts/order.js";
7
7
  export type { OrderPaymentSnapshot, OrderPaymentStatus } from "./contracts/payment-snapshot.js";
8
8
  export { Order } from "./entities/order.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@comity/order",
3
- "version": "0.9.0",
3
+ "version": "0.9.2",
4
4
  "description": "Order domain contracts for Comity commerce modules.",
5
5
  "type": "module",
6
6
  "private": false,
@@ -29,52 +29,33 @@
29
29
  "typescript"
30
30
  ],
31
31
  "files": [
32
- "./dist",
33
- "!./dist/**/*.map"
32
+ "dist",
33
+ "!dist/**/*.map"
34
34
  ],
35
- "main": "./dist/cjs/index.js",
36
- "module": "./dist/esm/index.js",
37
- "types": "./dist/types/index.d.ts",
35
+ "main": "./dist/index.js",
36
+ "types": "./dist/index.d.ts",
38
37
  "exports": {
39
38
  ".": {
40
- "import": {
41
- "types": "./dist/types/index.d.ts",
42
- "default": "./dist/esm/index.js"
43
- },
44
- "require": {
45
- "types": "./dist/types/index.d.ts",
46
- "default": "./dist/cjs/index.js"
47
- }
39
+ "types": "./dist/index.d.ts",
40
+ "default": "./dist/index.js"
48
41
  },
49
42
  "./errors": {
50
- "import": {
51
- "types": "./dist/types/errors/index.d.ts",
52
- "default": "./dist/esm/errors/index.js"
53
- },
54
- "require": {
55
- "types": "./dist/types/errors/index.d.ts",
56
- "default": "./dist/cjs/errors/index.js"
57
- }
43
+ "types": "./dist/errors/index.d.ts",
44
+ "default": "./dist/errors/index.js"
58
45
  },
59
46
  "./repositories": {
60
- "import": {
61
- "types": "./dist/types/repositories/index.d.ts",
62
- "default": "./dist/esm/repositories/index.js"
63
- },
64
- "require": {
65
- "types": "./dist/types/repositories/index.d.ts",
66
- "default": "./dist/cjs/repositories/index.js"
67
- }
47
+ "types": "./dist/repositories/index.d.ts",
48
+ "default": "./dist/repositories/index.js"
68
49
  },
69
50
  "./package.json": "./package.json"
70
51
  },
71
52
  "typesVersions": {
72
53
  "*": {
73
54
  "errors": [
74
- "./dist/types/errors/index.d.ts"
55
+ "./dist/errors/index.d.ts"
75
56
  ],
76
57
  "repositories": [
77
- "./dist/types/repositories/index.d.ts"
58
+ "./dist/repositories/index.d.ts"
78
59
  ]
79
60
  }
80
61
  },
@@ -84,12 +65,12 @@
84
65
  },
85
66
  "sideEffects": false,
86
67
  "dependencies": {
87
- "@comity/pricing": "0.9.0",
88
- "@comity/organization": "0.9.0",
89
- "@comity/primitives": "0.9.0"
68
+ "@comity/organization": "0.9.2",
69
+ "@comity/pricing": "0.9.2",
70
+ "@comity/primitives": "0.9.2"
90
71
  },
91
72
  "devDependencies": {
92
- "@types/node": "^24.13.4",
73
+ "@types/node": "^24.19.2",
93
74
  "typescript": "^5.9.3"
94
75
  },
95
76
  "scripts": {
@@ -1,3 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- //# sourceMappingURL=address-snapshot.js.map
@@ -1,3 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- //# sourceMappingURL=contact.js.map
@@ -1,3 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- //# sourceMappingURL=customer-snapshot.js.map
@@ -1,3 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- //# sourceMappingURL=item.js.map
@@ -1,3 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- //# sourceMappingURL=order-repository.js.map
@@ -1,3 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- //# sourceMappingURL=order.js.map
@@ -1,3 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- //# sourceMappingURL=payment-snapshot.js.map
@@ -1,52 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.canTransition = canTransition;
4
- exports.transitionOrderStatus = transitionOrderStatus;
5
- const result_1 = require("@comity/primitives/result");
6
- const order_js_1 = require("../errors/order.js");
7
- /**
8
- * Legal transitions for each order status.
9
- *
10
- * - `draft` → `pending` (submit)
11
- * - `pending` → `confirmed` (confirm)
12
- * - `confirmed` → `fulfilled` (fulfill)
13
- * - `draft | pending | confirmed` → `cancelled` (cancel)
14
- *
15
- * `fulfilled` and `cancelled` are terminal: no transition is legal from them.
16
- */
17
- const TRANSITIONS = {
18
- draft: ["pending", "cancelled"],
19
- pending: ["confirmed", "cancelled"],
20
- confirmed: ["fulfilled", "cancelled"],
21
- fulfilled: [],
22
- cancelled: [],
23
- };
24
- /**
25
- * Checks whether a status transition is legal.
26
- *
27
- * @param from - Source status.
28
- * @param to - Target status.
29
- *
30
- * @returns True if the transition is allowed, false otherwise.
31
- */
32
- function canTransition(from, to) {
33
- return TRANSITIONS[from].includes(to);
34
- }
35
- /**
36
- * Applies a status transition.
37
- *
38
- * @param from - Source status.
39
- * @param to - Target status.
40
- *
41
- * @returns The target status, or an `invalid_status_transition` error when the
42
- * transition is not allowed.
43
- */
44
- function transitionOrderStatus(from, to) {
45
- if (!canTransition(from, to)) {
46
- return (0, result_1.failure)(new order_js_1.OrderError("invalid_status_transition", {
47
- details: { from, to },
48
- }));
49
- }
50
- return (0, result_1.success)(to);
51
- }
52
- //# sourceMappingURL=order-transitions.js.map