@substrat-run/contracts 0.1.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.
@@ -0,0 +1,368 @@
1
+ import { z } from 'zod';
2
+ export declare const semverVersion: z.ZodString;
3
+ export declare const permissionDeclaration: z.ZodObject<{
4
+ key: z.ZodBranded<z.ZodString, "PermissionKey">;
5
+ description: z.ZodString;
6
+ }, "strip", z.ZodTypeAny, {
7
+ key: string & z.BRAND<"PermissionKey">;
8
+ description: string;
9
+ }, {
10
+ key: string;
11
+ description: string;
12
+ }>;
13
+ export type PermissionDeclaration = z.infer<typeof permissionDeclaration>;
14
+ export declare const eventTypeRef: z.ZodObject<{
15
+ type: z.ZodString;
16
+ schemaVersion: z.ZodNumber;
17
+ }, "strip", z.ZodTypeAny, {
18
+ type: string;
19
+ schemaVersion: number;
20
+ }, {
21
+ type: string;
22
+ schemaVersion: number;
23
+ }>;
24
+ export type EventTypeRef = z.infer<typeof eventTypeRef>;
25
+ export declare const moduleManifest: z.ZodObject<{
26
+ id: z.ZodBranded<z.ZodString, "ModuleId">;
27
+ version: z.ZodString;
28
+ kernelContract: z.ZodString;
29
+ permissions: z.ZodArray<z.ZodObject<{
30
+ key: z.ZodBranded<z.ZodString, "PermissionKey">;
31
+ description: z.ZodString;
32
+ }, "strip", z.ZodTypeAny, {
33
+ key: string & z.BRAND<"PermissionKey">;
34
+ description: string;
35
+ }, {
36
+ key: string;
37
+ description: string;
38
+ }>, "many">;
39
+ events: z.ZodObject<{
40
+ emits: z.ZodArray<z.ZodObject<{
41
+ type: z.ZodString;
42
+ schemaVersion: z.ZodNumber;
43
+ }, "strip", z.ZodTypeAny, {
44
+ type: string;
45
+ schemaVersion: number;
46
+ }, {
47
+ type: string;
48
+ schemaVersion: number;
49
+ }>, "many">;
50
+ consumes: z.ZodArray<z.ZodObject<{
51
+ type: z.ZodString;
52
+ schemaVersion: z.ZodNumber;
53
+ }, "strip", z.ZodTypeAny, {
54
+ type: string;
55
+ schemaVersion: number;
56
+ }, {
57
+ type: string;
58
+ schemaVersion: number;
59
+ }>, "many">;
60
+ }, "strip", z.ZodTypeAny, {
61
+ emits: {
62
+ type: string;
63
+ schemaVersion: number;
64
+ }[];
65
+ consumes: {
66
+ type: string;
67
+ schemaVersion: number;
68
+ }[];
69
+ }, {
70
+ emits: {
71
+ type: string;
72
+ schemaVersion: number;
73
+ }[];
74
+ consumes: {
75
+ type: string;
76
+ schemaVersion: number;
77
+ }[];
78
+ }>;
79
+ migrations: z.ZodObject<{
80
+ journalDir: z.ZodString;
81
+ compatibleFrom: z.ZodString;
82
+ }, "strip", z.ZodTypeAny, {
83
+ journalDir: string;
84
+ compatibleFrom: string;
85
+ }, {
86
+ journalDir: string;
87
+ compatibleFrom: string;
88
+ }>;
89
+ attachmentTargets: z.ZodArray<z.ZodObject<{
90
+ entityType: z.ZodString;
91
+ readPermission: z.ZodBranded<z.ZodString, "PermissionKey">;
92
+ }, "strip", z.ZodTypeAny, {
93
+ entityType: string;
94
+ readPermission: string & z.BRAND<"PermissionKey">;
95
+ }, {
96
+ entityType: string;
97
+ readPermission: string;
98
+ }>, "many">;
99
+ entityRelations: z.ZodOptional<z.ZodArray<z.ZodObject<{
100
+ entityType: z.ZodString;
101
+ parentType: z.ZodString;
102
+ }, "strip", z.ZodTypeAny, {
103
+ entityType: string;
104
+ parentType: string;
105
+ }, {
106
+ entityType: string;
107
+ parentType: string;
108
+ }>, "many">>;
109
+ entitlementKey: z.ZodString;
110
+ api: z.ZodOptional<z.ZodString>;
111
+ searchables: z.ZodOptional<z.ZodArray<z.ZodObject<{
112
+ entityType: z.ZodString;
113
+ fields: z.ZodArray<z.ZodString, "many">;
114
+ }, "strip", z.ZodTypeAny, {
115
+ entityType: string;
116
+ fields: string[];
117
+ }, {
118
+ entityType: string;
119
+ fields: string[];
120
+ }>, "many">>;
121
+ ui: z.ZodOptional<z.ZodObject<{
122
+ routes: z.ZodOptional<z.ZodArray<z.ZodObject<{
123
+ path: z.ZodString;
124
+ screen: z.ZodString;
125
+ permission: z.ZodBranded<z.ZodString, "PermissionKey">;
126
+ }, "strip", z.ZodTypeAny, {
127
+ path: string;
128
+ permission: string & z.BRAND<"PermissionKey">;
129
+ screen: string;
130
+ }, {
131
+ path: string;
132
+ permission: string;
133
+ screen: string;
134
+ }>, "many">>;
135
+ nav: z.ZodOptional<z.ZodArray<z.ZodObject<{
136
+ label: z.ZodString;
137
+ icon: z.ZodOptional<z.ZodString>;
138
+ to: z.ZodString;
139
+ permission: z.ZodBranded<z.ZodString, "PermissionKey">;
140
+ }, "strip", z.ZodTypeAny, {
141
+ permission: string & z.BRAND<"PermissionKey">;
142
+ label: string;
143
+ to: string;
144
+ icon?: string | undefined;
145
+ }, {
146
+ permission: string;
147
+ label: string;
148
+ to: string;
149
+ icon?: string | undefined;
150
+ }>, "many">>;
151
+ entityViews: z.ZodOptional<z.ZodArray<z.ZodObject<{
152
+ entityType: z.ZodString;
153
+ view: z.ZodString;
154
+ }, "strip", z.ZodTypeAny, {
155
+ entityType: string;
156
+ view: string;
157
+ }, {
158
+ entityType: string;
159
+ view: string;
160
+ }>, "many">>;
161
+ widgets: z.ZodOptional<z.ZodArray<z.ZodObject<{
162
+ slot: z.ZodString;
163
+ component: z.ZodString;
164
+ permission: z.ZodBranded<z.ZodString, "PermissionKey">;
165
+ }, "strip", z.ZodTypeAny, {
166
+ permission: string & z.BRAND<"PermissionKey">;
167
+ slot: string;
168
+ component: string;
169
+ }, {
170
+ permission: string;
171
+ slot: string;
172
+ component: string;
173
+ }>, "many">>;
174
+ settingsPanels: z.ZodOptional<z.ZodArray<z.ZodObject<{
175
+ label: z.ZodString;
176
+ component: z.ZodString;
177
+ permission: z.ZodBranded<z.ZodString, "PermissionKey">;
178
+ }, "strip", z.ZodTypeAny, {
179
+ permission: string & z.BRAND<"PermissionKey">;
180
+ label: string;
181
+ component: string;
182
+ }, {
183
+ permission: string;
184
+ label: string;
185
+ component: string;
186
+ }>, "many">>;
187
+ }, "strip", z.ZodTypeAny, {
188
+ routes?: {
189
+ path: string;
190
+ permission: string & z.BRAND<"PermissionKey">;
191
+ screen: string;
192
+ }[] | undefined;
193
+ nav?: {
194
+ permission: string & z.BRAND<"PermissionKey">;
195
+ label: string;
196
+ to: string;
197
+ icon?: string | undefined;
198
+ }[] | undefined;
199
+ entityViews?: {
200
+ entityType: string;
201
+ view: string;
202
+ }[] | undefined;
203
+ widgets?: {
204
+ permission: string & z.BRAND<"PermissionKey">;
205
+ slot: string;
206
+ component: string;
207
+ }[] | undefined;
208
+ settingsPanels?: {
209
+ permission: string & z.BRAND<"PermissionKey">;
210
+ label: string;
211
+ component: string;
212
+ }[] | undefined;
213
+ }, {
214
+ routes?: {
215
+ path: string;
216
+ permission: string;
217
+ screen: string;
218
+ }[] | undefined;
219
+ nav?: {
220
+ permission: string;
221
+ label: string;
222
+ to: string;
223
+ icon?: string | undefined;
224
+ }[] | undefined;
225
+ entityViews?: {
226
+ entityType: string;
227
+ view: string;
228
+ }[] | undefined;
229
+ widgets?: {
230
+ permission: string;
231
+ slot: string;
232
+ component: string;
233
+ }[] | undefined;
234
+ settingsPanels?: {
235
+ permission: string;
236
+ label: string;
237
+ component: string;
238
+ }[] | undefined;
239
+ }>>;
240
+ }, "strip", z.ZodTypeAny, {
241
+ id: string & z.BRAND<"ModuleId">;
242
+ permissions: {
243
+ key: string & z.BRAND<"PermissionKey">;
244
+ description: string;
245
+ }[];
246
+ version: string;
247
+ kernelContract: string;
248
+ events: {
249
+ emits: {
250
+ type: string;
251
+ schemaVersion: number;
252
+ }[];
253
+ consumes: {
254
+ type: string;
255
+ schemaVersion: number;
256
+ }[];
257
+ };
258
+ migrations: {
259
+ journalDir: string;
260
+ compatibleFrom: string;
261
+ };
262
+ attachmentTargets: {
263
+ entityType: string;
264
+ readPermission: string & z.BRAND<"PermissionKey">;
265
+ }[];
266
+ entitlementKey: string;
267
+ entityRelations?: {
268
+ entityType: string;
269
+ parentType: string;
270
+ }[] | undefined;
271
+ api?: string | undefined;
272
+ searchables?: {
273
+ entityType: string;
274
+ fields: string[];
275
+ }[] | undefined;
276
+ ui?: {
277
+ routes?: {
278
+ path: string;
279
+ permission: string & z.BRAND<"PermissionKey">;
280
+ screen: string;
281
+ }[] | undefined;
282
+ nav?: {
283
+ permission: string & z.BRAND<"PermissionKey">;
284
+ label: string;
285
+ to: string;
286
+ icon?: string | undefined;
287
+ }[] | undefined;
288
+ entityViews?: {
289
+ entityType: string;
290
+ view: string;
291
+ }[] | undefined;
292
+ widgets?: {
293
+ permission: string & z.BRAND<"PermissionKey">;
294
+ slot: string;
295
+ component: string;
296
+ }[] | undefined;
297
+ settingsPanels?: {
298
+ permission: string & z.BRAND<"PermissionKey">;
299
+ label: string;
300
+ component: string;
301
+ }[] | undefined;
302
+ } | undefined;
303
+ }, {
304
+ id: string;
305
+ permissions: {
306
+ key: string;
307
+ description: string;
308
+ }[];
309
+ version: string;
310
+ kernelContract: string;
311
+ events: {
312
+ emits: {
313
+ type: string;
314
+ schemaVersion: number;
315
+ }[];
316
+ consumes: {
317
+ type: string;
318
+ schemaVersion: number;
319
+ }[];
320
+ };
321
+ migrations: {
322
+ journalDir: string;
323
+ compatibleFrom: string;
324
+ };
325
+ attachmentTargets: {
326
+ entityType: string;
327
+ readPermission: string;
328
+ }[];
329
+ entitlementKey: string;
330
+ entityRelations?: {
331
+ entityType: string;
332
+ parentType: string;
333
+ }[] | undefined;
334
+ api?: string | undefined;
335
+ searchables?: {
336
+ entityType: string;
337
+ fields: string[];
338
+ }[] | undefined;
339
+ ui?: {
340
+ routes?: {
341
+ path: string;
342
+ permission: string;
343
+ screen: string;
344
+ }[] | undefined;
345
+ nav?: {
346
+ permission: string;
347
+ label: string;
348
+ to: string;
349
+ icon?: string | undefined;
350
+ }[] | undefined;
351
+ entityViews?: {
352
+ entityType: string;
353
+ view: string;
354
+ }[] | undefined;
355
+ widgets?: {
356
+ permission: string;
357
+ slot: string;
358
+ component: string;
359
+ }[] | undefined;
360
+ settingsPanels?: {
361
+ permission: string;
362
+ label: string;
363
+ component: string;
364
+ }[] | undefined;
365
+ } | undefined;
366
+ }>;
367
+ export type ModuleManifest = z.infer<typeof moduleManifest>;
368
+ //# sourceMappingURL=manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAOxB,eAAO,MAAM,aAAa,aAEsC,CAAC;AAEjE,eAAO,MAAM,qBAAqB;;;;;;;;;EAGhC,CAAC;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAE1E,eAAO,MAAM,YAAY;;;;;;;;;EAGvB,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAExD,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuEzB,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC"}
@@ -0,0 +1,81 @@
1
+ import { z } from 'zod';
2
+ import { moduleId, permissionKey } from './ids.js';
3
+ import { eventType } from './events.js';
4
+ // The manifest is what makes a module self-describing — to agents now, to
5
+ // strangers buying it later (§5.6 of the plan, §7.1 of the design doc).
6
+ export const semverVersion = z
7
+ .string()
8
+ .regex(/^\d+\.\d+\.\d+(-[0-9A-Za-z-.]+)?(\+[0-9A-Za-z-.]+)?$/);
9
+ export const permissionDeclaration = z.object({
10
+ key: permissionKey,
11
+ description: z.string().min(1), // fuel for the human-readable permission diff (§4.3)
12
+ });
13
+ export const eventTypeRef = z.object({
14
+ type: eventType,
15
+ schemaVersion: z.number().int().positive(),
16
+ });
17
+ export const moduleManifest = z.object({
18
+ id: moduleId, // '@substrat-run/engine-workorder'
19
+ version: semverVersion,
20
+ kernelContract: z.string().min(1), // semver range of the kernel API it targets
21
+ permissions: z.array(permissionDeclaration),
22
+ events: z.object({
23
+ emits: z.array(eventTypeRef),
24
+ consumes: z.array(eventTypeRef), // star topology: consumes types, never siblings (D-19)
25
+ }),
26
+ migrations: z.object({
27
+ journalDir: z.string().min(1), // Drizzle journal location within the package
28
+ compatibleFrom: z.string().min(1), // skew window: oldest schema this code tolerates
29
+ }),
30
+ attachmentTargets: z.array(z.object({
31
+ entityType: z.string().min(1),
32
+ readPermission: permissionKey, // attachment access checks the owning entity's key
33
+ })),
34
+ // Declared entity parent edges, e.g. workorder → facility. Permission flows
35
+ // along these in the tuple evaluator's fixed algebra (design doc §4.2 rule 3,
36
+ // depth-capped) — how entity-narrowed grants resolve.
37
+ entityRelations: z
38
+ .array(z.object({
39
+ entityType: z.string().min(1), // 'workorder'
40
+ parentType: z.string().min(1), // 'facility'
41
+ }))
42
+ .optional(),
43
+ entitlementKey: z.string().regex(/^[a-z0-9-]+$/), // the SKU flag that gates loading (D-20)
44
+ api: z.string().optional(), // path to emitted OAS for the HTTP surface, if any (D-22)
45
+ searchables: z
46
+ .array(z.object({
47
+ entityType: z.string().min(1),
48
+ fields: z.array(z.string().min(1)).min(1),
49
+ }))
50
+ .optional(),
51
+ // UI contributions, composed into the vertical's app at BUILD time by the
52
+ // shell (design doc §7.4, K-15). Component values are module-relative import
53
+ // paths. All contributions are permission-keyed; the shell renders them
54
+ // permission-aware from the proof-path checker.
55
+ ui: z
56
+ .object({
57
+ routes: z
58
+ .array(z.object({ path: z.string().min(1), screen: z.string().min(1), permission: permissionKey }))
59
+ .optional(),
60
+ nav: z
61
+ .array(z.object({
62
+ label: z.string().min(1), // i18n key
63
+ icon: z.string().optional(),
64
+ to: z.string().min(1),
65
+ permission: permissionKey,
66
+ }))
67
+ .optional(),
68
+ // the EntityRef → view registry: cross-engine rendering without imports
69
+ entityViews: z
70
+ .array(z.object({ entityType: z.string().min(1), view: z.string().min(1) }))
71
+ .optional(),
72
+ widgets: z
73
+ .array(z.object({ slot: z.string().min(1), component: z.string().min(1), permission: permissionKey }))
74
+ .optional(),
75
+ settingsPanels: z
76
+ .array(z.object({ label: z.string().min(1), component: z.string().min(1), permission: permissionKey }))
77
+ .optional(),
78
+ })
79
+ .optional(),
80
+ });
81
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import { z } from 'zod';
2
+ export declare const currencyCode: z.ZodBranded<z.ZodString, "CurrencyCode">;
3
+ export type CurrencyCode = z.infer<typeof currencyCode>;
4
+ export declare const moneyAmount: z.ZodBranded<z.ZodString, "MoneyAmount">;
5
+ export type MoneyAmount = z.infer<typeof moneyAmount>;
6
+ export declare const money: z.ZodObject<{
7
+ amount: z.ZodBranded<z.ZodString, "MoneyAmount">;
8
+ currency: z.ZodBranded<z.ZodString, "CurrencyCode">;
9
+ }, "strip", z.ZodTypeAny, {
10
+ amount: string & z.BRAND<"MoneyAmount">;
11
+ currency: string & z.BRAND<"CurrencyCode">;
12
+ }, {
13
+ amount: string;
14
+ currency: string;
15
+ }>;
16
+ export type Money = z.infer<typeof money>;
17
+ /** quantity × unit price, half-up at 6 dp. Both args are decimal strings. */
18
+ export declare function mulDecimal(qty: string, amount: string): string;
19
+ export declare function addDecimal(a: string, b: string): string;
20
+ export declare function compareDecimal(a: string, b: string): -1 | 0 | 1;
21
+ export declare function moneyOf(amount: string, currency: string): Money;
22
+ export declare function addMoney(a: Money, b: Money): Money;
23
+ export declare function mulMoney(qty: string, unitPrice: Money): Money;
24
+ //# sourceMappingURL=money.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"money.d.ts","sourceRoot":"","sources":["../src/money.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAOxB,eAAO,MAAM,YAAY,2CAGC,CAAC;AAC3B,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAExD,eAAO,MAAM,WAAW,0CAGC,CAAC;AAC1B,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEtD,eAAO,MAAM,KAAK;;;;;;;;;EAGhB,CAAC;AACH,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,KAAK,CAAC,CAAC;AA0B1C,6EAA6E;AAC7E,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAK9D;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAG/D;AAED,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,KAAK,CAE/D;AAED,wBAAgB,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,GAAG,KAAK,CAKlD;AAED,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,GAAG,KAAK,CAE7D"}
package/dist/money.js ADDED
@@ -0,0 +1,64 @@
1
+ import { z } from 'zod';
2
+ // The one money representation (K-14). Decimal strings, never floats — the
3
+ // Tier 1 ↔ Tier 2 reconciliation guarantee (plan §5.3) needs uniform,
4
+ // exact money handling across every engine. Multi-currency from day one
5
+ // (SEK/NOK evidence in the FSM survey).
6
+ export const currencyCode = z
7
+ .string()
8
+ .regex(/^[A-Z]{3}$/) // ISO 4217
9
+ .brand();
10
+ export const moneyAmount = z
11
+ .string()
12
+ .regex(/^-?\d+(\.\d{1,6})?$/)
13
+ .brand();
14
+ export const money = z.object({
15
+ amount: moneyAmount,
16
+ currency: currencyCode,
17
+ });
18
+ // ---------------------------------------------------------------------------
19
+ // Exact decimal arithmetic on micro-units (6 dp, bigint) — the ONLY sanctioned
20
+ // way engines compute with money. Half-up rounding at the 6th decimal.
21
+ // ---------------------------------------------------------------------------
22
+ const SCALE = 1000000n;
23
+ function toMicro(decimal) {
24
+ const negative = decimal.startsWith('-');
25
+ const parts = (negative ? decimal.slice(1) : decimal).split('.');
26
+ const intPart = parts[0] ?? '0';
27
+ const fracPart = parts[1] ?? '';
28
+ const micro = BigInt(intPart) * SCALE + BigInt(fracPart.padEnd(6, '0').slice(0, 6));
29
+ return negative ? -micro : micro;
30
+ }
31
+ function fromMicro(micro) {
32
+ const negative = micro < 0n;
33
+ const abs = negative ? -micro : micro;
34
+ const intPart = abs / SCALE;
35
+ const frac = (abs % SCALE).toString().padStart(6, '0').replace(/0+$/, '');
36
+ return `${negative ? '-' : ''}${intPart}${frac ? `.${frac}` : ''}`;
37
+ }
38
+ /** quantity × unit price, half-up at 6 dp. Both args are decimal strings. */
39
+ export function mulDecimal(qty, amount) {
40
+ const product = toMicro(qty) * toMicro(amount);
41
+ const sign = product < 0n ? -1n : 1n;
42
+ const abs = product < 0n ? -product : product;
43
+ return fromMicro(sign * ((abs + SCALE / 2n) / SCALE));
44
+ }
45
+ export function addDecimal(a, b) {
46
+ return fromMicro(toMicro(a) + toMicro(b));
47
+ }
48
+ export function compareDecimal(a, b) {
49
+ const [ma, mb] = [toMicro(a), toMicro(b)];
50
+ return ma < mb ? -1 : ma > mb ? 1 : 0;
51
+ }
52
+ export function moneyOf(amount, currency) {
53
+ return money.parse({ amount, currency });
54
+ }
55
+ export function addMoney(a, b) {
56
+ if (a.currency !== b.currency) {
57
+ throw new Error(`currency mismatch: ${a.currency} vs ${b.currency}`);
58
+ }
59
+ return moneyOf(addDecimal(a.amount, b.amount), a.currency);
60
+ }
61
+ export function mulMoney(qty, unitPrice) {
62
+ return moneyOf(mulDecimal(qty, unitPrice.amount), unitPrice.currency);
63
+ }
64
+ //# sourceMappingURL=money.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"money.js","sourceRoot":"","sources":["../src/money.ts"],"names":[],"mappings":"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"}