@classytic/primitives 0.26.1 → 0.26.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.
- package/CHANGELOG.md +23 -0
- package/dist/item-identity.d.mts +45 -1
- package/dist/item-identity.mjs +44 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,29 @@
|
|
|
3
3
|
Format based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
4
4
|
adhering to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
5
5
|
|
|
6
|
+
## 0.26.2
|
|
7
|
+
|
|
8
|
+
### Added — `stockKeyFor` (`/item-identity`): one stock-key precedence for every package
|
|
9
|
+
|
|
10
|
+
The product-level stock key, with a single precedence:
|
|
11
|
+
|
|
12
|
+
1. the variant's stamped `skuRef` — assigned once at birth, immutable
|
|
13
|
+
2. a **sole** variant with no stamp → the product's `_id`, never the label
|
|
14
|
+
(legacy writers derived `variantSku || productId`, and for one variant that
|
|
15
|
+
key IS the `_id`)
|
|
16
|
+
3. a **sibling** with no stamp → its label, which genuinely is its legacy key
|
|
17
|
+
4. nothing addressable → the product's `_id`
|
|
18
|
+
|
|
19
|
+
The caller declares only what it HAS — `{ kind: 'sole' | 'label' | 'variant' }`
|
|
20
|
+
— so the sole-vs-sibling decision is internal and a caller cannot make it wrong.
|
|
21
|
+
That choice previously lived at every call site across three packages, in two
|
|
22
|
+
functions with different fallbacks; they disagreed on a sole, unstamped,
|
|
23
|
+
labelled variant, and the read then looked up a key no quant is stored under.
|
|
24
|
+
|
|
25
|
+
It lives here, below every domain, because `@spinekit/order` and
|
|
26
|
+
`@spinekit/catalog` must not import a peer domain — the rule sitting one layer
|
|
27
|
+
too high is exactly why each package re-derived it.
|
|
28
|
+
|
|
6
29
|
## 0.26.1
|
|
7
30
|
|
|
8
31
|
- `./testing/stock-key-seam` — new subpath; `scanStockKeySeam`, `assertStockKeySeam`, `takesStockKeyFromLabel`, `STOCK_KEY_SEAM_FIXTURES` for conformance-scanning packages that resolve stock keys
|
package/dist/item-identity.d.mts
CHANGED
|
@@ -31,6 +31,50 @@ interface SkuFields {
|
|
|
31
31
|
declare function stockKeyOf(item: SkuFields, fallback?: string | {
|
|
32
32
|
toString(): string;
|
|
33
33
|
}): StockKey | undefined;
|
|
34
|
+
/** The minimal product shape {@link stockKeyFor} reads. */
|
|
35
|
+
interface StockKeySource {
|
|
36
|
+
_id: string | {
|
|
37
|
+
toString(): string;
|
|
38
|
+
};
|
|
39
|
+
variants?: ReadonlyArray<SkuFields> | null;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* WHAT THE CALLER HAS — never what the key should be.
|
|
43
|
+
*
|
|
44
|
+
* - `sole` nothing selected (a simple product, or a whole-product read)
|
|
45
|
+
* - `label` an operator-typed / scanned SKU label
|
|
46
|
+
* - `variant` one variant record in hand, from iterating `variants`
|
|
47
|
+
*/
|
|
48
|
+
type StockKeySelector = {
|
|
49
|
+
readonly kind: "sole";
|
|
50
|
+
} | {
|
|
51
|
+
readonly kind: "label";
|
|
52
|
+
readonly sku: string;
|
|
53
|
+
} | {
|
|
54
|
+
readonly kind: "variant";
|
|
55
|
+
readonly variant: SkuFields;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* THE product-level stock key. One precedence, for every package.
|
|
59
|
+
*
|
|
60
|
+
* 1. the variant's STAMPED `skuRef` — assigned once at birth, immutable
|
|
61
|
+
* 2. a SOLE variant with no stamp -> the product's `_id`. Never the label:
|
|
62
|
+
* legacy writers derived `variantSku || productId`, and for one variant
|
|
63
|
+
* that key IS the `_id`
|
|
64
|
+
* 3. a SIBLING with no stamp -> its label, which genuinely IS its legacy key
|
|
65
|
+
* 4. nothing addressable -> the product's `_id`
|
|
66
|
+
*
|
|
67
|
+
* The caller declares only what it HAS; the sole-vs-sibling decision is
|
|
68
|
+
* internal, so a caller cannot make it wrong. That choice previously lived at
|
|
69
|
+
* every call site across three packages, in two functions with different
|
|
70
|
+
* fallbacks — they disagreed on a sole, unstamped, labelled variant, and the
|
|
71
|
+
* read then looked up a key no quant is stored under.
|
|
72
|
+
*
|
|
73
|
+
* Lives HERE, below every domain, because `@spinekit/order` and
|
|
74
|
+
* `@spinekit/catalog` must not import a peer domain — the rule being one layer
|
|
75
|
+
* too high is exactly why each re-derived it.
|
|
76
|
+
*/
|
|
77
|
+
declare function stockKeyFor(product: StockKeySource, selector: StockKeySelector): StockKey;
|
|
34
78
|
/**
|
|
35
79
|
* {@link stockKeyOf} that THROWS. Use wherever the key feeds a stock movement:
|
|
36
80
|
* a reservation on `undefined` returns "out of stock" instead of failing.
|
|
@@ -58,4 +102,4 @@ declare function skuDisplay(item: SkuFields): SkuDisplay;
|
|
|
58
102
|
*/
|
|
59
103
|
declare function looksLikeOpaqueId(value: string | undefined): boolean;
|
|
60
104
|
//#endregion
|
|
61
|
-
export { SkuDisplay, SkuFields, StockKey, looksLikeOpaqueId, requireStockKey, skuDisplay, stockKeyOf };
|
|
105
|
+
export { SkuDisplay, SkuFields, StockKey, StockKeySelector, StockKeySource, looksLikeOpaqueId, requireStockKey, skuDisplay, stockKeyFor, stockKeyOf };
|
package/dist/item-identity.mjs
CHANGED
|
@@ -14,6 +14,49 @@ function stockKeyOf(item, fallback) {
|
|
|
14
14
|
return nonEmpty(item.skuRef) ?? nonEmpty(item.sku) ?? (fallback !== void 0 ? String(fallback) : void 0);
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
|
+
* THE product-level stock key. One precedence, for every package.
|
|
18
|
+
*
|
|
19
|
+
* 1. the variant's STAMPED `skuRef` — assigned once at birth, immutable
|
|
20
|
+
* 2. a SOLE variant with no stamp -> the product's `_id`. Never the label:
|
|
21
|
+
* legacy writers derived `variantSku || productId`, and for one variant
|
|
22
|
+
* that key IS the `_id`
|
|
23
|
+
* 3. a SIBLING with no stamp -> its label, which genuinely IS its legacy key
|
|
24
|
+
* 4. nothing addressable -> the product's `_id`
|
|
25
|
+
*
|
|
26
|
+
* The caller declares only what it HAS; the sole-vs-sibling decision is
|
|
27
|
+
* internal, so a caller cannot make it wrong. That choice previously lived at
|
|
28
|
+
* every call site across three packages, in two functions with different
|
|
29
|
+
* fallbacks — they disagreed on a sole, unstamped, labelled variant, and the
|
|
30
|
+
* read then looked up a key no quant is stored under.
|
|
31
|
+
*
|
|
32
|
+
* Lives HERE, below every domain, because `@spinekit/order` and
|
|
33
|
+
* `@spinekit/catalog` must not import a peer domain — the rule being one layer
|
|
34
|
+
* too high is exactly why each re-derived it.
|
|
35
|
+
*/
|
|
36
|
+
function stockKeyFor(product, selector) {
|
|
37
|
+
const productId = String(product._id);
|
|
38
|
+
const variants = product.variants ?? [];
|
|
39
|
+
const sole = variants.length <= 1;
|
|
40
|
+
switch (selector.kind) {
|
|
41
|
+
case "sole":
|
|
42
|
+
if (!sole) return productId;
|
|
43
|
+
return nonEmpty(variants[0]?.skuRef) ?? productId;
|
|
44
|
+
case "label": {
|
|
45
|
+
const matched = variants.find((v) => v.sku === selector.sku);
|
|
46
|
+
const stamp = nonEmpty(matched?.skuRef);
|
|
47
|
+
if (stamp !== void 0) return stamp;
|
|
48
|
+
if (sole) return productId;
|
|
49
|
+
return selector.sku;
|
|
50
|
+
}
|
|
51
|
+
case "variant": {
|
|
52
|
+
const stamp = nonEmpty(selector.variant.skuRef);
|
|
53
|
+
if (stamp !== void 0) return stamp;
|
|
54
|
+
if (sole) return productId;
|
|
55
|
+
return stockKeyOf(selector.variant, productId);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
17
60
|
* {@link stockKeyOf} that THROWS. Use wherever the key feeds a stock movement:
|
|
18
61
|
* a reservation on `undefined` returns "out of stock" instead of failing.
|
|
19
62
|
*/
|
|
@@ -47,4 +90,4 @@ function looksLikeOpaqueId(value) {
|
|
|
47
90
|
return typeof value === "string" && /^[0-9a-f]{24}$/i.test(value);
|
|
48
91
|
}
|
|
49
92
|
//#endregion
|
|
50
|
-
export { looksLikeOpaqueId, requireStockKey, skuDisplay, stockKeyOf };
|
|
93
|
+
export { looksLikeOpaqueId, requireStockKey, skuDisplay, stockKeyFor, stockKeyOf };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@classytic/primitives",
|
|
3
|
-
"version": "0.26.
|
|
3
|
+
"version": "0.26.2",
|
|
4
4
|
"description": "Framework-agnostic TypeScript primitives shared across Classytic packages — Money, events, pagination, tenant config, and other building blocks.",
|
|
5
5
|
"author": "Classytic",
|
|
6
6
|
"license": "MIT",
|