platform-types 1.10.715 → 1.10.717

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/auth.d.ts CHANGED
@@ -326,6 +326,8 @@ declare global {
326
326
  accessToken: string;
327
327
  /** Transport-conditional exactly as `AuthUser.refreshToken` — absent on the default cookie path. */
328
328
  refreshToken?: string;
329
+ /** As `AuthUser.effectiveCapabilities` — the gate's own answer for the session just issued. */
330
+ effectiveCapabilities: UserCapability[];
329
331
  }
330
332
  /**
331
333
  * One row of `GET /auth?mode=sessions` — a storefront customer's own
package/dist/cjs/index.js CHANGED
@@ -74,6 +74,7 @@ __exportStar(require("./mercadopago.js"), exports);
74
74
  __exportStar(require("./notification.js"), exports);
75
75
  __exportStar(require("./order.js"), exports);
76
76
  __exportStar(require("./orderCollectionAttempt.js"), exports);
77
+ __exportStar(require("./orderCompletionAttempt.js"), exports);
77
78
  __exportStar(require("./orderCreateAttempt.js"), exports);
78
79
  __exportStar(require("./payment.js"), exports);
79
80
  __exportStar(require("./platform.js"), exports);
package/dist/cjs/order.js CHANGED
@@ -464,6 +464,22 @@ exports.orderFulfilmentProgress = orderFulfilmentProgress;
464
464
  * `IDEMPOTENCY_KEY_FAILED`, ask `POST /orders { mode: 'collection-attempt' }`
465
465
  * (`OrderCollectionAttemptVerdict`) — never resend it under a new key first.
466
466
  *
467
+ * Completion-attempt refusals, on `mode: 'delivery'` under an `oc1~`
468
+ * `Idempotency-Key` (`ORDER_COMPLETION_ATTEMPT_KEY_PATTERN`). A legacy key never
469
+ * gets either; the key refusals are the create's own `ORDER_ATTEMPT_KEY_EXPIRED`
470
+ * and `ORDER_ATTEMPT_KEY_SCOPE_MISMATCH` above. ⚠️ On a delivery their remedy is
471
+ * NOT "mint a new key and resend": the original may have applied, so run
472
+ * `mode: 'completion-attempt'` on the old key first and send a new attempt only
473
+ * on `not-written`. Nothing was written on any of them:
474
+ * - `ORDER_COMPLETION_ATTEMPT_COMMITTED` · 409 — this key's original attempt
475
+ * already applied; `data` is the `committed` `OrderCompletionAttemptVerdict`.
476
+ * Do not resend.
477
+ * - `ORDER_COMPLETION_ATTEMPT_ABANDONED` · 409 — a completion-attempt lookup
478
+ * fenced this key and answered `not-written`. Re-read the order and send a NEW
479
+ * attempt under a NEW key.
480
+ * - `IDEMPOTENCY_KEY_REUSE` · 422 — the key's recorded attempt carried a
481
+ * different `orderId`, `delivered` or `sendSms`.
482
+ *
467
483
  * Idempotency answers, all six from `withIdempotency`:
468
484
  * - `INVALID_IDEMPOTENCY_KEY` · 400 · `IDEMPOTENCY_KEY_REUSE` · 422 (the same
469
485
  * key with a different body)
@@ -613,4 +629,7 @@ exports.ORDER_ERROR_CODES = [
613
629
  'ORDER_ATTEMPT_ABANDONED',
614
630
  'ORDER_ATTEMPT_KEY_EXPIRED',
615
631
  'ORDER_ATTEMPT_KEY_SCOPE_MISMATCH',
632
+ // `mode: 'delivery'` under an `oc1~` key — see `ORDER_COMPLETION_ATTEMPT_KEY_PATTERN`.
633
+ 'ORDER_COMPLETION_ATTEMPT_COMMITTED',
634
+ 'ORDER_COMPLETION_ATTEMPT_ABANDONED',
616
635
  ];
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ /**
3
+ * Staff completion attempt evidence — the recovery protocol for
4
+ * `POST /orders { mode: 'delivery' }` (pickup completion and its same-day
5
+ * reversal) when the client lost the reply and has to decide whether the
6
+ * delivery it sent landed before it sends anything again.
7
+ *
8
+ * The protocol is opt-in and rides on the `Idempotency-Key` value. A key
9
+ * matching {@link ORDER_COMPLETION_ATTEMPT_KEY_PATTERN} makes the delivery
10
+ * write a durable `committed` record IN THE SAME TRANSACTION as the order, the
11
+ * customer balance and the receivable, bound to the store, the user, the key
12
+ * and the request (`orderId`, `delivered`, `sendSms`). That record — not the
13
+ * 24-hour `Idempotency-Key` replay cache — is what keeps the attempt from
14
+ * applying twice: a same-key resend after the cache is gone, after the order was
15
+ * un-delivered on another device, or after a crash between the commit and the
16
+ * reply is refused `409 ORDER_COMPLETION_ATTEMPT_COMMITTED` and writes nothing.
17
+ * Any other key is a legacy key and behaves exactly as before.
18
+ *
19
+ * `POST /orders { mode: 'completion-attempt', attemptKey }` answers for a
20
+ * protocol key ({@link OrderCompletionAttemptVerdict}).
21
+ *
22
+ * The key carries the same scope tag, mint window, clock skew and retention as
23
+ * the order-create attempt key: `ORDER_ATTEMPT_SCOPE_TAG_LENGTH`,
24
+ * `ORDER_ATTEMPT_CREATE_WINDOW_MS`, `ORDER_ATTEMPT_CLOCK_SKEW_MS` and
25
+ * `ORDER_ATTEMPT_RETENTION_MS`. A delivery under a key older than the window,
26
+ * or minted for another store or user session, is refused before anything is
27
+ * read or written (`400 ORDER_ATTEMPT_KEY_EXPIRED` /
28
+ * `400 ORDER_ATTEMPT_KEY_SCOPE_MISMATCH`). ⚠️ Neither says the original did not
29
+ * apply: before sending again under a new key, look the OLD key up — the
30
+ * lookup answers from the record for the whole retention, past the window —
31
+ * and resend only on `not-written`.
32
+ *
33
+ * What a client does with each outcome:
34
+ *
35
+ * - `200` — applied now. Done.
36
+ * - `409 ORDER_COMPLETION_ATTEMPT_COMMITTED` or a `committed` verdict — the
37
+ * ORIGINAL attempt applied; `data` names the order, the leg (`delivered`) and
38
+ * `committedAt`, which is also the `updatedAt` that write stamped. Done:
39
+ * refresh the order, never resend. The order may have moved since (an
40
+ * un-delivery on the web is a later, separate write).
41
+ * - `pending` — the original is still running. Ask again after `retryAfterMs`.
42
+ * - `not-written` or `409 ORDER_COMPLETION_ATTEMPT_ABANDONED` — the attempt
43
+ * never applied and never will. A resend is allowed only as a NEW attempt:
44
+ * re-read the order, then send under a NEW key.
45
+ * - `unknown` — the server cannot decide. Never treat it as `not-written` and
46
+ * never resend automatically; the operator verifies the order first.
47
+ *
48
+ * ⚠️ `409 ORDER_DELIVERY_STATE_UNCHANGED`, a missing list row or an absent
49
+ * replay are NOT evidence of either outcome; only the codes and verdicts above
50
+ * are.
51
+ *
52
+ * ⚠️ A backend that predates this protocol refuses a protocol key with
53
+ * `400 INVALID_IDEMPOTENCY_KEY` before writing anything (`~` is outside the
54
+ * legacy key grammar). Do not mint protocol keys against a backend that has not
55
+ * shipped it: every delivery would fail, safely but entirely.
56
+ *
57
+ * The staff READY mark is a field edit (`POST /orders` with `readyAt` and
58
+ * `expectedUpdatedAt`), not this protocol: its `expectedUpdatedAt` is a frozen
59
+ * version precondition, so a resend of the same frozen body can never apply
60
+ * over a later change of the order — it is refused `409 ORDER_VERSION_CONFLICT`.
61
+ * ⚠️ Only when the token is sent: it is optional, and a ready mark without it
62
+ * is pinned to the server's own read at resend time, so a token-less resend
63
+ * after a later un-ready marks the order ready again. Send `expectedUpdatedAt`
64
+ * with every ready mark a client may resend.
65
+ */
66
+ Object.defineProperty(exports, "__esModule", { value: true });
67
+ exports.ORDER_COMPLETION_ATTEMPT_UNKNOWN_REASONS = exports.ORDER_COMPLETION_ATTEMPT_VERDICTS = exports.ORDER_COMPLETION_ATTEMPT_KEY_PREFIX = exports.ORDER_COMPLETION_ATTEMPT_KEY_PATTERN = void 0;
68
+ /**
69
+ * The completion attempt key: `oc1~<mintMs>~<scopeTag>~<nonce>` — the
70
+ * order-create grammar under its own version tag, so neither route accepts the
71
+ * other's key. `mintMs` is 13 digits of epoch milliseconds, `scopeTag` is
72
+ * `ORDER_ATTEMPT_SCOPE_TAG_LENGTH` base64url characters computed exactly as for
73
+ * the create key, and `nonce` is 16 to 64 base64url characters.
74
+ *
75
+ * Mint ONE key per completion attempt and keep it, with the same body, for
76
+ * every resend of that attempt; a new key is a new attempt.
77
+ *
78
+ * A string, not a `RegExp`, and it carries no flags.
79
+ */
80
+ exports.ORDER_COMPLETION_ATTEMPT_KEY_PATTERN = '^oc1~\\d{13}~[A-Za-z0-9_-]{12}~[A-Za-z0-9_-]{16,64}$';
81
+ /** The version tag every completion attempt key starts with. */
82
+ exports.ORDER_COMPLETION_ATTEMPT_KEY_PREFIX = 'oc1~';
83
+ /** Every `verdict` a completion-attempt lookup answers — see `OrderCompletionAttemptVerdict`. */
84
+ exports.ORDER_COMPLETION_ATTEMPT_VERDICTS = ['committed', 'pending', 'not-written', 'unknown'];
85
+ /**
86
+ * Every `reason` on an `unknown` verdict — the same four, with the same
87
+ * meaning, as the order-create lookup's.
88
+ */
89
+ exports.ORDER_COMPLETION_ATTEMPT_UNKNOWN_REASONS = [
90
+ 'LEGACY_KEY',
91
+ 'SCOPE_MISMATCH',
92
+ 'EVIDENCE_EXPIRED',
93
+ 'KEY_CLOCK_AHEAD',
94
+ ];
package/dist/index.d.ts CHANGED
@@ -50,6 +50,7 @@ export * from './mercadopago.ts';
50
50
  export * from './notification.ts';
