sinfactura-types 1.10.577 → 1.10.579

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
@@ -12,8 +12,7 @@ declare global {
12
12
  * ignored. Treated as ABSENT while the server's `PASSKEY_STEP_UP` flag is
13
13
  * off, so a client ahead of the flag gets `REQUIRES_2FA` again, never a
14
14
  * wrong-code refusal. Operator `mode: 'login'` only — the storefront
15
- * customer login and the Cloud Print `mode: 'printer'` login read the
16
- * same body type but do not accept it.
15
+ * customer login reads the same body type but does not accept it.
17
16
  */
18
17
  passkeyAssertion?: PasskeyAssertion;
19
18
  /** reCAPTCHA v3 token — required only after a `CAPTCHA_REQUIRED` login rejection; otherwise omit. */
@@ -522,8 +521,8 @@ declare global {
522
521
  * on. ⚠️ A property of the DOOR, not of the account, which is what makes it
523
522
  * different from `disabled`: the same row may be refused here and sign in
524
523
  * successfully at another mode, so it must never be rendered as "this
525
- * account is blocked". Emitted by `login`, `social`, `magic-link-verify`
526
- * and `printer`.
524
+ * account is blocked". Emitted by `login`, `social` and
525
+ * `magic-link-verify`.
527
526
  */
528
527
  type LoginOutcome = 'success' | 'wrong_password' | 'disabled' | 'blocked' | 'requires_2fa' | 'wrong_totp' | 'totp_replay' | 'wrong_passkey' | 'totp_locked' | 'enrollment_required'
529
528
  /**
@@ -970,10 +969,10 @@ declare global {
970
969
  /** The session making this very request. Exactly one row carries `true`. */
971
970
  current: boolean;
972
971
  /**
973
- * Device class, present only for non-browser sessions — today the Cloud
974
- * Print agent. Descriptive only; it exempts nothing from revocation, but it
975
- * lets a client warn that ending this session (or a blanket revoke-others)
976
- * also stops printing.
972
+ * @deprecated Never written. It once marked a Cloud Print agent's
973
+ * password-login session; print agents now pair instead of logging in as a
974
+ * user, so the api no longer sets it. Kept so a consumer that
975
+ * still reads `client === 'printer'` compiles — the test is simply never true.
977
976
  */
978
977
  client?: 'printer';
979
978
  }
@@ -1362,25 +1361,12 @@ export type RefreshErrorCode = (typeof REFRESH_ERROR_CODES)[number];
1362
1361
  * there is nothing to re-submit: no code the user can type will satisfy it, so
1363
1362
  * a client that renders the six-digit prompt for this member builds a dead
1364
1363
  * end. The only move that succeeds is enrolment. Emitted by
1365
- * `auth/helpers/mandatory2fa.ts` from its three opt-in callers — `_login.ts`,
1366
- * `_social.ts` and `_printer.ts`. ⚠️ On the `_printer.ts` leg the remedy is
1367
- * NOT enrolment: that mode reaches the code only for an account holding a
1368
- * PRINTER role *and* an operator role, and enrolling then earns
1369
- * `TWO_FA_NOT_SUPPORTED` instead. Show `body.message`, which names the real
1370
- * fix (a PRINTER-only service user), rather than a generic enrol prompt.
1364
+ * `auth/helpers/mandatory2fa.ts` from its opt-in callers — `_login.ts`,
1365
+ * `_social.ts`, `_refresh.ts` and `terminal/_activation.ts`.
1371
1366
  * ⚠️ NOT emitted from `_magicLinkVerify.ts`, `_verifyPassword.ts` or
1372
1367
  * `impersonation/_post.ts` — those three deliberately do not opt in, so the
1373
1368
  * mandate cannot brick account recovery or incident response.
1374
1369
  *
1375
- * ⚠️ `ENROLLMENT_REQUIRED` is spelled exactly that, with no `PRINTER` substring —
1376
- * however natural a surface-prefixed sibling looks, and this constrains any future
1377
- * rename too. A released Cloud Print agent substring-matches on `PRINTER` in the
1378
- * error code for an unrelated client-side condition; api `_printer.ts:196-198`
1379
- * documents this, and it is why the code emitted there is `TWO_FA_NOT_SUPPORTED`
1380
- * rather than anything clearer. A `PRINTER_ENROLLMENT_REQUIRED` would silently trip
1381
- * that branch in every deployed agent, and the failure would surface in the agent,
1382
- * never in our logs.
1383
- *
1384
1370
  * The GRACE-window half of the same obligation is not an error at all — the login
1385
1371
  * succeeds and the body carries `AuthUser.mustEnroll2fa` / `enrollBy`.
1386
1372
  */
@@ -1440,8 +1426,7 @@ export declare const LOGIN_ERROR_CODES: readonly ["WRONG_CREDENTIALS", "ACCOUNT_
1440
1426
  *
1441
1427
  * ⚠️ `ENROLLMENT_REQUIRED` is not here: it belongs to `STEP_UP_ERROR_CODES`,
1442
1428
  * because the second factor did not FAIL — there is no factor to fail yet. See
1443
- * that tuple for it, and for the `PRINTER`-substring hazard that constrains how
1444
- * it may be spelled.
1429
+ * that tuple for it.
1445
1430
  */
1446
1431
  export declare const LOGIN_2FA_ERROR_CODES: readonly ["INVALID_2FA_CODE", "2FA_LOCKED"];
1447
1432
  export type Login2faErrorCode = (typeof LOGIN_2FA_ERROR_CODES)[number];
package/dist/auth.js CHANGED
@@ -193,25 +193,12 @@ export const REFRESH_ERROR_CODES = [
193
193
  * there is nothing to re-submit: no code the user can type will satisfy it, so
194
194
  * a client that renders the six-digit prompt for this member builds a dead
195
195
  * end. The only move that succeeds is enrolment. Emitted by
196
- * `auth/helpers/mandatory2fa.ts` from its three opt-in callers — `_login.ts`,
197
- * `_social.ts` and `_printer.ts`. ⚠️ On the `_printer.ts` leg the remedy is
198
- * NOT enrolment: that mode reaches the code only for an account holding a
199
- * PRINTER role *and* an operator role, and enrolling then earns
200
- * `TWO_FA_NOT_SUPPORTED` instead. Show `body.message`, which names the real
201
- * fix (a PRINTER-only service user), rather than a generic enrol prompt.
196
+ * `auth/helpers/mandatory2fa.ts` from its opt-in callers — `_login.ts`,
197
+ * `_social.ts`, `_refresh.ts` and `terminal/_activation.ts`.
202
198
  * ⚠️ NOT emitted from `_magicLinkVerify.ts`, `_verifyPassword.ts` or
203
199
  * `impersonation/_post.ts` — those three deliberately do not opt in, so the
204
200
  * mandate cannot brick account recovery or incident response.
205
201
  *
206
- * ⚠️ `ENROLLMENT_REQUIRED` is spelled exactly that, with no `PRINTER` substring —
207
- * however natural a surface-prefixed sibling looks, and this constrains any future
208
- * rename too. A released Cloud Print agent substring-matches on `PRINTER` in the
209
- * error code for an unrelated client-side condition; api `_printer.ts:196-198`
210
- * documents this, and it is why the code emitted there is `TWO_FA_NOT_SUPPORTED`
211
- * rather than anything clearer. A `PRINTER_ENROLLMENT_REQUIRED` would silently trip
212
- * that branch in every deployed agent, and the failure would surface in the agent,
213
- * never in our logs.
214
- *
215
202
  * The GRACE-window half of the same obligation is not an error at all — the login
216
203
  * succeeds and the body carries `AuthUser.mustEnroll2fa` / `enrollBy`.
217
204
  */
@@ -279,8 +266,7 @@ export const LOGIN_ERROR_CODES = [
279
266
  *
280
267
  * ⚠️ `ENROLLMENT_REQUIRED` is not here: it belongs to `STEP_UP_ERROR_CODES`,
281
268
  * because the second factor did not FAIL — there is no factor to fail yet. See
282
- * that tuple for it, and for the `PRINTER`-substring hazard that constrains how
283
- * it may be spelled.
269
+ * that tuple for it.
284
270
  */
285
271
  export const LOGIN_2FA_ERROR_CODES = ['INVALID_2FA_CODE', '2FA_LOCKED'];
286
272
  /**
package/dist/cjs/auth.js CHANGED
@@ -196,25 +196,12 @@ exports.REFRESH_ERROR_CODES = [
196
196
  * there is nothing to re-submit: no code the user can type will satisfy it, so
197
197
  * a client that renders the six-digit prompt for this member builds a dead
198
198
  * end. The only move that succeeds is enrolment. Emitted by
199
- * `auth/helpers/mandatory2fa.ts` from its three opt-in callers — `_login.ts`,
200
- * `_social.ts` and `_printer.ts`. ⚠️ On the `_printer.ts` leg the remedy is
201
- * NOT enrolment: that mode reaches the code only for an account holding a
202
- * PRINTER role *and* an operator role, and enrolling then earns
203
- * `TWO_FA_NOT_SUPPORTED` instead. Show `body.message`, which names the real
204
- * fix (a PRINTER-only service user), rather than a generic enrol prompt.
199
+ * `auth/helpers/mandatory2fa.ts` from its opt-in callers — `_login.ts`,
200
+ * `_social.ts`, `_refresh.ts` and `terminal/_activation.ts`.
205
201
  * ⚠️ NOT emitted from `_magicLinkVerify.ts`, `_verifyPassword.ts` or
206
202
  * `impersonation/_post.ts` — those three deliberately do not opt in, so the
207
203
  * mandate cannot brick account recovery or incident response.
208
204
  *
209
- * ⚠️ `ENROLLMENT_REQUIRED` is spelled exactly that, with no `PRINTER` substring —
210
- * however natural a surface-prefixed sibling looks, and this constrains any future
211
- * rename too. A released Cloud Print agent substring-matches on `PRINTER` in the
212
- * error code for an unrelated client-side condition; api `_printer.ts:196-198`
213
- * documents this, and it is why the code emitted there is `TWO_FA_NOT_SUPPORTED`
214
- * rather than anything clearer. A `PRINTER_ENROLLMENT_REQUIRED` would silently trip
215
- * that branch in every deployed agent, and the failure would surface in the agent,
216
- * never in our logs.
217
- *
218
205
  * The GRACE-window half of the same obligation is not an error at all — the login
219
206
  * succeeds and the body carries `AuthUser.mustEnroll2fa` / `enrollBy`.
220
207
  */
@@ -282,8 +269,7 @@ exports.LOGIN_ERROR_CODES = [
282
269
  *
283
270
  * ⚠️ `ENROLLMENT_REQUIRED` is not here: it belongs to `STEP_UP_ERROR_CODES`,
284
271
  * because the second factor did not FAIL — there is no factor to fail yet. See
285
- * that tuple for it, and for the `PRINTER`-substring hazard that constrains how
286
- * it may be spelled.
272
+ * that tuple for it.
287
273
  */
288
274
  exports.LOGIN_2FA_ERROR_CODES = ['INVALID_2FA_CODE', '2FA_LOCKED'];
289
275
  /**
@@ -12,19 +12,25 @@
12
12
  * the sale consumes the recipe's components from stock, and the line freezes the
13
13
  * recipe it consumed (`OrderLine.kit`). Cancelling or returning that line gives
14
14
  * back exactly the frozen components, whatever the recipe says later. Every
15
- * other channel (storefront, service, marketplace, quote conversion) refuses an
16
- * active kit with the shared `409 PRODUCT_NOT_AVAILABLE`, with stock control on
17
- * or off, and it cannot be linked to or reactivated on MercadoLibre. Retiring
18
- * the recipe makes it an ordinary product again.
15
+ * other channel (service, marketplace, quote conversion) refuses an active kit
16
+ * with the shared `409 PRODUCT_NOT_AVAILABLE`, with stock control on or off,
17
+ * and it cannot be linked to or reactivated on MercadoLibre. Retiring the
18
+ * recipe makes it an ordinary product again.
19
+ *
20
+ * A shopper never sees the recipe: the storefront reads `Product.isKit` and the
21
+ * kit's `stock`, which the shopper projection derives from its components.
19
22
  */
20
23
  Object.defineProperty(exports, "__esModule", { value: true });
21
24
  exports.KIT_COMPONENT_MAX_QUANTITY = exports.KIT_RECIPE_MAX_COMPONENTS = exports.KIT_SALE_CHANNELS = void 0;
22
25
  /**
23
- * The channels that sell a kit by consuming its components. Only the counter
24
- * (the operator basket and `POST /orders`, which the app's POS and the mobile
25
- * app share) does today.
26
+ * The channels that sell a kit by consuming its components:
27
+ *
28
+ * - `counter` — the operator basket and `POST /orders`, which the app's POS and
29
+ * the mobile app share;
30
+ * - `storefront` — the shopper cart and the web checkout. Its WhatsApp catalogue
31
+ * and order drafts still leave kits out.
26
32
  */
27
- exports.KIT_SALE_CHANNELS = ['counter'];
33
+ exports.KIT_SALE_CHANNELS = ['counter', 'storefront'];
28
34
  /**
29
35
  * Most components one recipe names. A save commits the kit, its new revision and
30
36
  * every component's usage row in ONE transaction, so this is bounded by
package/dist/cjs/order.js CHANGED
@@ -251,9 +251,12 @@ exports.orderFulfilmentProgress = orderFulfilmentProgress;
251
251
  * `mode: 'deposit-refund'`, see `OrderDepositRefundRequest`. Nothing was
252
252
  * written. `DEPOSIT_ALREADY_REFUNDED`'s `data` carries
253
253
  * `OrderDepositRefundManualData` when the refund on the ledger was non-cash.
254
- * - `KIT_RECIPE_CHANGED` · 409 — a counter sale of a kit whose recipe was
255
- * saved again or retired after the sale read it; `productIds` names the kits.
256
- * Nothing was written; re-read the basket and retry.
254
+ * - `KIT_RECIPE_CHANGED` · 409 — a sale of a kit (counter or storefront
255
+ * checkout) whose recipe was saved again or retired after the sale read it;
256
+ * `data.productIds` names the kits. Nothing was written; re-read the cart and
257
+ * retry. On the storefront a recipe that moved before the sale read it is
258
+ * refused before the coupon is redeemed, while one saved as the order commits
259
+ * is refused after it, like every other refusal at commit.
257
260
  * - `ORDER_HAS_KIT_LINES` · 409 — `mode: 'edit'` on an order holding a kit line
258
261
  * (`CartLine.kit`). Cancel or return the order instead. Nothing was written.
259
262
  *
package/dist/cjs/roles.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.USER_ROLES = void 0;
3
+ exports.PRINT_AGENT_ROLE = exports.USER_ROLES = void 0;
4
4
  /**
5
5
  * The staff role vocabulary — the single published source of truth for what
6
6
  * may appear in `User.roles`.
@@ -15,9 +15,8 @@ exports.USER_ROLES = void 0;
15
15
  *
16
16
  * ⚠️ **`User.roles` is a delimited STRING, not one of these.** The wire shape
17
17
  * is a space- or comma-separated list, so a claim can legitimately carry more
18
- * than one token (`'USER PRINTER'` is real and load-bearing — a Cloud Print
19
- * agent whose backing user also operates the till). This tuple types the
20
- * TOKENS, never the whole attribute. Split before comparing, and compare
18
+ * than one token (`'USER ADMIN'`). This tuple types the TOKENS, never the
19
+ * whole attribute. Split before comparing, and compare
21
20
  * exact tokens: a bare `String#includes` is a SUBSTRING test, so a future
22
21
  * `MANAGER_READONLY` would silently satisfy a `MANAGER` gate.
23
22
  *
@@ -28,14 +27,18 @@ exports.USER_ROLES = void 0;
28
27
  * to this vocabulary would let a customer role be *assigned to staff*, which
29
28
  * no gate anywhere expects.
30
29
  *
30
+ * ⚠️ **`PRINTER` is not here either — it is not a person's role.** It is the
31
+ * machine marker a PAIRED Cloud Print agent's token carries
32
+ * (`PRINT_AGENT_ROLE` below), and no `User` row may hold it: every login door
33
+ * refuses a row that does, and every gate refuses a `PRINTER` token that does
34
+ * not also carry the agent's `printAgentId` claim.
35
+ *
31
36
  * ⚠️ **Order is not precedence.** This is the assignable set, listed
32
37
  * least-privileged first for readability. Audit attribution uses its own
33
- * priority order in which `PRINTER` outranks every operator role — the
34
- * machine identity has to win attribution because it already wins the
35
- * authorization decision. Do not derive one from the other.
38
+ * priority order. Do not derive one from the other.
36
39
  */
37
40
  /*
38
- * ⚠️ **`ACCOUNTANT` is a RESTRICTED role, like `PRINTER` — not a rung on a ladder.**
41
+ * ⚠️ **`ACCOUNTANT` is a RESTRICTED role — not a rung on a ladder.**
39
42
  * It is the tenant's accountant: a read-only fiscal login that reaches the
40
43
  * fiscal-book report modes (`invoices`, `supplier-invoices`, `libro-iva-digital`,
41
44
  * `iva-simple-apertura`) and the `GET /invoices` reads, and is refused
@@ -44,4 +47,10 @@ exports.USER_ROLES = void 0;
44
47
  * separately. Unrelated to the reserved `UserPermissions.accountant` capability
45
48
  * key, which nothing reads.
46
49
  */
47
- exports.USER_ROLES = ['USER', 'ADMIN', 'SUPERVISOR', 'MANAGER', 'PRINTER', 'ACCOUNTANT'];
50
+ exports.USER_ROLES = ['USER', 'ADMIN', 'SUPERVISOR', 'MANAGER', 'ACCOUNTANT'];
51
+ /**
52
+ * The role a paired Cloud Print agent's session token carries — a machine
53
+ * marker, never a person's role. Minted only by `POST
54
+ * /terminal/print-agents/session`, always beside a `printAgentId` claim.
55
+ */
56
+ exports.PRINT_AGENT_ROLE = 'PRINTER';
@@ -137,8 +137,9 @@ exports.TERMINAL_PAIRING_KINDS = ['terminal', 'print-agent'];
137
137
  * - `PRINT_AGENT_NOT_FOUND` · 404 — revoke: no such agent in the caller's
138
138
  * store (another store's agent answers the same).
139
139
  * - `AGENT_ID_BOUND` · 409 — pair: the install `agentId` is bound to a live
140
- * paired agent or a PRINTER user and the code was not minted to replace it
141
- * (`replacesAgentId`), or it is bound and the code was minted to replace a
140
+ * paired agent and the code was not minted to replace it (`replacesAgentId`);
141
+ * a binding left by a pre-pairing PRINTER user holds the id for nobody, so a
142
+ * normal code rebinds it. Or it is bound and the code was minted to replace a
142
143
  * DIFFERENT id, or its binding changed during the pairing. Nothing is
143
144
  * consumed.
144
145
  */
package/dist/cjs/user.js CHANGED
@@ -6,9 +6,10 @@ exports.USER_DIRECTORY_KEYS = void 0;
6
6
  * below-admin reader of the store's staff list receives instead of the row.
7
7
  *
8
8
  * ⚠️ An ALLOW-LIST, and the whole point. Name a colleague, tell a disabled or
9
- * print-agent account apart — that is what a picker, a "finalised by" label or
10
- * a technician filter needs. Contact data (`email`, `phone`), `permissions`,
11
- * `notifications`, `totp` and every key added to `User` later are OUT until
9
+ * legacy PRINTER row (a pre-pairing print-agent user, a non-person) apart —
10
+ * that is what a picker, a "finalised by" label or a technician filter needs.
11
+ * Contact data (`email`, `phone`), `permissions`, `notifications`, `totp` and
12
+ * every key added to `User` later are OUT until
12
13
  * someone adds them here, deliberately. Data minimisation (Ley 25.326): a
13
14
  * cashier has no need for a colleague's address.
14
15
  *
@@ -11,17 +11,23 @@
11
11
  * the sale consumes the recipe's components from stock, and the line freezes the
12
12
  * recipe it consumed (`OrderLine.kit`). Cancelling or returning that line gives
13
13
  * back exactly the frozen components, whatever the recipe says later. Every
14
- * other channel (storefront, service, marketplace, quote conversion) refuses an
15
- * active kit with the shared `409 PRODUCT_NOT_AVAILABLE`, with stock control on
16
- * or off, and it cannot be linked to or reactivated on MercadoLibre. Retiring
17
- * the recipe makes it an ordinary product again.
14
+ * other channel (service, marketplace, quote conversion) refuses an active kit
15
+ * with the shared `409 PRODUCT_NOT_AVAILABLE`, with stock control on or off,
16
+ * and it cannot be linked to or reactivated on MercadoLibre. Retiring the
17
+ * recipe makes it an ordinary product again.
18
+ *
19
+ * A shopper never sees the recipe: the storefront reads `Product.isKit` and the
20
+ * kit's `stock`, which the shopper projection derives from its components.
18
21
  */
19
22
  /**
20
- * The channels that sell a kit by consuming its components. Only the counter
21
- * (the operator basket and `POST /orders`, which the app's POS and the mobile
22
- * app share) does today.
23
+ * The channels that sell a kit by consuming its components:
24
+ *
25
+ * - `counter` — the operator basket and `POST /orders`, which the app's POS and
26
+ * the mobile app share;
27
+ * - `storefront` — the shopper cart and the web checkout. Its WhatsApp catalogue
28
+ * and order drafts still leave kits out.
23
29
  */
24
- export declare const KIT_SALE_CHANNELS: readonly ["counter"];
30
+ export declare const KIT_SALE_CHANNELS: readonly ["counter", "storefront"];
25
31
  /**
26
32
  * Most components one recipe names. A save commits the kit, its new revision and
27
33
  * every component's usage row in ONE transaction, so this is bounded by
package/dist/kitRecipe.js CHANGED
@@ -11,17 +11,23 @@
11
11
  * the sale consumes the recipe's components from stock, and the line freezes the
12
12
  * recipe it consumed (`OrderLine.kit`). Cancelling or returning that line gives
13
13
  * back exactly the frozen components, whatever the recipe says later. Every
14
- * other channel (storefront, service, marketplace, quote conversion) refuses an
15
- * active kit with the shared `409 PRODUCT_NOT_AVAILABLE`, with stock control on
16
- * or off, and it cannot be linked to or reactivated on MercadoLibre. Retiring
17
- * the recipe makes it an ordinary product again.
14
+ * other channel (service, marketplace, quote conversion) refuses an active kit
15
+ * with the shared `409 PRODUCT_NOT_AVAILABLE`, with stock control on or off,
16
+ * and it cannot be linked to or reactivated on MercadoLibre. Retiring the
17
+ * recipe makes it an ordinary product again.
18
+ *
19
+ * A shopper never sees the recipe: the storefront reads `Product.isKit` and the
20
+ * kit's `stock`, which the shopper projection derives from its components.
18
21
  */
19
22
  /**
20
- * The channels that sell a kit by consuming its components. Only the counter
21
- * (the operator basket and `POST /orders`, which the app's POS and the mobile
22
- * app share) does today.
23
+ * The channels that sell a kit by consuming its components:
24
+ *
25
+ * - `counter` — the operator basket and `POST /orders`, which the app's POS and
26
+ * the mobile app share;
27
+ * - `storefront` — the shopper cart and the web checkout. Its WhatsApp catalogue
28
+ * and order drafts still leave kits out.
23
29
  */
24
- export const KIT_SALE_CHANNELS = ['counter'];
30
+ export const KIT_SALE_CHANNELS = ['counter', 'storefront'];
25
31
  /**
26
32
  * Most components one recipe names. A save commits the kit, its new revision and
27
33
  * every component's usage row in ONE transaction, so this is bounded by
package/dist/order.d.ts CHANGED
@@ -2082,7 +2082,14 @@ declare global {
2082
2082
  province: string;
2083
2083
  };
2084
2084
  }
2085
- /** One product a sale asked for more of than is on hand. */
2085
+ /**
2086
+ * One product a sale asked for more of than is on hand.
2087
+ *
2088
+ * On the operator surfaces a kit's short component is named itself. The
2089
+ * storefront checkout never names a component: it reports the KIT, with
2090
+ * `requested` the kit units ordered and `available` the kit units the short
2091
+ * components still make.
2092
+ */
2086
2093
  interface StockShortfall {
2087
2094
  productId: string;
2088
2095
  requested: number;
@@ -2305,9 +2312,12 @@ export declare const orderFulfilmentProgress: (order: OrderFulfilmentProgressInp
2305
2312
  * `mode: 'deposit-refund'`, see `OrderDepositRefundRequest`. Nothing was
2306
2313
  * written. `DEPOSIT_ALREADY_REFUNDED`'s `data` carries
2307
2314
  * `OrderDepositRefundManualData` when the refund on the ledger was non-cash.
2308
- * - `KIT_RECIPE_CHANGED` · 409 — a counter sale of a kit whose recipe was
2309
- * saved again or retired after the sale read it; `productIds` names the kits.
2310
- * Nothing was written; re-read the basket and retry.
2315
+ * - `KIT_RECIPE_CHANGED` · 409 — a sale of a kit (counter or storefront
2316
+ * checkout) whose recipe was saved again or retired after the sale read it;
2317
+ * `data.productIds` names the kits. Nothing was written; re-read the cart and
2318
+ * retry. On the storefront a recipe that moved before the sale read it is
2319
+ * refused before the coupon is redeemed, while one saved as the order commits
2320
+ * is refused after it, like every other refusal at commit.
2311
2321
  * - `ORDER_HAS_KIT_LINES` · 409 — `mode: 'edit'` on an order holding a kit line
2312
2322
  * (`CartLine.kit`). Cancel or return the order instead. Nothing was written.
2313
2323
  *
package/dist/order.js CHANGED
@@ -244,9 +244,12 @@ export const orderFulfilmentProgress = (order) => {
244
244
  * `mode: 'deposit-refund'`, see `OrderDepositRefundRequest`. Nothing was
245
245
  * written. `DEPOSIT_ALREADY_REFUNDED`'s `data` carries
246
246
  * `OrderDepositRefundManualData` when the refund on the ledger was non-cash.
247
- * - `KIT_RECIPE_CHANGED` · 409 — a counter sale of a kit whose recipe was
248
- * saved again or retired after the sale read it; `productIds` names the kits.
249
- * Nothing was written; re-read the basket and retry.
247
+ * - `KIT_RECIPE_CHANGED` · 409 — a sale of a kit (counter or storefront
248
+ * checkout) whose recipe was saved again or retired after the sale read it;
249
+ * `data.productIds` names the kits. Nothing was written; re-read the cart and
250
+ * retry. On the storefront a recipe that moved before the sale read it is
251
+ * refused before the coupon is redeemed, while one saved as the order commits
252
+ * is refused after it, like every other refusal at commit.
250
253
  * - `ORDER_HAS_KIT_LINES` · 409 — `mode: 'edit'` on an order holding a kit line
251
254
  * (`CartLine.kit`). Cancel or return the order instead. Nothing was written.
252
255
  *
package/dist/product.d.ts CHANGED
@@ -680,6 +680,22 @@ declare global {
680
680
  * never in a shopper projection.
681
681
  */
682
682
  kitRecipe?: ProductKitRecipePointer;
683
+ /**
684
+ * SHOPPER PROJECTION ONLY, derived and never stored: `true` while
685
+ * `kitRecipe` is active. The recipe itself never reaches a shopper.
686
+ *
687
+ * On a kit, the shopper projection's `stock` is the kit units its
688
+ * components make right now — the smallest `floor(component stock ÷ units
689
+ * per kit)`, a missing component reading `0`, from the online depósito on
690
+ * a store booking stock per depósito — never the kit row's own figure,
691
+ * which no sale moves. A frame announcing a component's stock does not
692
+ * re-announce the kits using it, so the figure can lag until the next read;
693
+ * the checkout judges the components themselves.
694
+ *
695
+ * Present only on a payload that carries the kit pointer: a partial frame
696
+ * without it says nothing about whether the product is a kit.
697
+ */
698
+ isKit?: boolean;
683
699
  model?: string;
684
700
  seoTitle?: string;
685
701
  seoDescription?: string;
package/dist/roles.d.ts CHANGED
@@ -12,9 +12,8 @@
12
12
  *
13
13
  * ⚠️ **`User.roles` is a delimited STRING, not one of these.** The wire shape
14
14
  * is a space- or comma-separated list, so a claim can legitimately carry more
15
- * than one token (`'USER PRINTER'` is real and load-bearing — a Cloud Print
16
- * agent whose backing user also operates the till). This tuple types the
17
- * TOKENS, never the whole attribute. Split before comparing, and compare
15
+ * than one token (`'USER ADMIN'`). This tuple types the TOKENS, never the
16
+ * whole attribute. Split before comparing, and compare
18
17
  * exact tokens: a bare `String#includes` is a SUBSTRING test, so a future
19
18
  * `MANAGER_READONLY` would silently satisfy a `MANAGER` gate.
20
19
  *
@@ -25,20 +24,31 @@
25
24
  * to this vocabulary would let a customer role be *assigned to staff*, which
26
25
  * no gate anywhere expects.
27
26
  *
27
+ * ⚠️ **`PRINTER` is not here either — it is not a person's role.** It is the
28
+ * machine marker a PAIRED Cloud Print agent's token carries
29
+ * (`PRINT_AGENT_ROLE` below), and no `User` row may hold it: every login door
30
+ * refuses a row that does, and every gate refuses a `PRINTER` token that does
31
+ * not also carry the agent's `printAgentId` claim.
32
+ *
28
33
  * ⚠️ **Order is not precedence.** This is the assignable set, listed
29
34
  * least-privileged first for readability. Audit attribution uses its own
30
- * priority order in which `PRINTER` outranks every operator role — the
31
- * machine identity has to win attribution because it already wins the
32
- * authorization decision. Do not derive one from the other.
35
+ * priority order. Do not derive one from the other.
33
36
  */
34
- export declare const USER_ROLES: readonly ["USER", "ADMIN", "SUPERVISOR", "MANAGER", "PRINTER", "ACCOUNTANT"];
37
+ export declare const USER_ROLES: readonly ["USER", "ADMIN", "SUPERVISOR", "MANAGER", "ACCOUNTANT"];
35
38
  /** One assignable staff role token. See `USER_ROLES` for why this is not the shape of `User.roles`. */
36
39
  export type UserRole = (typeof USER_ROLES)[number];
37
40
  /**
38
- * Every role string an authenticated principal can present, staff or shopper.
41
+ * The role a paired Cloud Print agent's session token carries — a machine
42
+ * marker, never a person's role. Minted only by `POST
43
+ * /terminal/print-agents/session`, always beside a `printAgentId` claim.
44
+ */
45
+ export declare const PRINT_AGENT_ROLE: "PRINTER";
46
+ /**
47
+ * Every role string an authenticated principal can present: staff, the paired
48
+ * print agent or a shopper.
39
49
  *
40
- * `CUSTOMER` is included here and excluded from `USER_ROLES` on purpose: a
41
- * reader that classifies an incoming token needs it, and a writer that
42
- * assigns a staff role must not.
50
+ * `PRINTER` and `CUSTOMER` are included here and excluded from `USER_ROLES` on
51
+ * purpose: a reader that classifies an incoming token needs them, and a writer
52
+ * that assigns a staff role must not.
43
53
  */
44
- export type PrincipalRole = UserRole | 'CUSTOMER';
54
+ export type PrincipalRole = UserRole | typeof PRINT_AGENT_ROLE | 'CUSTOMER';
package/dist/roles.js CHANGED
@@ -12,9 +12,8 @@
12
12
  *
13
13
  * ⚠️ **`User.roles` is a delimited STRING, not one of these.** The wire shape
14
14
  * is a space- or comma-separated list, so a claim can legitimately carry more
15
- * than one token (`'USER PRINTER'` is real and load-bearing — a Cloud Print
16
- * agent whose backing user also operates the till). This tuple types the
17
- * TOKENS, never the whole attribute. Split before comparing, and compare
15
+ * than one token (`'USER ADMIN'`). This tuple types the TOKENS, never the
16
+ * whole attribute. Split before comparing, and compare
18
17
  * exact tokens: a bare `String#includes` is a SUBSTRING test, so a future
19
18
  * `MANAGER_READONLY` would silently satisfy a `MANAGER` gate.
20
19
  *
@@ -25,14 +24,18 @@
25
24
  * to this vocabulary would let a customer role be *assigned to staff*, which
26
25
  * no gate anywhere expects.
27
26
  *
27
+ * ⚠️ **`PRINTER` is not here either — it is not a person's role.** It is the
28
+ * machine marker a PAIRED Cloud Print agent's token carries
29
+ * (`PRINT_AGENT_ROLE` below), and no `User` row may hold it: every login door
30
+ * refuses a row that does, and every gate refuses a `PRINTER` token that does
31
+ * not also carry the agent's `printAgentId` claim.
32
+ *
28
33
  * ⚠️ **Order is not precedence.** This is the assignable set, listed
29
34
  * least-privileged first for readability. Audit attribution uses its own
30
- * priority order in which `PRINTER` outranks every operator role — the
31
- * machine identity has to win attribution because it already wins the
32
- * authorization decision. Do not derive one from the other.
35
+ * priority order. Do not derive one from the other.
33
36
  */
34
37
  /*
35
- * ⚠️ **`ACCOUNTANT` is a RESTRICTED role, like `PRINTER` — not a rung on a ladder.**
38
+ * ⚠️ **`ACCOUNTANT` is a RESTRICTED role — not a rung on a ladder.**
36
39
  * It is the tenant's accountant: a read-only fiscal login that reaches the
37
40
  * fiscal-book report modes (`invoices`, `supplier-invoices`, `libro-iva-digital`,
38
41
  * `iva-simple-apertura`) and the `GET /invoices` reads, and is refused
@@ -41,4 +44,10 @@
41
44
  * separately. Unrelated to the reserved `UserPermissions.accountant` capability
42
45
  * key, which nothing reads.
43
46
  */
44
- export const USER_ROLES = ['USER', 'ADMIN', 'SUPERVISOR', 'MANAGER', 'PRINTER', 'ACCOUNTANT'];
47
+ export const USER_ROLES = ['USER', 'ADMIN', 'SUPERVISOR', 'MANAGER', 'ACCOUNTANT'];
48
+ /**
49
+ * The role a paired Cloud Print agent's session token carries — a machine
50
+ * marker, never a person's role. Minted only by `POST
51
+ * /terminal/print-agents/session`, always beside a `printAgentId` claim.
52
+ */
53
+ export const PRINT_AGENT_ROLE = 'PRINTER';
@@ -46,8 +46,9 @@ declare global {
46
46
  * Must exist, else `404 AGENT_ID_NOT_FOUND` and no code is minted. The
47
47
  * agent that redeems the code with this exact `agentId` gets the id; a
48
48
  * paired agent that held it is revoked by that pairing. Without it, a
49
- * redeem naming an id bound to a LIVE agent or a PRINTER user is refused
50
- * `409 AGENT_ID_BOUND`; an id whose binding names a revoked paired agent
49
+ * redeem naming an id bound to a LIVE paired agent is refused
50
+ * `409 AGENT_ID_BOUND`; an id whose binding names a revoked paired agent,
51
+ * or a pre-pairing PRINTER user (no door admits one any more), is free and
51
52
  * needs none. Same charset as `PrintAgentPairRequest.agentId`.
52
53
  */
53
54
  replacesAgentId?: string;
@@ -121,12 +122,12 @@ declare global {
121
122
  /**
122
123
  * The agent's INSTALL id. Its printers and routing rules are keyed by it.
123
124
  * An id nothing holds yet — or one whose binding names a revoked paired
124
- * agent, when the code was minted for no install id — is bound to the new
125
- * print agent. A code minted with `replacesAgentId` pairs only that id. An
126
- * id bound to a LIVE agent or
127
- * a PRINTER user moves to it only when the code was minted with that id
128
- * as `replacesAgentId` (the admin's decision); otherwise the pair is
129
- * refused `409 AGENT_ID_BOUND` and the code stays unconsumed. Omit on a fresh
125
+ * agent or a pre-pairing PRINTER user, when the code was minted for no
126
+ * install id — is bound to the new print agent. A code minted with
127
+ * `replacesAgentId` pairs only that id. An id bound to a LIVE paired agent
128
+ * moves to it only when the code was minted with that id as
129
+ * `replacesAgentId` (the admin's decision); otherwise the pair is refused
130
+ * `409 AGENT_ID_BOUND` and the code stays unconsumed. Omit on a fresh
130
131
  * install to bind on first use.
131
132
  */
132
133
  agentId?: string;
@@ -513,8 +514,9 @@ export type TerminalPairingKind = (typeof TERMINAL_PAIRING_KINDS)[number];
513
514
  * - `PRINT_AGENT_NOT_FOUND` · 404 — revoke: no such agent in the caller's
514
515
  * store (another store's agent answers the same).
515
516
  * - `AGENT_ID_BOUND` · 409 — pair: the install `agentId` is bound to a live
516
- * paired agent or a PRINTER user and the code was not minted to replace it
517
- * (`replacesAgentId`), or it is bound and the code was minted to replace a
517
+ * paired agent and the code was not minted to replace it (`replacesAgentId`);
518
+ * a binding left by a pre-pairing PRINTER user holds the id for nobody, so a
519
+ * normal code rebinds it. Or it is bound and the code was minted to replace a
518
520
  * DIFFERENT id, or its binding changed during the pairing. Nothing is
519
521
  * consumed.
520
522
  */
package/dist/terminal.js CHANGED
@@ -134,8 +134,9 @@ export const TERMINAL_PAIRING_KINDS = ['terminal', 'print-agent'];
134
134
  * - `PRINT_AGENT_NOT_FOUND` · 404 — revoke: no such agent in the caller's
135
135
  * store (another store's agent answers the same).
136
136
  * - `AGENT_ID_BOUND` · 409 — pair: the install `agentId` is bound to a live
137
- * paired agent or a PRINTER user and the code was not minted to replace it
138
- * (`replacesAgentId`), or it is bound and the code was minted to replace a
137
+ * paired agent and the code was not minted to replace it (`replacesAgentId`);
138
+ * a binding left by a pre-pairing PRINTER user holds the id for nobody, so a
139
+ * normal code rebinds it. Or it is bound and the code was minted to replace a
139
140
  * DIFFERENT id, or its binding changed during the pairing. Nothing is
140
141
  * consumed.
141
142
  */
package/dist/user.d.ts CHANGED
@@ -4,9 +4,10 @@ import type { NotificationTypeEnum } from './notification.ts';
4
4
  * below-admin reader of the store's staff list receives instead of the row.
5
5
  *
6
6
  * ⚠️ An ALLOW-LIST, and the whole point. Name a colleague, tell a disabled or
7
- * print-agent account apart — that is what a picker, a "finalised by" label or
8
- * a technician filter needs. Contact data (`email`, `phone`), `permissions`,
9
- * `notifications`, `totp` and every key added to `User` later are OUT until
7
+ * legacy PRINTER row (a pre-pairing print-agent user, a non-person) apart —
8
+ * that is what a picker, a "finalised by" label or a technician filter needs.
9
+ * Contact data (`email`, `phone`), `permissions`, `notifications`, `totp` and
10
+ * every key added to `User` later are OUT until
10
11
  * someone adds them here, deliberately. Data minimisation (Ley 25.326): a
11
12
  * cashier has no need for a colleague's address.
12
13
  *
@@ -41,7 +42,8 @@ declare global {
41
42
  * fallback; never assume a name.
42
43
  *
43
44
  * Read `roles` with exact-token matching, as on `User.roles` — it is how a
44
- * picker leaves out a print-agent account.
45
+ * picker leaves out a legacy PRINTER row (a pre-pairing print-agent user
46
+ * that can no longer log in).
45
47
  */
46
48
  type UserDirectoryEntry = Pick<User, Exclude<UserDirectoryKey, 'fullName'>> & Partial<Pick<User, 'fullName'>>;
47
49
  /**
@@ -101,9 +103,12 @@ declare global {
101
103
  * Always read `roles`; never the alias.
102
104
  *
103
105
  * ⚠️ A space- or comma-DELIMITED list, not a single role — a claim can
104
- * legitimately carry more than one token (`'USER PRINTER'` is real: a
105
- * Cloud Print agent whose backing user also operates the till). Every
106
- * token must be a member of the published `USER_ROLES` tuple; split and
106
+ * legitimately carry more than one token (`'USER ADMIN'`). Every token a
107
+ * write can GRANT today is a member of the published `USER_ROLES` tuple. A
108
+ * LEGACY row may still carry `PRINTER` — a pre-pairing print-agent user that
109
+ * every login door now refuses, which an edit preserves but never grants.
110
+ * Treat it as a non-person, never as a print agent (those are paired, not
111
+ * users). Split and
107
112
  * compare EXACT tokens, never `String#includes`, which is a substring
108
113
  * test that would let a future `MANAGER_READONLY` satisfy a `MANAGER`
109
114
  * gate. It stays typed `string` rather than ```${UserRole}``` because
package/dist/user.js CHANGED
@@ -3,9 +3,10 @@
3
3
  * below-admin reader of the store's staff list receives instead of the row.
4
4
  *
5
5
  * ⚠️ An ALLOW-LIST, and the whole point. Name a colleague, tell a disabled or
6
- * print-agent account apart — that is what a picker, a "finalised by" label or
7
- * a technician filter needs. Contact data (`email`, `phone`), `permissions`,
8
- * `notifications`, `totp` and every key added to `User` later are OUT until
6
+ * legacy PRINTER row (a pre-pairing print-agent user, a non-person) apart —
7
+ * that is what a picker, a "finalised by" label or a technician filter needs.
8
+ * Contact data (`email`, `phone`), `permissions`, `notifications`, `totp` and
9
+ * every key added to `User` later are OUT until
9
10
  * someone adds them here, deliberately. Data minimisation (Ley 25.326): a
10
11
  * cashier has no need for a colleague's address.
11
12
  *
@@ -1303,7 +1303,7 @@ declare global {
1303
1303
  agent_id?: string;
1304
1304
  /**
1305
1305
  * The binding this pairing replaced, as stored — the raw `boundUserId`,
1306
- * NOT a bare user id: `{storeId}#USR…` for a PRINTER user, or
1306
+ * NOT a bare user id: `{storeId}#USR…` for a pre-pairing PRINTER user (legacy rows only), or
1307
1307
  * `{storeId}#PAG…` for a paired agent — one this pairing revoked (a
1308
1308
  * sibling `Print Agent Revoked` row), or, with `rebind_via: 'dead-agent'`,
1309
1309
  * one already revoked or aged out (no sibling row). Absent on a fresh
@@ -1313,8 +1313,9 @@ declare global {
1313
1313
  /**
1314
1314
  * Why a bound install id moved, beside `previous_bound_user_id`:
1315
1315
  * `'replaces-agent-id'` — the code's minting admin named it;
1316
- * `'dead-agent'` — the binding named a revoked (or aged-out) paired
1317
- * agent, which holds the id for nobody, so no admin named it.
1316
+ * `'dead-agent'` — the binding held the id for nobody, so no admin named
1317
+ * it: it named a revoked (or aged-out) paired agent, or no paired agent at
1318
+ * all (a pre-pairing PRINTER user's `{storeId}#USR…`).
1318
1319
  */
1319
1320
  rebind_via?: 'replaces-agent-id' | 'dead-agent';
1320
1321
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sinfactura-types",
3
- "version": "1.10.577",
3
+ "version": "1.10.579",
4
4
  "description": "Shared wire and entity contracts for the SINFACTURA platform.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",