@classytic/primitives 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +57 -0
- package/dist/money.d.mts +33 -1
- package/dist/money.mjs +13 -1
- package/dist/state-machine.d.mts +103 -0
- package/dist/state-machine.mjs +94 -0
- package/dist/tenant.d.mts +8 -1
- package/dist/tenant.mjs +4 -0
- package/package.json +7 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,63 @@
|
|
|
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.2.0]
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- **`/state-machine` subpath** — `defineStateMachine<TStatus>({ name, transitions, errorFactory? })`
|
|
11
|
+
declarative state-transition primitive. Replaces hand-rolled
|
|
12
|
+
`if (status === 'X') throw` blocks across kernels:
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
import { defineStateMachine } from '@classytic/primitives/state-machine';
|
|
16
|
+
|
|
17
|
+
const ORDER_MACHINE = defineStateMachine<OrderStatus>({
|
|
18
|
+
name: 'Order',
|
|
19
|
+
transitions: {
|
|
20
|
+
draft: ['approved', 'cancelled'],
|
|
21
|
+
approved: ['shipped', 'cancelled'],
|
|
22
|
+
shipped: [],
|
|
23
|
+
cancelled: [],
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
ORDER_MACHINE.assertTransition('order-1', 'draft', 'approved'); // OK
|
|
28
|
+
ORDER_MACHINE.assertTransition('order-1', 'shipped', 'draft'); // throws
|
|
29
|
+
ORDER_MACHINE.canTransition('draft', 'shipped'); // false
|
|
30
|
+
ORDER_MACHINE.isTerminal('shipped'); // true
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Generic over the status string-union — `Record<TStatus, readonly TStatus[]>`
|
|
34
|
+
on `transitions` enforces exhaustiveness at the call site. Kernels with their
|
|
35
|
+
own typed transition error class (e.g. flow's `InvalidTransitionError`) wire
|
|
36
|
+
it via `errorFactory` so the thrown type stays domain-consistent. Standalone
|
|
37
|
+
callers get the bundled `IllegalTransitionError` (carries `entityType` /
|
|
38
|
+
`entityId` / `from` / `to` + `code: 'illegal_transition'` + `status: 422`).
|
|
39
|
+
|
|
40
|
+
First consumer: `@classytic/flow` `PROCUREMENT_MACHINE` (procurement-order
|
|
41
|
+
approve / cancel transitions).
|
|
42
|
+
|
|
43
|
+
## [0.1.1]
|
|
44
|
+
|
|
45
|
+
### Added
|
|
46
|
+
|
|
47
|
+
- **`MinorUnits<C>` branded type on `/money`** — wire-format monetary amount
|
|
48
|
+
(integer minor units, branded with phantom currency) for HTTP/JSON fields
|
|
49
|
+
where a full `Money` struct is overkill. Erases at runtime; `MinorUnits<'BDT'>`
|
|
50
|
+
vs `MinorUnits<'USD'>` collide at compile time. Pair with structured `Money`
|
|
51
|
+
for in-process arithmetic. Constructor: `MinorUnits(amount, currency?)`.
|
|
52
|
+
|
|
53
|
+
### Changed
|
|
54
|
+
|
|
55
|
+
- **`/tenant` `contextKey` cascade default** — `resolveTenantConfig({ tenantField: 'branchId' })`
|
|
56
|
+
now defaults `contextKey` to `'branchId'` instead of `'organizationId'`.
|
|
57
|
+
When a host renames `tenantField`, their `OperationContext` carries the id
|
|
58
|
+
under the same key in the overwhelming majority of cases; mirroring the
|
|
59
|
+
rename is the least-surprise default. Explicit `contextKey` still overrides
|
|
60
|
+
(set when the doc field and context key genuinely diverge). Existing call
|
|
61
|
+
sites that supplied both `tenantField` and `contextKey` are unaffected.
|
|
62
|
+
|
|
6
63
|
## [0.1.0]
|
|
7
64
|
|
|
8
65
|
Initial release. Framework-agnostic TypeScript primitives shared across
|
package/dist/money.d.mts
CHANGED
|
@@ -1,6 +1,38 @@
|
|
|
1
1
|
import { CurrencyCode } from "./currency.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/money.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Wire-format monetary amount: integer minor units, branded with a phantom
|
|
6
|
+
* currency type so calls like `formatBdt(usdValue)` fail at compile time.
|
|
7
|
+
*
|
|
8
|
+
* Use this for HTTP/JSON fields where transmitting a `Money` struct would be
|
|
9
|
+
* overkill: `interface Order { grandTotal: MinorUnits<'BDT'>; ... }`. Pair
|
|
10
|
+
* with the structured {@link Money} for in-process arithmetic.
|
|
11
|
+
*
|
|
12
|
+
* Erases at runtime — branded types have zero cost.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* type Paisa = MinorUnits<'BDT'>;
|
|
16
|
+
* type UsdCents = MinorUnits<'USD'>;
|
|
17
|
+
* const p: Paisa = MinorUnits(489900, 'BDT');
|
|
18
|
+
* const c: UsdCents = p; // ❌ type error: BDT vs USD
|
|
19
|
+
*/
|
|
20
|
+
declare const __unitBrand: unique symbol;
|
|
21
|
+
declare const __currencyBrand: unique symbol;
|
|
22
|
+
type MinorUnits<C extends string = string> = number & {
|
|
23
|
+
readonly [__unitBrand]: 'minor';
|
|
24
|
+
readonly [__currencyBrand]: C;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Cast a raw number to {@link MinorUnits}. Pass the currency as the second
|
|
28
|
+
* argument to fix the phantom type. With no second arg, `MinorUnits<string>`
|
|
29
|
+
* — useful for generic SDK boundaries that don't know the currency yet.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* MinorUnits(489900, 'BDT') // typed MinorUnits<'BDT'>
|
|
33
|
+
* MinorUnits(199, 'USD') // typed MinorUnits<'USD'>
|
|
34
|
+
*/
|
|
35
|
+
declare function MinorUnits<C extends string = string>(amount: number, _currency?: C): MinorUnits<C>;
|
|
4
36
|
/**
|
|
5
37
|
* A monetary amount stored as an integer in the currency's minor unit.
|
|
6
38
|
*
|
|
@@ -75,4 +107,4 @@ declare function absMoney(m: Money): Money;
|
|
|
75
107
|
/** Structural type guard. */
|
|
76
108
|
declare function isMoney(value: unknown): value is Money;
|
|
77
109
|
//#endregion
|
|
78
|
-
export { CurrencyMismatchError, Money, absMoney, addMoney, compareMoney, equalsMoney, fromMajor, isMoney, isNegativeMoney, isPositiveMoney, isZeroMoney, money, multiplyMoney, negateMoney, subtractMoney, sumMoney, toMajor };
|
|
110
|
+
export { CurrencyMismatchError, MinorUnits, Money, absMoney, addMoney, compareMoney, equalsMoney, fromMajor, isMoney, isNegativeMoney, isPositiveMoney, isZeroMoney, money, multiplyMoney, negateMoney, subtractMoney, sumMoney, toMajor };
|
package/dist/money.mjs
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import { minorUnitFactor } from "./currency.mjs";
|
|
2
2
|
//#region src/money.ts
|
|
3
|
+
/**
|
|
4
|
+
* Cast a raw number to {@link MinorUnits}. Pass the currency as the second
|
|
5
|
+
* argument to fix the phantom type. With no second arg, `MinorUnits<string>`
|
|
6
|
+
* — useful for generic SDK boundaries that don't know the currency yet.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* MinorUnits(489900, 'BDT') // typed MinorUnits<'BDT'>
|
|
10
|
+
* MinorUnits(199, 'USD') // typed MinorUnits<'USD'>
|
|
11
|
+
*/
|
|
12
|
+
function MinorUnits(amount, _currency) {
|
|
13
|
+
return amount;
|
|
14
|
+
}
|
|
3
15
|
var CurrencyMismatchError = class extends Error {
|
|
4
16
|
name = "CurrencyMismatchError";
|
|
5
17
|
left;
|
|
@@ -128,4 +140,4 @@ function isMoney(value) {
|
|
|
128
140
|
return typeof value === "object" && value !== null && typeof value.amount === "number" && Number.isInteger(value.amount) && typeof value.currency === "string";
|
|
129
141
|
}
|
|
130
142
|
//#endregion
|
|
131
|
-
export { CurrencyMismatchError, absMoney, addMoney, compareMoney, equalsMoney, fromMajor, isMoney, isNegativeMoney, isPositiveMoney, isZeroMoney, money, multiplyMoney, negateMoney, subtractMoney, sumMoney, toMajor };
|
|
143
|
+
export { CurrencyMismatchError, MinorUnits, absMoney, addMoney, compareMoney, equalsMoney, fromMajor, isMoney, isNegativeMoney, isPositiveMoney, isZeroMoney, money, multiplyMoney, negateMoney, subtractMoney, sumMoney, toMajor };
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
//#region src/state-machine.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Declarative state-machine primitive.
|
|
4
|
+
*
|
|
5
|
+
* Replaces hand-rolled `if (status === 'X') throw` blocks scattered
|
|
6
|
+
* across domain services. Each aggregate declares one transition table;
|
|
7
|
+
* the helper provides `canTransition` / `assertTransition` / `isTerminal`
|
|
8
|
+
* accessors that the services share.
|
|
9
|
+
*
|
|
10
|
+
* Generic over the status string-union so calls stay type-safe:
|
|
11
|
+
* `defineStateMachine<ProcurementStatus>({ name: 'ProcurementOrder', transitions: { … } })`
|
|
12
|
+
* The TS compiler enforces exhaustiveness — every status string in the
|
|
13
|
+
* union MUST have an entry in `transitions` (terminal statuses map to `[]`).
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* import { defineStateMachine } from '@classytic/primitives/state-machine';
|
|
17
|
+
*
|
|
18
|
+
* type OrderStatus = 'draft' | 'approved' | 'shipped' | 'cancelled';
|
|
19
|
+
* const ORDER_MACHINE = defineStateMachine<OrderStatus>({
|
|
20
|
+
* name: 'Order',
|
|
21
|
+
* transitions: {
|
|
22
|
+
* draft: ['approved', 'cancelled'],
|
|
23
|
+
* approved: ['shipped', 'cancelled'],
|
|
24
|
+
* shipped: [],
|
|
25
|
+
* cancelled: [],
|
|
26
|
+
* },
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* ORDER_MACHINE.assertTransition('order-1', 'draft', 'approved'); // OK
|
|
30
|
+
* ORDER_MACHINE.assertTransition('order-1', 'shipped', 'draft'); // throws
|
|
31
|
+
* ORDER_MACHINE.canTransition('draft', 'shipped'); // false
|
|
32
|
+
* ORDER_MACHINE.isTerminal('shipped'); // true
|
|
33
|
+
*
|
|
34
|
+
* Hosts that already have their own typed transition error can swap it
|
|
35
|
+
* in via `errorFactory`:
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* import { InvalidTransitionError } from './errors.js';
|
|
39
|
+
* const FLOW_MACHINE = defineStateMachine<MoveStatus>({
|
|
40
|
+
* name: 'StockMove',
|
|
41
|
+
* transitions: { … },
|
|
42
|
+
* errorFactory: ({ entityType, entityId, from, to }) =>
|
|
43
|
+
* new InvalidTransitionError(entityType, entityId, from, to),
|
|
44
|
+
* });
|
|
45
|
+
*/
|
|
46
|
+
/**
|
|
47
|
+
* Default error thrown by `assertTransition` when no `errorFactory` is
|
|
48
|
+
* supplied. Carries the structured fields (`entityType`, `entityId`,
|
|
49
|
+
* `from`, `to`) plus a stable `code` so hosts can pattern-match on
|
|
50
|
+
* either `instanceof` or `error.code === 'illegal_transition'`.
|
|
51
|
+
*/
|
|
52
|
+
declare class IllegalTransitionError extends Error {
|
|
53
|
+
readonly code: "illegal_transition";
|
|
54
|
+
readonly status = 422;
|
|
55
|
+
readonly entityType: string;
|
|
56
|
+
readonly entityId: string;
|
|
57
|
+
readonly from: string;
|
|
58
|
+
readonly to: string;
|
|
59
|
+
constructor(entityType: string, entityId: string, from: string, to: string);
|
|
60
|
+
}
|
|
61
|
+
interface TransitionErrorContext {
|
|
62
|
+
readonly entityType: string;
|
|
63
|
+
readonly entityId: string;
|
|
64
|
+
readonly from: string;
|
|
65
|
+
readonly to: string;
|
|
66
|
+
}
|
|
67
|
+
interface StateMachineDefinition<TStatus extends string> {
|
|
68
|
+
/** Aggregate name surfaced in the thrown error's `entityType`. */
|
|
69
|
+
name: string;
|
|
70
|
+
/**
|
|
71
|
+
* Adjacency list — for each status, the set of statuses you can
|
|
72
|
+
* legally transition TO. Terminal statuses map to an empty array.
|
|
73
|
+
* `Record<TStatus, …>` enforces exhaustiveness at the call site.
|
|
74
|
+
*/
|
|
75
|
+
transitions: Record<TStatus, readonly TStatus[]>;
|
|
76
|
+
/**
|
|
77
|
+
* Optional custom error factory. When omitted, `assertTransition`
|
|
78
|
+
* throws `IllegalTransitionError`. Hosts that own a domain error
|
|
79
|
+
* (e.g. flow's `InvalidTransitionError`) wire their own here so the
|
|
80
|
+
* thrown type stays consistent with the rest of the package.
|
|
81
|
+
*/
|
|
82
|
+
errorFactory?: (ctx: TransitionErrorContext) => Error;
|
|
83
|
+
}
|
|
84
|
+
interface StateMachine<TStatus extends string> {
|
|
85
|
+
/** The aggregate name (`StockMove`, `ProcurementOrder`, etc.). */
|
|
86
|
+
readonly name: string;
|
|
87
|
+
/** All terminal statuses (no outgoing transitions). */
|
|
88
|
+
readonly terminal: ReadonlySet<TStatus>;
|
|
89
|
+
/** True when `from → to` is in the transition table. */
|
|
90
|
+
canTransition(from: TStatus, to: TStatus): boolean;
|
|
91
|
+
/** True when `status` is terminal. */
|
|
92
|
+
isTerminal(status: TStatus): boolean;
|
|
93
|
+
/**
|
|
94
|
+
* Throw a transition error when `from → to` isn't allowed.
|
|
95
|
+
* `entityId` is included in the error for traceability.
|
|
96
|
+
*/
|
|
97
|
+
assertTransition(entityId: string, from: TStatus, to: TStatus): void;
|
|
98
|
+
/** The raw transition table — exposed read-only for advanced callers. */
|
|
99
|
+
readonly transitions: Readonly<Record<TStatus, readonly TStatus[]>>;
|
|
100
|
+
}
|
|
101
|
+
declare function defineStateMachine<TStatus extends string>(definition: StateMachineDefinition<TStatus>): StateMachine<TStatus>;
|
|
102
|
+
//#endregion
|
|
103
|
+
export { IllegalTransitionError, StateMachine, StateMachineDefinition, TransitionErrorContext, defineStateMachine };
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
//#region src/state-machine.ts
|
|
2
|
+
/**
|
|
3
|
+
* Declarative state-machine primitive.
|
|
4
|
+
*
|
|
5
|
+
* Replaces hand-rolled `if (status === 'X') throw` blocks scattered
|
|
6
|
+
* across domain services. Each aggregate declares one transition table;
|
|
7
|
+
* the helper provides `canTransition` / `assertTransition` / `isTerminal`
|
|
8
|
+
* accessors that the services share.
|
|
9
|
+
*
|
|
10
|
+
* Generic over the status string-union so calls stay type-safe:
|
|
11
|
+
* `defineStateMachine<ProcurementStatus>({ name: 'ProcurementOrder', transitions: { … } })`
|
|
12
|
+
* The TS compiler enforces exhaustiveness — every status string in the
|
|
13
|
+
* union MUST have an entry in `transitions` (terminal statuses map to `[]`).
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* import { defineStateMachine } from '@classytic/primitives/state-machine';
|
|
17
|
+
*
|
|
18
|
+
* type OrderStatus = 'draft' | 'approved' | 'shipped' | 'cancelled';
|
|
19
|
+
* const ORDER_MACHINE = defineStateMachine<OrderStatus>({
|
|
20
|
+
* name: 'Order',
|
|
21
|
+
* transitions: {
|
|
22
|
+
* draft: ['approved', 'cancelled'],
|
|
23
|
+
* approved: ['shipped', 'cancelled'],
|
|
24
|
+
* shipped: [],
|
|
25
|
+
* cancelled: [],
|
|
26
|
+
* },
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* ORDER_MACHINE.assertTransition('order-1', 'draft', 'approved'); // OK
|
|
30
|
+
* ORDER_MACHINE.assertTransition('order-1', 'shipped', 'draft'); // throws
|
|
31
|
+
* ORDER_MACHINE.canTransition('draft', 'shipped'); // false
|
|
32
|
+
* ORDER_MACHINE.isTerminal('shipped'); // true
|
|
33
|
+
*
|
|
34
|
+
* Hosts that already have their own typed transition error can swap it
|
|
35
|
+
* in via `errorFactory`:
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* import { InvalidTransitionError } from './errors.js';
|
|
39
|
+
* const FLOW_MACHINE = defineStateMachine<MoveStatus>({
|
|
40
|
+
* name: 'StockMove',
|
|
41
|
+
* transitions: { … },
|
|
42
|
+
* errorFactory: ({ entityType, entityId, from, to }) =>
|
|
43
|
+
* new InvalidTransitionError(entityType, entityId, from, to),
|
|
44
|
+
* });
|
|
45
|
+
*/
|
|
46
|
+
/**
|
|
47
|
+
* Default error thrown by `assertTransition` when no `errorFactory` is
|
|
48
|
+
* supplied. Carries the structured fields (`entityType`, `entityId`,
|
|
49
|
+
* `from`, `to`) plus a stable `code` so hosts can pattern-match on
|
|
50
|
+
* either `instanceof` or `error.code === 'illegal_transition'`.
|
|
51
|
+
*/
|
|
52
|
+
var IllegalTransitionError = class extends Error {
|
|
53
|
+
code = "illegal_transition";
|
|
54
|
+
status = 422;
|
|
55
|
+
entityType;
|
|
56
|
+
entityId;
|
|
57
|
+
from;
|
|
58
|
+
to;
|
|
59
|
+
constructor(entityType, entityId, from, to) {
|
|
60
|
+
super(`Invalid transition for ${entityType} ${entityId}: ${from} → ${to}`);
|
|
61
|
+
this.name = "IllegalTransitionError";
|
|
62
|
+
this.entityType = entityType;
|
|
63
|
+
this.entityId = entityId;
|
|
64
|
+
this.from = from;
|
|
65
|
+
this.to = to;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
function defineStateMachine(definition) {
|
|
69
|
+
const { name, transitions, errorFactory } = definition;
|
|
70
|
+
const terminal = /* @__PURE__ */ new Set();
|
|
71
|
+
for (const [status, allowed] of Object.entries(transitions)) if (allowed.length === 0) terminal.add(status);
|
|
72
|
+
const buildError = errorFactory ? errorFactory : (ctx) => new IllegalTransitionError(ctx.entityType, ctx.entityId, ctx.from, ctx.to);
|
|
73
|
+
return {
|
|
74
|
+
name,
|
|
75
|
+
terminal,
|
|
76
|
+
transitions,
|
|
77
|
+
canTransition(from, to) {
|
|
78
|
+
return transitions[from]?.includes(to) ?? false;
|
|
79
|
+
},
|
|
80
|
+
isTerminal(status) {
|
|
81
|
+
return terminal.has(status);
|
|
82
|
+
},
|
|
83
|
+
assertTransition(entityId, from, to) {
|
|
84
|
+
if (!transitions[from]?.includes(to)) throw buildError({
|
|
85
|
+
entityType: name,
|
|
86
|
+
entityId,
|
|
87
|
+
from,
|
|
88
|
+
to
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
//#endregion
|
|
94
|
+
export { IllegalTransitionError, defineStateMachine };
|
package/dist/tenant.d.mts
CHANGED
|
@@ -71,7 +71,14 @@ interface TenantConfig {
|
|
|
71
71
|
/**
|
|
72
72
|
* Which key on `OperationContext` to read the tenant id from.
|
|
73
73
|
*
|
|
74
|
-
*
|
|
74
|
+
* Defaults cascade: if omitted, falls back to the caller's `tenantField`
|
|
75
|
+
* (if supplied), else to `'organizationId'`. Rationale: when a host renames
|
|
76
|
+
* `tenantField` to e.g. `'branchId'`, their `OperationContext` almost always
|
|
77
|
+
* carries the value under the same key — so mirroring `tenantField` is the
|
|
78
|
+
* least-surprise behavior. Override explicitly if the context key diverges
|
|
79
|
+
* from the document field (e.g. `tenantField: 'branchId'`, `contextKey: 'organizationId'`).
|
|
80
|
+
*
|
|
81
|
+
* @default tenantField ?? 'organizationId'
|
|
75
82
|
*/
|
|
76
83
|
contextKey?: string;
|
|
77
84
|
/**
|
package/dist/tenant.mjs
CHANGED
|
@@ -26,9 +26,11 @@ function resolveTenantConfig(config) {
|
|
|
26
26
|
};
|
|
27
27
|
if (config === true || config === void 0) return { ...DEFAULT_TENANT_CONFIG };
|
|
28
28
|
const strategy = config.strategy ?? (config.enabled === false ? "none" : "field");
|
|
29
|
+
const contextKey = config.contextKey ?? config.tenantField ?? DEFAULT_TENANT_CONFIG.contextKey;
|
|
29
30
|
if (strategy === "none") return {
|
|
30
31
|
...DEFAULT_TENANT_CONFIG,
|
|
31
32
|
...config,
|
|
33
|
+
contextKey,
|
|
32
34
|
strategy: "none",
|
|
33
35
|
enabled: false,
|
|
34
36
|
required: false
|
|
@@ -38,6 +40,7 @@ function resolveTenantConfig(config) {
|
|
|
38
40
|
return {
|
|
39
41
|
...DEFAULT_TENANT_CONFIG,
|
|
40
42
|
...config,
|
|
43
|
+
contextKey,
|
|
41
44
|
strategy: "custom",
|
|
42
45
|
enabled: config.enabled ?? true,
|
|
43
46
|
resolve: config.resolve
|
|
@@ -46,6 +49,7 @@ function resolveTenantConfig(config) {
|
|
|
46
49
|
return {
|
|
47
50
|
...DEFAULT_TENANT_CONFIG,
|
|
48
51
|
...config,
|
|
52
|
+
contextKey,
|
|
49
53
|
strategy: "field",
|
|
50
54
|
enabled: config.enabled ?? true
|
|
51
55
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@classytic/primitives",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
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",
|
|
@@ -97,6 +97,10 @@
|
|
|
97
97
|
"types": "./dist/person.d.mts",
|
|
98
98
|
"default": "./dist/person.mjs"
|
|
99
99
|
},
|
|
100
|
+
"./state-machine": {
|
|
101
|
+
"types": "./dist/state-machine.d.mts",
|
|
102
|
+
"default": "./dist/state-machine.mjs"
|
|
103
|
+
},
|
|
100
104
|
"./package.json": "./package.json"
|
|
101
105
|
},
|
|
102
106
|
"files": [
|
|
@@ -117,11 +121,13 @@
|
|
|
117
121
|
"test:all": "vitest run",
|
|
118
122
|
"test:watch": "vitest --project unit --project integration",
|
|
119
123
|
"test:smoke": "node tests/smoke/smoke.mjs",
|
|
124
|
+
"push": "classytic-push",
|
|
120
125
|
"prepublishOnly": "npm run typecheck && npm run test && npm run build && npm run test:smoke",
|
|
121
126
|
"release": "npm run build && npm run typecheck && npm publish --access public"
|
|
122
127
|
},
|
|
123
128
|
"devDependencies": {
|
|
124
129
|
"@biomejs/biome": "^2.4.9",
|
|
130
|
+
"@classytic/dev-tools": "^0.2.0",
|
|
125
131
|
"@types/node": "^25.5.0",
|
|
126
132
|
"tsdown": "^0.21.7",
|
|
127
133
|
"typescript": "^5.7.0",
|