51
51
  export * from './order.ts';
52
52
  export * from './orderCollectionAttempt.ts';
53
+ export * from './orderCompletionAttempt.ts';
53
54
  export * from './orderCreateAttempt.ts';
54
55
  export * from './payment.ts';
55
56
  export * from './platform.ts';
package/dist/index.js CHANGED
@@ -58,6 +58,7 @@ export * from "./mercadopago.js";
58
58
  export * from "./notification.js";
59
59
  export * from "./order.js";
60
60
  export * from "./orderCollectionAttempt.js";
61
+ export * from "./orderCompletionAttempt.js";
61
62
  export * from "./orderCreateAttempt.js";
62
63
  export * from "./payment.js";
63
64
  export * from "./platform.js";
package/dist/order.d.ts CHANGED
@@ -2702,6 +2702,22 @@ export declare const orderFulfilmentProgress: (order: OrderFulfilmentProgressInp
2702
2702
  * `IDEMPOTENCY_KEY_FAILED`, ask `POST /orders { mode: 'collection-attempt' }`
2703
2703
  * (`OrderCollectionAttemptVerdict`) — never resend it under a new key first.
2704
2704
  *
2705
+ * Completion-attempt refusals, on `mode: 'delivery'` under an `oc1~`
2706
+ * `Idempotency-Key` (`ORDER_COMPLETION_ATTEMPT_KEY_PATTERN`). A legacy key never
2707
+ * gets either; the key refusals are the create's own `ORDER_ATTEMPT_KEY_EXPIRED`
2708
+ * and `ORDER_ATTEMPT_KEY_SCOPE_MISMATCH` above. ⚠️ On a delivery their remedy is
2709
+ * NOT "mint a new key and resend": the original may have applied, so run
2710
+ * `mode: 'completion-attempt'` on the old key first and send a new attempt only
2711
+ * on `not-written`. Nothing was written on any of them:
2712
+ * - `ORDER_COMPLETION_ATTEMPT_COMMITTED` · 409 — this key's original attempt
2713
+ * already applied; `data` is the `committed` `OrderCompletionAttemptVerdict`.
2714
+ * Do not resend.
2715
+ * - `ORDER_COMPLETION_ATTEMPT_ABANDONED` · 409 — a completion-attempt lookup
2716
+ * fenced this key and answered `not-written`. Re-read the order and send a NEW
2717
+ * attempt under a NEW key.
2718
+ * - `IDEMPOTENCY_KEY_REUSE` · 422 — the key's recorded attempt carried a
2719
+ * different `orderId`, `delivered` or `sendSms`.
2720
+ *
2705
2721
  * Idempotency answers, all six from `withIdempotency`:
2706
2722
  * - `INVALID_IDEMPOTENCY_KEY` · 400 · `IDEMPOTENCY_KEY_REUSE` · 422 (the same
2707
2723
  * key with a different body)
@@ -2723,6 +2739,6 @@ export declare const orderFulfilmentProgress: (order: OrderFulfilmentProgressInp
2723
2739
  * `SUBSCRIPTION_READONLY`) sit on the order CREATE leg only: an edit is not a new
2724
2740
  * order, so `bareOrderPostHandler` routes past `requireMetered` entirely.
2725
2741
  */
2726
- export declare const ORDER_ERROR_CODES: readonly ["ORDER_COST_DENOMINATION_UNKNOWN", "SERVICE_ORDER_DELIVERY_MANAGED", "ORDER_NOT_FOUND", "ORDER_LOCKED", "ORDER_NOT_READY", "ORDER_DELIVERED_ANOTHER_DAY", "ORDER_DELIVERY_STATE_UNCHANGED", "ORDER_ILLEGAL_TRANSITION", "ORDER_VERSION_CONFLICT", "CUSTOMER_NOT_FOUND", "CUSTOMER_MERGED", "ORDER_HAS_NO_CUSTOMER", "CUSTOMER_ANONYMIZED", "FX_CONVERSION_UNAVAILABLE", "ROLE_FORBIDDEN", "ORDER_CANCELLED", "PAYMENT_SETTLED_ON_ANOTHER_ORDER", "ORDER_HAS_SHIPMENTS", "ORDER_HAS_RETURNS", "SHIPMENT_QUANTITY_EXCEEDED", "SHIPMENT_LINE_MISMATCH", "SHIPMENT_NOT_FOUND", "SHIPMENT_LIMIT_REACHED", "SHIPMENT_TOO_LARGE", "ORDER_FULFILMENT_EXTERNAL", "SHIPMENT_FAILED", "ADMIN_REQUIRED", "IDEMPOTENCY_KEY_REQUIRED", "INVALID_IDEMPOTENCY_KEY", "IDEMPOTENCY_KEY_REUSE", "IDEMPOTENCY_IN_PROGRESS", "IDEMPOTENCY_LOCK_CONTENTION", "IDEMPOTENCY_KEY_FAILED", "IDEMPOTENT_RESPONSE_NOT_CACHED", "ORDER_CANCELLATION_NOT_PROTECTED", "ORDER_CANCELLATION_COMMITTED", "ORDER_CANCELLATION_LOCKED", "ORDER_CANCELLATION_TOO_LARGE", "ORDER_CANCELLATION_FAILED", "SALE_LEDGER_RETRACTION_REFUSED", "SALE_LEDGER_ROW_NOT_OWNED", "PRODUCT_NOT_FOUND", "PRODUCT_SALE_LIMIT_EXCEEDED", "PRODUCT_SALE_LIMIT_INVALID", "DEPOSIT_SOURCE_NOT_ALLOWED", "DEPOSIT_CURRENCY_MISMATCH", "DEPOSIT_EXCEEDS_TOTAL", "DEPOSIT_FREEZE_REQUIRES_INVOICE", "DEPOSIT_FREEZE_INVOICE_PENDING", "DEPOSIT_CHARACTERIZATION_CONFLICT", "DEPOSIT_FREEZE_INVOICE_NOT_QUALIFYING", "DEPOSIT_INVOICED_ORDER_MUST_FREEZE", "DEPOSIT_REFERENCE_IS_LINKED_PAYMENT", "DEPOSIT_DUPLICATE_REFERENCE", "TILL_TENDER_REQUIRES_DEPOSIT", "PAYMENT_REFERENCED_BY_DEPOSIT", "ORDER_HAS_UNREFUNDED_DEPOSIT", "DEPOSIT_NOT_FOUND", "DEPOSIT_ALREADY_REFUNDED", "UNIT_QUANTITY_NOT_WHOLE", "UNIT_REVISION_STALE", "UNIT_QUANTITY_UNREPRESENTABLE", "KIT_RECIPE_CHANGED", "CART_PROMOTION_UNAVAILABLE", "ORDER_HAS_KIT_LINES", "ORDER_TARGET_REQUIRED", "ORDER_SOURCE_CONFLICT", "ORDER_CUSTOMER_REQUIRED", "BASKET_EMPTY", "BASKET_CHANGED_DURING_CHECKOUT", "CART_VERSION_CREATE_ONLY", "CART_VERSION_REQUIRES_CART_ID", "CART_REVISION_STALE", "CART_CUSTOMER_MISMATCH", "COUNTER_SALE_CREATE_ONLY", "SOLD_AT_CREATE_ONLY", "SOLD_AT_IN_FUTURE", "ORDER_TENDER_CREATE_ONLY", "CREDIT_OVERRIDE_CREATE_ONLY", "ON_ACCOUNT_CREATE_ONLY", "ON_ACCOUNT_WITH_TENDERS", "TENDERS_REQUIRE_COUNTER_SALE", "LINES_REQUIRE_COUNTER_SALE", "PRODUCT_COST_UNKNOWN", "PRICE_CHANGE_DISABLED", "LINE_DISCOUNT_EXCEEDS_MAX", "CUSTOMER_DISABLED", "SHIFT_NOT_AVAILABLE", "SOLD_AT_OUTSIDE_SHIFT", "ORDER_CURRENCY_NOT_SUBSCRIBED", "PAYMENT_METHODS_NOT_CONFIGURED", "PAYMENT_METHOD_NOT_FOUND", "DELIVERY_METHOD_NOT_FOUND", "ORDER_BELOW_MINIMUM_PURCHASE", "ORDER_INVALID_CART", "EMAIL_VERIFICATION_REQUIRED", "ACCOUNT_DISABLED", "CURRENCY_MISMATCH", "SHIFT_NOT_OPEN", "ORDER_DISCOUNT_AFTER_INVOICE", "ORDER_DISCOUNT_ON_SERVICE_ORDER", "ORDER_DISCOUNT_ON_MARKETPLACE_ORDER", "ORDER_INVOICE_METHOD_OUTSTANDING_FACTURA", "ORDER_SERVICE_PERIOD_AFTER_INVOICE", "ORDER_ATTEMPT_COMMITTED", "ORDER_ATTEMPT_ABANDONED", "ORDER_ATTEMPT_KEY_EXPIRED", "ORDER_ATTEMPT_KEY_SCOPE_MISMATCH"];
2742
+ export declare const ORDER_ERROR_CODES: readonly ["ORDER_COST_DENOMINATION_UNKNOWN", "SERVICE_ORDER_DELIVERY_MANAGED", "ORDER_NOT_FOUND", "ORDER_LOCKED", "ORDER_NOT_READY", "ORDER_DELIVERED_ANOTHER_DAY", "ORDER_DELIVERY_STATE_UNCHANGED", "ORDER_ILLEGAL_TRANSITION", "ORDER_VERSION_CONFLICT", "CUSTOMER_NOT_FOUND", "CUSTOMER_MERGED", "ORDER_HAS_NO_CUSTOMER", "CUSTOMER_ANONYMIZED", "FX_CONVERSION_UNAVAILABLE", "ROLE_FORBIDDEN", "ORDER_CANCELLED", "PAYMENT_SETTLED_ON_ANOTHER_ORDER", "ORDER_HAS_SHIPMENTS", "ORDER_HAS_RETURNS", "SHIPMENT_QUANTITY_EXCEEDED", "SHIPMENT_LINE_MISMATCH", "SHIPMENT_NOT_FOUND", "SHIPMENT_LIMIT_REACHED", "SHIPMENT_TOO_LARGE", "ORDER_FULFILMENT_EXTERNAL", "SHIPMENT_FAILED", "ADMIN_REQUIRED", "IDEMPOTENCY_KEY_REQUIRED", "INVALID_IDEMPOTENCY_KEY", "IDEMPOTENCY_KEY_REUSE", "IDEMPOTENCY_IN_PROGRESS", "IDEMPOTENCY_LOCK_CONTENTION", "IDEMPOTENCY_KEY_FAILED", "IDEMPOTENT_RESPONSE_NOT_CACHED", "ORDER_CANCELLATION_NOT_PROTECTED", "ORDER_CANCELLATION_COMMITTED", "ORDER_CANCELLATION_LOCKED", "ORDER_CANCELLATION_TOO_LARGE", "ORDER_CANCELLATION_FAILED", "SALE_LEDGER_RETRACTION_REFUSED", "SALE_LEDGER_ROW_NOT_OWNED", "PRODUCT_NOT_FOUND", "PRODUCT_SALE_LIMIT_EXCEEDED", "PRODUCT_SALE_LIMIT_INVALID", "DEPOSIT_SOURCE_NOT_ALLOWED", "DEPOSIT_CURRENCY_MISMATCH", "DEPOSIT_EXCEEDS_TOTAL", "DEPOSIT_FREEZE_REQUIRES_INVOICE", "DEPOSIT_FREEZE_INVOICE_PENDING", "DEPOSIT_CHARACTERIZATION_CONFLICT", "DEPOSIT_FREEZE_INVOICE_NOT_QUALIFYING", "DEPOSIT_INVOICED_ORDER_MUST_FREEZE", "DEPOSIT_REFERENCE_IS_LINKED_PAYMENT", "DEPOSIT_DUPLICATE_REFERENCE", "TILL_TENDER_REQUIRES_DEPOSIT", "PAYMENT_REFERENCED_BY_DEPOSIT", "ORDER_HAS_UNREFUNDED_DEPOSIT", "DEPOSIT_NOT_FOUND", "DEPOSIT_ALREADY_REFUNDED", "UNIT_QUANTITY_NOT_WHOLE", "UNIT_REVISION_STALE", "UNIT_QUANTITY_UNREPRESENTABLE", "KIT_RECIPE_CHANGED", "CART_PROMOTION_UNAVAILABLE", "ORDER_HAS_KIT_LINES", "ORDER_TARGET_REQUIRED", "ORDER_SOURCE_CONFLICT", "ORDER_CUSTOMER_REQUIRED", "BASKET_EMPTY", "BASKET_CHANGED_DURING_CHECKOUT", "CART_VERSION_CREATE_ONLY", "CART_VERSION_REQUIRES_CART_ID", "CART_REVISION_STALE", "CART_CUSTOMER_MISMATCH", "COUNTER_SALE_CREATE_ONLY", "SOLD_AT_CREATE_ONLY", "SOLD_AT_IN_FUTURE", "ORDER_TENDER_CREATE_ONLY", "CREDIT_OVERRIDE_CREATE_ONLY", "ON_ACCOUNT_CREATE_ONLY", "ON_ACCOUNT_WITH_TENDERS", "TENDERS_REQUIRE_COUNTER_SALE", "LINES_REQUIRE_COUNTER_SALE", "PRODUCT_COST_UNKNOWN", "PRICE_CHANGE_DISABLED", "LINE_DISCOUNT_EXCEEDS_MAX", "CUSTOMER_DISABLED", "SHIFT_NOT_AVAILABLE", "SOLD_AT_OUTSIDE_SHIFT", "ORDER_CURRENCY_NOT_SUBSCRIBED", "PAYMENT_METHODS_NOT_CONFIGURED", "PAYMENT_METHOD_NOT_FOUND", "DELIVERY_METHOD_NOT_FOUND", "ORDER_BELOW_MINIMUM_PURCHASE", "ORDER_INVALID_CART", "EMAIL_VERIFICATION_REQUIRED", "ACCOUNT_DISABLED", "CURRENCY_MISMATCH", "SHIFT_NOT_OPEN", "ORDER_DISCOUNT_AFTER_INVOICE", "ORDER_DISCOUNT_ON_SERVICE_ORDER", "ORDER_DISCOUNT_ON_MARKETPLACE_ORDER", "ORDER_INVOICE_METHOD_OUTSTANDING_FACTURA", "ORDER_SERVICE_PERIOD_AFTER_INVOICE", "ORDER_ATTEMPT_COMMITTED", "ORDER_ATTEMPT_ABANDONED", "ORDER_ATTEMPT_KEY_EXPIRED", "ORDER_ATTEMPT_KEY_SCOPE_MISMATCH", "ORDER_COMPLETION_ATTEMPT_COMMITTED", "ORDER_COMPLETION_ATTEMPT_ABANDONED"];
2727
2743
  export type OrderErrorCode = (typeof ORDER_ERROR_CODES)[number];
2728
2744
  export {};
package/dist/order.js CHANGED
@@ -457,6 +457,22 @@ export const orderFulfilmentProgress = (order) => {
457
457
  * `IDEMPOTENCY_KEY_FAILED`, ask `POST /orders { mode: 'collection-attempt' }`
458
458
  * (`OrderCollectionAttemptVerdict`) — never resend it under a new key first.
459
459
  *
460
+ * Completion-attempt refusals, on `mode: 'delivery'` under an `oc1~`
461
+ * `Idempotency-Key` (`ORDER_COMPLETION_ATTEMPT_KEY_PATTERN`). A legacy key never
462
+ * gets either; the key refusals are the create's own `ORDER_ATTEMPT_KEY_EXPIRED`
463
+ * and `ORDER_ATTEMPT_KEY_SCOPE_MISMATCH` above. ⚠️ On a delivery their remedy is
464
+ * NOT "mint a new key and resend": the original may have applied, so run
465
+ * `mode: 'completion-attempt'` on the old key first and send a new attempt only
466
+ * on `not-written`. Nothing was written on any of them:
467
+ * - `ORDER_COMPLETION_ATTEMPT_COMMITTED` · 409 — this key's original attempt
468
+ * already applied; `data` is the `committed` `OrderCompletionAttemptVerdict`.
469
+ * Do not resend.
470
+ * - `ORDER_COMPLETION_ATTEMPT_ABANDONED` · 409 — a completion-attempt lookup
471
+ * fenced this key and answered `not-written`. Re-read the order and send a NEW
472
+ * attempt under a NEW key.
473
+ * - `IDEMPOTENCY_KEY_REUSE` · 422 — the key's recorded attempt carried a
474
+ * different `orderId`, `delivered` or `sendSms`.
475
+ *
460
476
  * Idempotency answers, all six from `withIdempotency`:
461
477
  * - `INVALID_IDEMPOTENCY_KEY` · 400 · `IDEMPOTENCY_KEY_REUSE` · 422 (the same
462
478
  * key with a different body)
@@ -606,4 +622,7 @@ export const ORDER_ERROR_CODES = [
606
622
  'ORDER_ATTEMPT_ABANDONED',
607
623
  'ORDER_ATTEMPT_KEY_EXPIRED',
608
624
  'ORDER_ATTEMPT_KEY_SCOPE_MISMATCH',
625
+ // `mode: 'delivery'` under an `oc1~` key — see `ORDER_COMPLETION_ATTEMPT_KEY_PATTERN`.
626
+ 'ORDER_COMPLETION_ATTEMPT_COMMITTED',
627
+ 'ORDER_COMPLETION_ATTEMPT_ABANDONED',
609
628
  ];
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Staff completion attempt evidence — the recovery protocol for
3
+ * `POST /orders { mode: 'delivery' }` (pickup completion and its same-day
4
+ * reversal) when the client lost the reply and has to decide whether the
5
+ * delivery it sent landed before it sends anything again.
6
+ *
7
+ * The protocol is opt-in and rides on the `Idempotency-Key` value. A key
8
+ * matching {@link ORDER_COMPLETION_ATTEMPT_KEY_PATTERN} makes the delivery
9
+ * write a durable `committed` record IN THE SAME TRANSACTION as the order, the
10
+ * customer balance and the receivable, bound to the store, the user, the key
11
+ * and the request (`orderId`, `delivered`, `sendSms`). That record — not the
12
+ * 24-hour `Idempotency-Key` replay cache — is what keeps the attempt from
13
+ * applying twice: a same-key resend after the cache is gone, after the order was
14
+ * un-delivered on another device, or after a crash between the commit and the
15
+ * reply is refused `409 ORDER_COMPLETION_ATTEMPT_COMMITTED` and writes nothing.
16
+ * Any other key is a legacy key and behaves exactly as before.
17
+ *
18
+ * `POST /orders { mode: 'completion-attempt', attemptKey }` answers for a
19
+ * protocol key ({@link OrderCompletionAttemptVerdict}).
20
+ *
21
+ * The key carries the same scope tag, mint window, clock skew and retention as
22
+ * the order-create attempt key: `ORDER_ATTEMPT_SCOPE_TAG_LENGTH`,
23
+ * `ORDER_ATTEMPT_CREATE_WINDOW_MS`, `ORDER_ATTEMPT_CLOCK_SKEW_MS` and
24
+ * `ORDER_ATTEMPT_RETENTION_MS`. A delivery under a key older than the window,
25
+ * or minted for another store or user session, is refused before anything is
26
+ * read or written (`400 ORDER_ATTEMPT_KEY_EXPIRED` /
27
+ * `400 ORDER_ATTEMPT_KEY_SCOPE_MISMATCH`). ⚠️ Neither says the original did not
28
+ * apply: before sending again under a new key, look the OLD key up — the
29
+ * lookup answers from the record for the whole retention, past the window —
30
+ * and resend only on `not-written`.
31
+ *
32
+ * What a client does with each outcome:
33
+ *
34
+ * - `200` — applied now. Done.
35
+ * - `409 ORDER_COMPLETION_ATTEMPT_COMMITTED` or a `committed` verdict — the
36
+ * ORIGINAL attempt applied; `data` names the order, the leg (`delivered`) and
37
+ * `committedAt`, which is also the `updatedAt` that write stamped. Done:
38
+ * refresh the order, never resend. The order may have moved since (an
39
+ * un-delivery on the web is a later, separate write).
40
+ * - `pending` — the original is still running. Ask again after `retryAfterMs`.
41
+ * - `not-written` or `409 ORDER_COMPLETION_ATTEMPT_ABANDONED` — the attempt
42
+ * never applied and never will. A resend is allowed only as a NEW attempt:
43
+ * re-read the order, then send under a NEW key.
44
+ * - `unknown` — the server cannot decide. Never treat it as `not-written` and
45
+ * never resend automatically; the operator verifies the order first.
46
+ *
47
+ * ⚠️ `409 ORDER_DELIVERY_STATE_UNCHANGED`, a missing list row or an absent
48
+ * replay are NOT evidence of either outcome; only the codes and verdicts above
49
+ * are.
50
+ *
51
+ * ⚠️ A backend that predates this protocol refuses a protocol key with
52
+ * `400 INVALID_IDEMPOTENCY_KEY` before writing anything (`~` is outside the
53
+ * legacy key grammar). Do not mint protocol keys against a backend that has not
54
+ * shipped it: every delivery would fail, safely but entirely.
55
+ *
56
+ * The staff READY mark is a field edit (`POST /orders` with `readyAt` and
57
+ * `expectedUpdatedAt`), not this protocol: its `expectedUpdatedAt` is a frozen
58
+ * version precondition, so a resend of the same frozen body can never apply
59
+ * over a later change of the order — it is refused `409 ORDER_VERSION_CONFLICT`.
60
+ * ⚠️ Only when the token is sent: it is optional, and a ready mark without it
61
+ * is pinned to the server's own read at resend time, so a token-less resend
62
+ * after a later un-ready marks the order ready again. Send `expectedUpdatedAt`
63
+ * with every ready mark a client may resend.
64
+ */
65
+ declare global {
66
+ /** One of {@link ORDER_COMPLETION_ATTEMPT_VERDICTS}. */
67
+ type OrderCompletionAttemptVerdictKind = (typeof ORDER_COMPLETION_ATTEMPT_VERDICTS)[number];
68
+ /** Why a lookup could not decide — one of {@link ORDER_COMPLETION_ATTEMPT_UNKNOWN_REASONS}. */
69
+ type OrderCompletionAttemptUnknownReason = (typeof ORDER_COMPLETION_ATTEMPT_UNKNOWN_REASONS)[number];
70
+ /**
71
+ * `POST /orders` body for the recovery lookup. `attemptKey` is the exact
72
+ * `Idempotency-Key` the delivery was sent with; the store and user are the
73
+ * caller's session. Safe to repeat, but `pending` is a moment in time: ask
74
+ * again after `retryAfterMs` and never cache it. `committed`, `not-written`
75
+ * and every `unknown` reason but `KEY_CLOCK_AHEAD` are final.
76
+ */
77
+ interface OrderCompletionAttemptLookupRequest {
78
+ mode: 'completion-attempt';
79
+ attemptKey: string;
80
+ }
81
+ /**
82
+ * `data` of a `200` from `POST /orders { mode: 'completion-attempt' }`, and
83
+ * `data` of a `409 ORDER_COMPLETION_ATTEMPT_COMMITTED` (always the
84
+ * `committed` member there).
85
+ *
86
+ * - `committed` — the delivery (`delivered: true`) or un-delivery
87
+ * (`delivered: false`) of `orderId` applied under this key at
88
+ * `committedAt` (epoch ms, the `updatedAt` it stamped on the order).
89
+ * - `pending` — still running; ask again after `retryAfterMs`.
90
+ * - `not-written` — nothing applied under this key and nothing ever will:
91
+ * the lookup fenced it at `fencedAt`. Resend under a NEW key.
92
+ * - `unknown` — undecidable; `reason` says why. Intervention, never a resend.
93
+ */
94
+ type OrderCompletionAttemptVerdict = {
95
+ verdict: Extract<OrderCompletionAttemptVerdictKind, 'committed'>;
96
+ orderId: string;
97
+ delivered: boolean;
98
+ committedAt: number;
99
+ } | {
100
+ verdict: Extract<OrderCompletionAttemptVerdictKind, 'pending'>;
101
+ retryAfterMs: number;
102
+ } | {
103
+ verdict: Extract<OrderCompletionAttemptVerdictKind, 'not-written'>;
104
+ fencedAt: number;
105
+ } | {
106
+ verdict: Extract<OrderCompletionAttemptVerdictKind, 'unknown'>;
107
+ reason: OrderCompletionAttemptUnknownReason;
108
+ };
109
+ }
110
+ /**
111
+ * The completion attempt key: `oc1~<mintMs>~<scopeTag>~<nonce>` — the
112
+ * order-create grammar under its own version tag, so neither route accepts the
113
+ * other's key. `mintMs` is 13 digits of epoch milliseconds, `scopeTag` is
114
+ * `ORDER_ATTEMPT_SCOPE_TAG_LENGTH` base64url characters computed exactly as for
115
+ * the create key, and `nonce` is 16 to 64 base64url characters.
116
+ *
117
+ * Mint ONE key per completion attempt and keep it, with the same body, for
118
+ * every resend of that attempt; a new key is a new attempt.
119
+ *
120
+ * A string, not a `RegExp`, and it carries no flags.
121
+ */
122
+ export declare const ORDER_COMPLETION_ATTEMPT_KEY_PATTERN = "^oc1~\\d{13}~[A-Za-z0-9_-]{12}~[A-Za-z0-9_-]{16,64}$";
123
+ /** The version tag every completion attempt key starts with. */
124
+ export declare const ORDER_COMPLETION_ATTEMPT_KEY_PREFIX = "oc1~";
125
+ /** Every `verdict` a completion-attempt lookup answers — see `OrderCompletionAttemptVerdict`. */
126
+ export declare const ORDER_COMPLETION_ATTEMPT_VERDICTS: readonly ["committed", "pending", "not-written", "unknown"];
127
+ /**
128
+ * Every `reason` on an `unknown` verdict — the same four, with the same
129
+ * meaning, as the order-create lookup's.
130
+ */
131
+ export declare const ORDER_COMPLETION_ATTEMPT_UNKNOWN_REASONS: readonly ["LEGACY_KEY", "SCOPE_MISMATCH", "EVIDENCE_EXPIRED", "KEY_CLOCK_AHEAD"];
132
+ export {};
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Staff completion attempt evidence — the recovery protocol for
3
+ * `POST /orders { mode: 'delivery' }` (pickup completion and its same-day
4
+ * reversal) when the client lost the reply and has to decide whether the
5
+ * delivery it sent landed before it sends anything again.
6
+ *
7
+ * The protocol is opt-in and rides on the `Idempotency-Key` value. A key
8
+ * matching {@link ORDER_COMPLETION_ATTEMPT_KEY_PATTERN} makes the delivery
9
+ * write a durable `committed` record IN THE SAME TRANSACTION as the order, the
10
+ * customer balance and the receivable, bound to the store, the user, the key
11
+ * and the request (`orderId`, `delivered`, `sendSms`). That record — not the
12
+ * 24-hour `Idempotency-Key` replay cache — is what keeps the attempt from
13
+ * applying twice: a same-key resend after the cache is gone, after the order was
14
+ * un-delivered on another device, or after a crash between the commit and the
15
+ * reply is refused `409 ORDER_COMPLETION_ATTEMPT_COMMITTED` and writes nothing.
16
+ * Any other key is a legacy key and behaves exactly as before.
17
+ *
18
+ * `POST /orders { mode: 'completion-attempt', attemptKey }` answers for a
19
+ * protocol key ({@link OrderCompletionAttemptVerdict}).
20
+ *
21
+ * The key carries the same scope tag, mint window, clock skew and retention as
22
+ * the order-create attempt key: `ORDER_ATTEMPT_SCOPE_TAG_LENGTH`,
23
+ * `ORDER_ATTEMPT_CREATE_WINDOW_MS`, `ORDER_ATTEMPT_CLOCK_SKEW_MS` and
24
+ * `ORDER_ATTEMPT_RETENTION_MS`. A delivery under a key older than the window,
25
+ * or minted for another store or user session, is refused before anything is
26
+ * read or written (`400 ORDER_ATTEMPT_KEY_EXPIRED` /
27
+ * `400 ORDER_ATTEMPT_KEY_SCOPE_MISMATCH`). ⚠️ Neither says the original did not
28
+ * apply: before sending again under a new key, look the OLD key up — the
29
+ * lookup answers from the record for the whole retention, past the window —
30
+ * and resend only on `not-written`.
31
+ *
32
+ * What a client does with each outcome:
33
+ *
34
+ * - `200` — applied now. Done.
35
+ * - `409 ORDER_COMPLETION_ATTEMPT_COMMITTED` or a `committed` verdict — the
36
+ * ORIGINAL attempt applied; `data` names the order, the leg (`delivered`) and
37
+ * `committedAt`, which is also the `updatedAt` that write stamped. Done:
38
+ * refresh the order, never resend. The order may have moved since (an
39
+ * un-delivery on the web is a later, separate write).
40
+ * - `pending` — the original is still running. Ask again after `retryAfterMs`.
41
+ * - `not-written` or `409 ORDER_COMPLETION_ATTEMPT_ABANDONED` — the attempt
42
+ * never applied and never will. A resend is allowed only as a NEW attempt:
43
+ * re-read the order, then send under a NEW key.
44
+ * - `unknown` — the server cannot decide. Never treat it as `not-written` and
45
+ * never resend automatically; the operator verifies the order first.
46
+ *
47
+ * ⚠️ `409 ORDER_DELIVERY_STATE_UNCHANGED`, a missing list row or an absent
48
+ * replay are NOT evidence of either outcome; only the codes and verdicts above
49
+ * are.
50
+ *
51
+ * ⚠️ A backend that predates this protocol refuses a protocol key with
52
+ * `400 INVALID_IDEMPOTENCY_KEY` before writing anything (`~` is outside the
53
+ * legacy key grammar). Do not mint protocol keys against a backend that has not
54
+ * shipped it: every delivery would fail, safely but entirely.
55
+ *
56
+ * The staff READY mark is a field edit (`POST /orders` with `readyAt` and
57
+ * `expectedUpdatedAt`), not this protocol: its `expectedUpdatedAt` is a frozen
58
+ * version precondition, so a resend of the same frozen body can never apply
59
+ * over a later change of the order — it is refused `409 ORDER_VERSION_CONFLICT`.
60
+ * ⚠️ Only when the token is sent: it is optional, and a ready mark without it
61
+ * is pinned to the server's own read at resend time, so a token-less resend
62
+ * after a later un-ready marks the order ready again. Send `expectedUpdatedAt`
63
+ * with every ready mark a client may resend.
64
+ */
65
+ /**
66
+ * The completion attempt key: `oc1~<mintMs>~<scopeTag>~<nonce>` — the
67
+ * order-create grammar under its own version tag, so neither route accepts the
68
+ * other's key. `mintMs` is 13 digits of epoch milliseconds, `scopeTag` is
69
+ * `ORDER_ATTEMPT_SCOPE_TAG_LENGTH` base64url characters computed exactly as for
70
+ * the create key, and `nonce` is 16 to 64 base64url characters.
71
+ *
72
+ * Mint ONE key per completion attempt and keep it, with the same body, for
73
+ * every resend of that attempt; a new key is a new attempt.
74
+ *
75
+ * A string, not a `RegExp`, and it carries no flags.
76
+ */
77
+ export const ORDER_COMPLETION_ATTEMPT_KEY_PATTERN = '^oc1~\\d{13}~[A-Za-z0-9_-]{12}~[A-Za-z0-9_-]{16,64}$';
78
+ /** The version tag every completion attempt key starts with. */
79
+ export const ORDER_COMPLETION_ATTEMPT_KEY_PREFIX = 'oc1~';
80
+ /** Every `verdict` a completion-attempt lookup answers — see `OrderCompletionAttemptVerdict`. */
81
+ export const ORDER_COMPLETION_ATTEMPT_VERDICTS = ['committed', 'pending', 'not-written', 'unknown'];
82
+ /**
83
+ * Every `reason` on an `unknown` verdict — the same four, with the same
84
+ * meaning, as the order-create lookup's.
85
+ */
86
+ export const ORDER_COMPLETION_ATTEMPT_UNKNOWN_REASONS = [
87
+ 'LEGACY_KEY',
88
+ 'SCOPE_MISMATCH',
89
+ 'EVIDENCE_EXPIRED',
90
+ 'KEY_CLOCK_AHEAD',
91
+ ];
package/dist/user.d.ts CHANGED
@@ -66,8 +66,16 @@ declare global {
66
66
  * consumer that models the row as `Omit<User, 'totp'>` throws away the only
67
67
  * signal that 2FA is enrolled. Unlike an auth body's `AuthUserTotp`, it
68
68
  * carries no `recoveryCodesRemaining`.
69
+ *
70
+ * `effectiveCapabilities` is present on the caller's OWN row only — the same
71
+ * answer as {@link AuthUser.effectiveCapabilities}, resolved from this
72
+ * request's token roles — and absent on every colleague's row, on the
73
+ * `POST /users` echo and on the `users` socket frame. A client keeps the list
74
+ * from its auth body or its own row here; a frame that omits it changes nothing.
69
75
  */
70
- type PublicUserRow = Omit<User, 'accessToken' | 'search' | 'login' | 'photoData' | 'removePhotoURL' | 'warnings'>;
76
+ type PublicUserRow = Omit<User, 'accessToken' | 'search' | 'login' | 'photoData' | 'removePhotoURL' | 'warnings'> & {
77
+ effectiveCapabilities?: UserCapability[];
78
+ };
71
79
  /**
72
80
  * Write shape for user create/update — carries the transient photo controls
73
81
  * and the plaintext password.
@@ -345,7 +353,31 @@ declare global {
345
353
  * Absent/false means no, for a plain USER only.
346
354
  */
347
355
  percepciones?: boolean;
356
+ /**
357
+ * May issue a nota de crédito — `POST /invoices` with `cbte_numero`, and
358
+ * the retry of a return's credit note (`POST /orders {mode:'return-credit-note'}`).
359
+ *
360
+ * ⚠️ **Role-implied** (ADMIN, SUPERVISOR, MANAGER hold it without the flag),
361
+ * for the reason `percepciones` gives: every elevated operator already issues
362
+ * NCs, and the granted class would withdraw that on deploy. A plain USER needs
363
+ * `creditNote: true` on their STORED row together with the server-stamped
364
+ * grant marker an admin's `POST /users` writes when it turns the flag on; a
365
+ * flag stored without that marker grants nothing, and nothing in a request
366
+ * grants it.
367
+ * Routine factura issuance is NOT gated by it.
368
+ *
369
+ * A client never derives this from `roles` — it reads
370
+ * `AuthUser.effectiveCapabilities`. Absent/false means no, for a plain USER only.
371
+ */
372
+ creditNote?: boolean;
348
373
  };
374
+ /**
375
+ * A key the server's capability gate decides — every `UserPermissions` key
376
+ * but the reserved, unread `accountant`. A refusal names one in
377
+ * `403 CAPABILITY_REQUIRED`'s `data.capability`, and
378
+ * `AuthUser.effectiveCapabilities` lists the ones a session holds.
379
+ */
380
+ type UserCapability = Exclude<keyof UserPermissions, 'accountant'>;
349
381
  /**
350
382
  * Wire error codes for the paths that create or update a USER row:
351
383
  * `POST /users` (create AND update — one handler serves both) and
@@ -389,6 +421,24 @@ declare global {
389
421
  * absent `totp` means the user never enrolled.
390
422
  */
391
423
  totp?: AuthUserTotp;
424
+ /**
425
+ * The capabilities this session holds, resolved by the SAME server function
426
+ * that gates each request (`@helpers/capabilityGate`), from the minted
427
+ * token's roles and the stored `permissions`. Clients read it —
428
+ * `effectiveCapabilities.includes('creditNote')` — and never compute it
429
+ * from `roles`, so the role-implied/granted split stays a server decision.
430
+ *
431
+ * ⚠️ A snapshot at token issue: a grant changed afterwards is enforced on
432
+ * the next request at once, but shows here only on the next refresh. A
433
+ * `403 CAPABILITY_REQUIRED` is the authoritative answer.
434
+ *
435
+ * PRINTER and ACCOUNTANT claims carry `[]`: neither is an operator acting on
436
+ * the store's authority, and the credit-note routes (`POST /invoices` with
437
+ * `cbte_numero`, the return credit-note retry) refuse them by role before
438
+ * consulting it. Not every capability-gated route does: `POST /cash` reads a
439
+ * PRINTER's stored flag.
440
+ */
441
+ effectiveCapabilities: UserCapability[];
392
442
  /**
393
443
  * ⚠️ Delivered in the body ONLY under the body refresh transport — the
394
444
  * native-mobile opt-in. The DEFAULT is cookie transport, where the token
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "platform-types",
3
- "version": "1.10.715",
3
+ "version": "1.10.717",
4
4
  "description": "Shared wire and entity contracts for the platform.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",