@nicolastoulemont/std 0.8.2 → 0.10.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/README.md +391 -40
- package/dist/brand/index.d.mts +1 -1
- package/dist/brand/index.mjs +1 -1
- package/dist/{brand-DZgGDrAe.mjs → brand-DP-C92GS.mjs} +2 -2
- package/dist/{brand-DZgGDrAe.mjs.map → brand-DP-C92GS.mjs.map} +1 -1
- package/dist/{brand.types-B3NDX1vo.d.mts → brand.types-C_7QgCA4.d.mts} +1 -1
- package/dist/{brand.types-B3NDX1vo.d.mts.map → brand.types-C_7QgCA4.d.mts.map} +1 -1
- package/dist/context/index.d.mts +1 -1
- package/dist/context/index.mjs +1 -1
- package/dist/{context-0xDbwtpx.mjs → context-7oKePrBY.mjs} +2 -2
- package/dist/{context-0xDbwtpx.mjs.map → context-7oKePrBY.mjs.map} +1 -1
- package/dist/{context-B2dWloPl.d.mts → context-B9oWzbwF.d.mts} +2 -2
- package/dist/{context-B2dWloPl.d.mts.map → context-B9oWzbwF.d.mts.map} +1 -1
- package/dist/data/index.d.mts +1 -1
- package/dist/data/index.mjs +1 -1
- package/dist/data-W10ldR5l.mjs +2 -0
- package/dist/data-W10ldR5l.mjs.map +1 -0
- package/dist/{dual-fN6OUwN_.mjs → dual-CZhzZslG.mjs} +1 -1
- package/dist/{dual-fN6OUwN_.mjs.map → dual-CZhzZslG.mjs.map} +1 -1
- package/dist/duration/index.d.mts +1 -1
- package/dist/duration/index.mjs +1 -1
- package/dist/{duration-CYoDHcOR.mjs → duration-Dwtjy95Z.mjs} +2 -2
- package/dist/{duration-CYoDHcOR.mjs.map → duration-Dwtjy95Z.mjs.map} +1 -1
- package/dist/either/index.d.mts +1 -1
- package/dist/either/index.mjs +1 -1
- package/dist/{either-G7uOu4Ar.mjs → either-B2TvVY_j.mjs} +2 -2
- package/dist/{either-G7uOu4Ar.mjs.map → either-B2TvVY_j.mjs.map} +1 -1
- package/dist/exit-DOdhmr81.d.mts +67 -0
- package/dist/exit-DOdhmr81.d.mts.map +1 -0
- package/dist/fiber/index.d.mts +2 -0
- package/dist/fiber/index.mjs +1 -0
- package/dist/fiber-CZsyrDdd.mjs +2 -0
- package/dist/fiber-CZsyrDdd.mjs.map +1 -0
- package/dist/{flow-CNyLsPGb.mjs → flow-D8_tllWl.mjs} +1 -1
- package/dist/{flow-CNyLsPGb.mjs.map → flow-D8_tllWl.mjs.map} +1 -1
- package/dist/functions/index.mjs +1 -1
- package/dist/functions-DmOZ7O4j.mjs +2 -0
- package/dist/{functions-ByAk682_.mjs.map → functions-DmOZ7O4j.mjs.map} +1 -1
- package/dist/fx/index.d.mts +1 -1
- package/dist/fx/index.mjs +1 -1
- package/dist/fx-DXBw4iYX.mjs +2 -0
- package/dist/fx-DXBw4iYX.mjs.map +1 -0
- package/dist/fx.runtime-B2_rL7h_.mjs +2 -0
- package/dist/fx.runtime-B2_rL7h_.mjs.map +1 -0
- package/dist/fx.runtime-BuIElLpZ.d.mts +16 -0
- package/dist/fx.runtime-BuIElLpZ.d.mts.map +1 -0
- package/dist/{fx.types-DyQVgTS8.mjs → fx.types-Bg-Mmdm5.mjs} +1 -1
- package/dist/{fx.types-DyQVgTS8.mjs.map → fx.types-Bg-Mmdm5.mjs.map} +1 -1
- package/dist/{fx.types-BdN1EWxr.d.mts → fx.types-CpFKa-Jj.d.mts} +1 -1
- package/dist/{fx.types-BdN1EWxr.d.mts.map → fx.types-CpFKa-Jj.d.mts.map} +1 -1
- package/dist/{index-DCUGtEcj.d.mts → index-5QkUtJ-4.d.mts} +4 -4
- package/dist/{index-DCUGtEcj.d.mts.map → index-5QkUtJ-4.d.mts.map} +1 -1
- package/dist/{index-C4DOLLaU.d.mts → index-B3xia3Jl.d.mts} +82 -58
- package/dist/index-B3xia3Jl.d.mts.map +1 -0
- package/dist/{index-CIvNgjsx.d.mts → index-B4rHoUK4.d.mts} +2 -2
- package/dist/{index-CIvNgjsx.d.mts.map → index-B4rHoUK4.d.mts.map} +1 -1
- package/dist/{index-BA0EsFxS.d.mts → index-BDUhDs4D.d.mts} +3 -3
- package/dist/{index-BA0EsFxS.d.mts.map → index-BDUhDs4D.d.mts.map} +1 -1
- package/dist/{index-CNTYbcY9.d.mts → index-BZ1-IrU_.d.mts} +1 -1
- package/dist/{index-CNTYbcY9.d.mts.map → index-BZ1-IrU_.d.mts.map} +1 -1
- package/dist/{index-uE3S3Krx.d.mts → index-BZP6t2h9.d.mts} +5 -5
- package/dist/{index-uE3S3Krx.d.mts.map → index-BZP6t2h9.d.mts.map} +1 -1
- package/dist/{index-D8rDE60Y.d.mts → index-Bu-z5Xoq.d.mts} +1 -1
- package/dist/index-Bu-z5Xoq.d.mts.map +1 -0
- package/dist/index-C8KMi_I9.d.mts +226 -0
- package/dist/index-C8KMi_I9.d.mts.map +1 -0
- package/dist/{index-dCRymj_g.d.mts → index-CfXGmPMY.d.mts} +5 -5
- package/dist/{index-dCRymj_g.d.mts.map → index-CfXGmPMY.d.mts.map} +1 -1
- package/dist/index-Cv48HmyO.d.mts +59 -0
- package/dist/index-Cv48HmyO.d.mts.map +1 -0
- package/dist/{index-B2Z7-XGR.d.mts → index-D-KxgnwF.d.mts} +59 -34
- package/dist/index-D-KxgnwF.d.mts.map +1 -0
- package/dist/{index-DR7hzXU4.d.mts → index-DLkMqvw4.d.mts} +137 -29
- package/dist/index-DLkMqvw4.d.mts.map +1 -0
- package/dist/index-DlWm_PwP.d.mts +436 -0
- package/dist/index-DlWm_PwP.d.mts.map +1 -0
- package/dist/{index-B0flvtFB.d.mts → index-DogEz6WQ.d.mts} +2 -2
- package/dist/{index-B0flvtFB.d.mts.map → index-DogEz6WQ.d.mts.map} +1 -1
- package/dist/{index-crtzMG48.d.mts → index-XxPUUAGQ.d.mts} +41 -6
- package/dist/index-XxPUUAGQ.d.mts.map +1 -0
- package/dist/{index-BqJ1GWAF.d.mts → index-pC80zLHb.d.mts} +2 -2
- package/dist/{index-BqJ1GWAF.d.mts.map → index-pC80zLHb.d.mts.map} +1 -1
- package/dist/index.d.mts +23 -20
- package/dist/index.mjs +1 -1
- package/dist/layer/index.d.mts +1 -1
- package/dist/layer/index.mjs +1 -1
- package/dist/layer-BmrPWBkT.mjs +2 -0
- package/dist/layer-BmrPWBkT.mjs.map +1 -0
- package/dist/{layer.types-BB0MrvLg.d.mts → layer.types-DsCTjICW.d.mts} +4 -4
- package/dist/{layer.types-BB0MrvLg.d.mts.map → layer.types-DsCTjICW.d.mts.map} +1 -1
- package/dist/log/index.d.mts +2 -0
- package/dist/log/index.mjs +1 -0
- package/dist/log-Bh8G5umo.mjs +2 -0
- package/dist/log-Bh8G5umo.mjs.map +1 -0
- package/dist/multithread/index.d.mts +1 -1
- package/dist/multithread/index.mjs +1 -1
- package/dist/multithread-CovZ2ioL.mjs +21 -0
- package/dist/multithread-CovZ2ioL.mjs.map +1 -0
- package/dist/option/index.d.mts +1 -1
- package/dist/option/index.mjs +1 -1
- package/dist/{option-C2iCxAuJ.mjs → option-BlyP5LA2.mjs} +2 -2
- package/dist/{option-C2iCxAuJ.mjs.map → option-BlyP5LA2.mjs.map} +1 -1
- package/dist/{option.types-D9hrKcfa.d.mts → option.types-DLp3QpFE.d.mts} +3 -3
- package/dist/{option.types-D9hrKcfa.d.mts.map → option.types-DLp3QpFE.d.mts.map} +1 -1
- package/dist/{option.types-CbY_swma.mjs → option.types-bFFSErJ-.mjs} +1 -1
- package/dist/{option.types-CbY_swma.mjs.map → option.types-bFFSErJ-.mjs.map} +1 -1
- package/dist/order/index.d.mts +1 -1
- package/dist/order/index.mjs +1 -1
- package/dist/{order-BXOBEKvB.mjs → order-VTXpppmI.mjs} +2 -2
- package/dist/{order-BXOBEKvB.mjs.map → order-VTXpppmI.mjs.map} +1 -1
- package/dist/{pipeable-BIrevC0D.d.mts → pipeable-BY9yPsNK.d.mts} +1 -1
- package/dist/{pipeable-BIrevC0D.d.mts.map → pipeable-BY9yPsNK.d.mts.map} +1 -1
- package/dist/pipeable-COGyGMUV.mjs +2 -0
- package/dist/{pipeable-Dp1_23zH.mjs.map → pipeable-COGyGMUV.mjs.map} +1 -1
- package/dist/predicate/index.d.mts +1 -1
- package/dist/predicate/index.mjs +1 -1
- package/dist/{predicate-D_1SsIi4.mjs → predicate-8hY-0Ocv.mjs} +2 -2
- package/dist/{predicate-D_1SsIi4.mjs.map → predicate-8hY-0Ocv.mjs.map} +1 -1
- package/dist/provide/index.d.mts +1 -1
- package/dist/provide/index.mjs +1 -1
- package/dist/provide-K-6oXtLm.mjs +2 -0
- package/dist/provide-K-6oXtLm.mjs.map +1 -0
- package/dist/queue/index.d.mts +1 -1
- package/dist/queue/index.mjs +1 -1
- package/dist/{queue-apiEOlRD.mjs → queue-CeEIUHcY.mjs} +2 -2
- package/dist/{queue-apiEOlRD.mjs.map → queue-CeEIUHcY.mjs.map} +1 -1
- package/dist/result/index.d.mts +1 -1
- package/dist/result/index.mjs +1 -1
- package/dist/{result-D3VY0qBG.mjs → result-C74pRN2x.mjs} +2 -2
- package/dist/{result-D3VY0qBG.mjs.map → result-C74pRN2x.mjs.map} +1 -1
- package/dist/{result.types-BKzChyWY.d.mts → result.types-CnhiVFEV.d.mts} +3 -3
- package/dist/{result.types-BKzChyWY.d.mts.map → result.types-CnhiVFEV.d.mts.map} +1 -1
- package/dist/schedule/index.d.mts +1 -1
- package/dist/schedule/index.mjs +1 -1
- package/dist/{schedule-C6iN3oMt.mjs → schedule-ChcIgvd5.mjs} +2 -2
- package/dist/{schedule-C6iN3oMt.mjs.map → schedule-ChcIgvd5.mjs.map} +1 -1
- package/dist/{schedule-D2651VJY.d.mts → schedule-DiidMLcl.d.mts} +3 -3
- package/dist/{schedule-D2651VJY.d.mts.map → schedule-DiidMLcl.d.mts.map} +1 -1
- package/dist/schema/index.d.mts +1 -1
- package/dist/schema/index.mjs +1 -1
- package/dist/schema-CT_wO7tN.mjs +2 -0
- package/dist/schema-CT_wO7tN.mjs.map +1 -0
- package/dist/scope/index.d.mts +1 -1
- package/dist/scope/index.mjs +1 -1
- package/dist/{scope-CuM3CzwG.d.mts → scope-7bLTmdRX.d.mts} +4 -4
- package/dist/scope-7bLTmdRX.d.mts.map +1 -0
- package/dist/scope-D2AqJy7j.mjs +2 -0
- package/dist/scope-D2AqJy7j.mjs.map +1 -0
- package/dist/service/index.d.mts +1 -1
- package/dist/service/index.mjs +1 -1
- package/dist/{service-D8mr0wwg.d.mts → service-C4xUfS_M.d.mts} +2 -2
- package/dist/{service-D8mr0wwg.d.mts.map → service-C4xUfS_M.d.mts.map} +1 -1
- package/dist/{service-CWAIEH46.mjs → service-DHkeorS3.mjs} +2 -2
- package/dist/{service-CWAIEH46.mjs.map → service-DHkeorS3.mjs.map} +1 -1
- package/dist/trace/index.d.mts +2 -0
- package/dist/trace/index.mjs +1 -0
- package/dist/trace-ByjppUes.mjs +2 -0
- package/dist/trace-ByjppUes.mjs.map +1 -0
- package/dist/trace-D_7sjH22.d.mts +375 -0
- package/dist/trace-D_7sjH22.d.mts.map +1 -0
- package/package.json +13 -5
- package/dist/adt/index.d.mts +0 -2
- package/dist/adt/index.mjs +0 -1
- package/dist/adt-CPG_sa8q.mjs +0 -2
- package/dist/adt-CPG_sa8q.mjs.map +0 -1
- package/dist/data-BHYPdqWZ.mjs +0 -2
- package/dist/data-BHYPdqWZ.mjs.map +0 -1
- package/dist/discriminator.types-C-ygT2S1.d.mts +0 -7
- package/dist/discriminator.types-C-ygT2S1.d.mts.map +0 -1
- package/dist/equality-BX6BUidG.mjs +0 -2
- package/dist/equality-BX6BUidG.mjs.map +0 -1
- package/dist/functions-ByAk682_.mjs +0 -2
- package/dist/fx-DUXDxwsU.mjs +0 -2
- package/dist/fx-DUXDxwsU.mjs.map +0 -1
- package/dist/fx.runtime-jQxh77s3.mjs +0 -2
- package/dist/fx.runtime-jQxh77s3.mjs.map +0 -1
- package/dist/index-B2Z7-XGR.d.mts.map +0 -1
- package/dist/index-B41_sFR6.d.mts +0 -64
- package/dist/index-B41_sFR6.d.mts.map +0 -1
- package/dist/index-C4DOLLaU.d.mts.map +0 -1
- package/dist/index-C6W3_n_Q.d.mts +0 -458
- package/dist/index-C6W3_n_Q.d.mts.map +0 -1
- package/dist/index-D8rDE60Y.d.mts.map +0 -1
- package/dist/index-DR7hzXU4.d.mts.map +0 -1
- package/dist/index-crtzMG48.d.mts.map +0 -1
- package/dist/layer-CKtH7TRL.mjs +0 -2
- package/dist/layer-CKtH7TRL.mjs.map +0 -1
- package/dist/multithread-Cyc8Bz45.mjs +0 -19
- package/dist/multithread-Cyc8Bz45.mjs.map +0 -1
- package/dist/pipeable-Dp1_23zH.mjs +0 -2
- package/dist/provide--yZE8x-n.mjs +0 -2
- package/dist/provide--yZE8x-n.mjs.map +0 -1
- package/dist/schema-DstB1_VK.mjs +0 -2
- package/dist/schema-DstB1_VK.mjs.map +0 -1
- package/dist/schema.shared-Bjyroa6b.mjs +0 -2
- package/dist/schema.shared-Bjyroa6b.mjs.map +0 -1
- package/dist/schema.types-E1pjcc0Y.d.mts +0 -62
- package/dist/schema.types-E1pjcc0Y.d.mts.map +0 -1
- package/dist/scope-CuM3CzwG.d.mts.map +0 -1
- package/dist/scope-gVt4PESc.mjs +0 -2
- package/dist/scope-gVt4PESc.mjs.map +0 -1
- package/dist/service-resolution-BefYr4nR.mjs +0 -2
- package/dist/service-resolution-BefYr4nR.mjs.map +0 -1
- /package/dist/{chunk-oQKkju2G.mjs → chunk-6rpU2rUb.mjs} +0 -0
- /package/dist/{option-CXXiA1w-.mjs → option-BqAUkJ8e.mjs} +0 -0
- /package/dist/{result-xFLfwriM.mjs → result-B5WbPg8C.mjs} +0 -0
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ Install optional extras used by some modules and examples:
|
|
|
20
20
|
pnpm add @nicolastoulemont/std zod multithreading
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
- `zod` is optional. The
|
|
23
|
+
- `zod` is optional. The schema-backed tagged union examples below use it, but `Schema` accepts any Standard Schema-compatible validator, including `zod`, `valibot`, `arktype`, and similar libraries.
|
|
24
24
|
- `multithreading` is optional. It is only required if you want to use the `Multithread` module.
|
|
25
25
|
|
|
26
26
|
## Quick Start
|
|
@@ -256,13 +256,16 @@ const canSearch = isSearchInput({ q: "books", limit: 20 })
|
|
|
256
256
|
|
|
257
257
|
### Schema
|
|
258
258
|
|
|
259
|
-
Schema wraps Standard Schema-compatible validators for
|
|
260
|
-
boundary parsing
|
|
259
|
+
Schema wraps Standard Schema-compatible validators for three production use cases:
|
|
260
|
+
boundary parsing, sync-only refinement, and bidirectional codecs.
|
|
261
261
|
|
|
262
262
|
Use `Schema.parse` at I/O boundaries when a broad external type hides smaller implicit subtypes.
|
|
263
263
|
Use `Schema.is` for direct in-memory proof checks.
|
|
264
264
|
Use `Schema.refine` when you need a reusable preserved-shape predicate, especially with higher-order APIs like `Array.filter`.
|
|
265
|
+
Use `Schema.codec` when you need an explicit bidirectional boundary adapter with validated encoded and decoded sides.
|
|
266
|
+
Use `Schema.codec.json` for JSON string encoding/decoding with explicit `JSON.stringify` options.
|
|
265
267
|
Use `Schema.Refine<Base, typeof schema>` for a reusable preserved-shape narrowed type, and `Schema.Infer<typeof schema>` for the exact schema output type.
|
|
268
|
+
Use `Schema.struct`, `Schema.tagged`, and `Schema.union` when you want schema-backed constructors that validate before returning typed data.
|
|
266
269
|
Only use `Schema.is` and `Schema.refine` with sync proof schemas that validate the current value in place. Transforms, defaults, and coercions should continue to use `Schema.parse`.
|
|
267
270
|
|
|
268
271
|
#### Boundary Parsing Example
|
|
@@ -376,26 +379,110 @@ const chatTickets = tickets.filter(isChatTicket)
|
|
|
376
379
|
|
|
377
380
|
Use `Schema.Infer<typeof schema>` when you need the exact schema output, and use `Schema.parse` whenever the schema changes the output shape.
|
|
378
381
|
|
|
379
|
-
|
|
382
|
+
#### Bidirectional Codec Example
|
|
380
383
|
|
|
381
|
-
|
|
382
|
-
|
|
384
|
+
```ts
|
|
385
|
+
import { Result, Schema } from "@nicolastoulemont/std"
|
|
386
|
+
import { z } from "zod"
|
|
383
387
|
|
|
384
|
-
|
|
388
|
+
const Port = z.number().int().min(1).max(65535)
|
|
389
|
+
|
|
390
|
+
const PortString = Schema.codec({
|
|
391
|
+
encoded: z.string(),
|
|
392
|
+
decoded: Port,
|
|
393
|
+
decode: (encoded) => Number(encoded),
|
|
394
|
+
encode: (decoded) => String(decoded),
|
|
395
|
+
})
|
|
396
|
+
|
|
397
|
+
const decoded = PortString.decode("3000")
|
|
398
|
+
const encoded = PortString.encode(3000)
|
|
399
|
+
|
|
400
|
+
if (Result.isOk(decoded)) {
|
|
401
|
+
decoded.value
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (Result.isOk(encoded)) {
|
|
405
|
+
encoded.value
|
|
406
|
+
}
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
`Schema.codec` validates both sides:
|
|
410
|
+
|
|
411
|
+
```txt
|
|
412
|
+
decode: encoded input -> encoded validation -> decode transform -> decoded validation -> decoded output
|
|
413
|
+
encode: decoded value -> decoded validation -> encode transform -> encoded validation -> encoded output
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
Codec errors are tagged errors under `Schema.Codec`:
|
|
417
|
+
|
|
418
|
+
```ts
|
|
419
|
+
type CodecError = Schema.Codec.Error
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
#### JSON Codec Example
|
|
423
|
+
|
|
424
|
+
```ts
|
|
425
|
+
import { Result, Schema } from "@nicolastoulemont/std"
|
|
426
|
+
import { z } from "zod"
|
|
427
|
+
|
|
428
|
+
const User = z.object({
|
|
429
|
+
id: z.string(),
|
|
430
|
+
name: z.string(),
|
|
431
|
+
})
|
|
432
|
+
|
|
433
|
+
const UserJson = Schema.codec.json(User, { space: 2 })
|
|
434
|
+
|
|
435
|
+
const decoded = UserJson.decode('{"id":"u1","name":"Alice"}')
|
|
436
|
+
const encoded = UserJson.encode({ id: "u1", name: "Alice" })
|
|
437
|
+
|
|
438
|
+
if (Result.isOk(decoded)) {
|
|
439
|
+
decoded.value.name
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
if (Result.isOk(encoded)) {
|
|
443
|
+
encoded.value
|
|
444
|
+
}
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
#### Schema-Backed Struct Example
|
|
448
|
+
|
|
449
|
+
```ts
|
|
450
|
+
import { Schema } from "@nicolastoulemont/std"
|
|
451
|
+
import { z } from "zod"
|
|
452
|
+
|
|
453
|
+
const Folder = Schema.struct(
|
|
454
|
+
z.object({
|
|
455
|
+
id: z.string(),
|
|
456
|
+
name: z.string(),
|
|
457
|
+
archived: z.boolean().default(false),
|
|
458
|
+
}),
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
const created = Folder({ id: "folder_1", name: "Inbox" })
|
|
462
|
+
|
|
463
|
+
if (created._tag === "Ok") {
|
|
464
|
+
created.value.equals({ id: "folder_1", name: "Inbox", archived: false })
|
|
465
|
+
}
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
#### Schema-Backed Tagged Union Example
|
|
469
|
+
|
|
470
|
+
Schema builds tagged unions backed by any Standard Schema-compatible validator.
|
|
471
|
+
The examples below use `zod`, but the same API works with `valibot`, `arktype`, and other libraries that implement the Standard Schema contract.
|
|
385
472
|
|
|
386
473
|
```ts
|
|
387
|
-
import {
|
|
474
|
+
import { Data, Schema } from "@nicolastoulemont/std"
|
|
388
475
|
import { z } from "zod"
|
|
389
476
|
|
|
390
|
-
const Shape =
|
|
477
|
+
const Shape = Schema.union("Shape", {
|
|
391
478
|
Circle: z.object({ radius: z.number() }),
|
|
392
479
|
Square: z.object({ side: z.number() }),
|
|
393
480
|
})
|
|
394
481
|
|
|
395
|
-
type Shape =
|
|
482
|
+
type Shape = Schema.Union.Infer<typeof Shape>
|
|
396
483
|
|
|
397
484
|
const describeShape = (shape: Shape) =>
|
|
398
|
-
|
|
485
|
+
Data.match(shape, {
|
|
399
486
|
Circle: (value) => `circle(${value.radius})`,
|
|
400
487
|
Square: (value) => `square(${value.side})`,
|
|
401
488
|
})
|
|
@@ -404,19 +491,19 @@ const describeShape = (shape: Shape) =>
|
|
|
404
491
|
#### Real-World Example
|
|
405
492
|
|
|
406
493
|
```ts
|
|
407
|
-
import {
|
|
494
|
+
import { Data, Schema } from "@nicolastoulemont/std"
|
|
408
495
|
import { z } from "zod"
|
|
409
496
|
|
|
410
|
-
const OrderState =
|
|
497
|
+
const OrderState = Schema.union("OrderState", {
|
|
411
498
|
Draft: z.object({ id: z.string() }),
|
|
412
499
|
Confirmed: z.object({ id: z.string(), paymentId: z.string() }),
|
|
413
500
|
Shipped: z.object({ id: z.string(), trackingId: z.string() }),
|
|
414
501
|
})
|
|
415
502
|
|
|
416
|
-
type OrderState =
|
|
503
|
+
type OrderState = Schema.Union.Infer<typeof OrderState>
|
|
417
504
|
|
|
418
505
|
const badgeLabel = (state: OrderState) =>
|
|
419
|
-
|
|
506
|
+
Data.match(state, {
|
|
420
507
|
Draft: () => "Waiting for payment",
|
|
421
508
|
Confirmed: () => "Preparing shipment",
|
|
422
509
|
Shipped: (value) => `Shipped: ${value.trackingId}`,
|
|
@@ -426,7 +513,7 @@ const badgeLabel = (state: OrderState) =>
|
|
|
426
513
|
### Data
|
|
427
514
|
|
|
428
515
|
Data creates immutable structural value objects with stable equality and hashing semantics.
|
|
429
|
-
Use it when you want value semantics for tuples, arrays, tagged records,
|
|
516
|
+
Use it when you want value semantics for structs, tuples, arrays, tagged records, custom error types, or pattern matching over tagged data.
|
|
430
517
|
|
|
431
518
|
#### Abstract Example
|
|
432
519
|
|
|
@@ -439,6 +526,8 @@ const b = Data.struct({ env: "prod", retries: 3 })
|
|
|
439
526
|
const same = a.equals(b) // true
|
|
440
527
|
```
|
|
441
528
|
|
|
529
|
+
`Schema.struct` is the validated counterpart to `Data.struct`: it validates through a sync Standard Schema, then wraps the validated object as a structural value.
|
|
530
|
+
|
|
442
531
|
#### Real-World Example
|
|
443
532
|
|
|
444
533
|
```ts
|
|
@@ -648,6 +737,8 @@ const readPort = Fx.gen(function* () {
|
|
|
648
737
|
const exit = Fx.run(Provide.service(Port, 3000)(readPort))
|
|
649
738
|
```
|
|
650
739
|
|
|
740
|
+
Use `Provide.layers(...)` when you want the ergonomics of `Provide.layer(Layer.merge(...))` without the extra nesting.
|
|
741
|
+
|
|
651
742
|
#### Real-World Example
|
|
652
743
|
|
|
653
744
|
```ts
|
|
@@ -711,7 +802,7 @@ const ApiLive = Layer.ok(Api, {
|
|
|
711
802
|
|
|
712
803
|
class InvalidQuantityError extends Data.TaggedError("InvalidQuantityError")<{ qty: number }> {}
|
|
713
804
|
|
|
714
|
-
const submitOrder = Fx.gen(function* (payload: { sku?: string; qty: number }) {
|
|
805
|
+
const submitOrder = Fx.gen(async function* (payload: { sku?: string; qty: number }) {
|
|
715
806
|
const api = yield* Api
|
|
716
807
|
const sku = yield* Fx.option(payload.sku)
|
|
717
808
|
const validQty = yield* Result.filter(
|
|
@@ -719,10 +810,161 @@ const submitOrder = Fx.gen(function* (payload: { sku?: string; qty: number }) {
|
|
|
719
810
|
(qty) => qty > 0,
|
|
720
811
|
(qty) => new InvalidQuantityError({ qty }),
|
|
721
812
|
)
|
|
722
|
-
|
|
813
|
+
const created = await Fx.try(() => api.postOrder({ sku, qty: validQty }))
|
|
814
|
+
return yield* created
|
|
815
|
+
})
|
|
816
|
+
|
|
817
|
+
const exit = await Fx.run(pipe(submitOrder({ sku: "book-1", qty: 2 }), Provide.layer(ApiLive)))
|
|
818
|
+
```
|
|
819
|
+
|
|
820
|
+
### Fiber
|
|
821
|
+
|
|
822
|
+
Fiber exposes handles for running `Fx` computations.
|
|
823
|
+
Use `Fx.runFork` to start a root fiber outside a program, and use `Fx.forkChild` or `Fx.forkDetach` inside `Fx.gen`.
|
|
824
|
+
|
|
825
|
+
Child fibers are owned by the current fiber and are interrupted when the parent exits.
|
|
826
|
+
Detached fibers snapshot the current runtime state but are not parent-owned.
|
|
827
|
+
Interruption is cooperative: long-running programs should yield with `Fx.yieldNow()` or wait on interruptible runtime operations.
|
|
828
|
+
|
|
829
|
+
#### Abstract Example
|
|
830
|
+
|
|
831
|
+
```ts
|
|
832
|
+
import { Fx } from "@nicolastoulemont/std"
|
|
833
|
+
|
|
834
|
+
const fiber = Fx.runFork(
|
|
835
|
+
Fx.gen(async function* () {
|
|
836
|
+
yield* Fx.yieldNow()
|
|
837
|
+
return 42
|
|
838
|
+
}),
|
|
839
|
+
)
|
|
840
|
+
|
|
841
|
+
const exit = await fiber.await()
|
|
842
|
+
// => { _tag: "Ok", value: 42 }
|
|
843
|
+
```
|
|
844
|
+
|
|
845
|
+
#### Real-World Example
|
|
846
|
+
|
|
847
|
+
```ts
|
|
848
|
+
import { Fiber, Fx } from "@nicolastoulemont/std"
|
|
849
|
+
|
|
850
|
+
const program = Fx.gen(async function* () {
|
|
851
|
+
const child = yield* Fx.forkChild(
|
|
852
|
+
Fx.gen(async function* () {
|
|
853
|
+
yield* Fx.yieldNow()
|
|
854
|
+
return "child-ready"
|
|
855
|
+
}),
|
|
856
|
+
)
|
|
857
|
+
|
|
858
|
+
const value = yield* Fiber.join(child)
|
|
859
|
+
const statusAfterJoin = yield* Fiber.status(child)
|
|
860
|
+
|
|
861
|
+
return { value, statusAfterJoin }
|
|
723
862
|
})
|
|
724
863
|
|
|
725
|
-
const exit = Fx.run(
|
|
864
|
+
const exit = await Fx.run(program)
|
|
865
|
+
// => { _tag: "Ok", value: { value: "child-ready", statusAfterJoin: "Done" } }
|
|
866
|
+
```
|
|
867
|
+
|
|
868
|
+
### Log
|
|
869
|
+
|
|
870
|
+
Log stores contextual fields in the `Fx` runtime state and sends log events to installed logger backends.
|
|
871
|
+
Log events include merged fields, active log spans, trace metadata when a span exists, and the current fiber id.
|
|
872
|
+
Logger failures are ignored so logging cannot fail the user program.
|
|
873
|
+
|
|
874
|
+
#### Abstract Example
|
|
875
|
+
|
|
876
|
+
```ts
|
|
877
|
+
import { Fx, Log, Provide } from "@nicolastoulemont/std"
|
|
878
|
+
|
|
879
|
+
const events: Log.Event[] = []
|
|
880
|
+
|
|
881
|
+
const program = Provide.layer(Log.layer({ log: (event) => events.push(event) }))(
|
|
882
|
+
Fx.gen(function* () {
|
|
883
|
+
const logger = yield* Log.context({ requestId: "req_1" })
|
|
884
|
+
yield* logger.info("request received", { route: "/orders" })
|
|
885
|
+
return "ok"
|
|
886
|
+
}),
|
|
887
|
+
)
|
|
888
|
+
|
|
889
|
+
const exit = Fx.run(program)
|
|
890
|
+
// => { _tag: "Ok", value: "ok" }
|
|
891
|
+
```
|
|
892
|
+
|
|
893
|
+
#### Real-World Example
|
|
894
|
+
|
|
895
|
+
```ts
|
|
896
|
+
import { Fx, Log, Provide } from "@nicolastoulemont/std"
|
|
897
|
+
|
|
898
|
+
const logger = Log.json()
|
|
899
|
+
|
|
900
|
+
const program = Provide.layer(Log.layer(logger))(
|
|
901
|
+
Log.withFields({ service: "checkout" })(
|
|
902
|
+
Fx.gen(function* () {
|
|
903
|
+
const log = yield* Log.context({ requestId: "req_42" }, { logSpan: "checkout" })
|
|
904
|
+
yield* log.info("charging card", { amountCents: 4600 })
|
|
905
|
+
return "paid"
|
|
906
|
+
}),
|
|
907
|
+
),
|
|
908
|
+
)
|
|
909
|
+
|
|
910
|
+
const exit = Fx.run(program)
|
|
911
|
+
// => { _tag: "Ok", value: "paid" }
|
|
912
|
+
```
|
|
913
|
+
|
|
914
|
+
Use `Log.withSpan` for log timing decoration. Use `Trace.span` when you need a real tracing span.
|
|
915
|
+
|
|
916
|
+
### Trace
|
|
917
|
+
|
|
918
|
+
Trace stores span context in the `Fx` runtime state.
|
|
919
|
+
Use `Trace.span` to wrap an effect in a tracing span, `Trace.annotate` or `Trace.attribute` to attach attributes, and `Trace.event` to add events.
|
|
920
|
+
`Trace.layer` installs a tracer backend; `Trace.native()` provides a lightweight in-memory-compatible tracer implementation for local use and tests.
|
|
921
|
+
|
|
922
|
+
#### Abstract Example
|
|
923
|
+
|
|
924
|
+
```ts
|
|
925
|
+
import { Fx, Trace } from "@nicolastoulemont/std"
|
|
926
|
+
|
|
927
|
+
const program = Trace.span(
|
|
928
|
+
"checkout",
|
|
929
|
+
Fx.gen(function* () {
|
|
930
|
+
yield* Trace.attribute("order.id", "ord_42")
|
|
931
|
+
yield* Trace.event("charged")
|
|
932
|
+
const context = yield* Trace.currentContext()
|
|
933
|
+
return context === undefined ? "missing" : "traced"
|
|
934
|
+
}),
|
|
935
|
+
)
|
|
936
|
+
|
|
937
|
+
const exit = Fx.run(program)
|
|
938
|
+
// => { _tag: "Ok", value: "traced" }
|
|
939
|
+
```
|
|
940
|
+
|
|
941
|
+
#### Real-World Example
|
|
942
|
+
|
|
943
|
+
```ts
|
|
944
|
+
import { Fx, Log, Provide, Trace } from "@nicolastoulemont/std"
|
|
945
|
+
|
|
946
|
+
const logger = Log.console()
|
|
947
|
+
const tracer = Trace.native()
|
|
948
|
+
|
|
949
|
+
const program = Provide.layers(
|
|
950
|
+
Log.layer(logger),
|
|
951
|
+
Trace.layer(tracer),
|
|
952
|
+
)(
|
|
953
|
+
Trace.span(
|
|
954
|
+
"POST /orders",
|
|
955
|
+
Fx.gen(function* () {
|
|
956
|
+
const log = yield* Log.context({ requestId: "req_42" })
|
|
957
|
+
yield* Trace.annotate({ "http.method": "POST", "http.route": "/orders" })
|
|
958
|
+
yield* log.info("request started")
|
|
959
|
+
yield* Trace.event("order.created", { orderId: "ord_42" })
|
|
960
|
+
return "ord_42"
|
|
961
|
+
}),
|
|
962
|
+
{ kind: "server" },
|
|
963
|
+
),
|
|
964
|
+
)
|
|
965
|
+
|
|
966
|
+
const exit = Fx.run(program)
|
|
967
|
+
// => { _tag: "Ok", value: "ord_42" }
|
|
726
968
|
```
|
|
727
969
|
|
|
728
970
|
### Duration
|
|
@@ -898,7 +1140,98 @@ await imageQueue.shutdown({ mode: "drain" })
|
|
|
898
1140
|
Multithread runs self-contained callbacks in worker threads using a Result-first API while remaining yieldable in `Fx.gen`.
|
|
899
1141
|
It requires the optional `multithreading` dependency at runtime.
|
|
900
1142
|
|
|
901
|
-
|
|
1143
|
+
Use it for independent CPU-heavy work that should not block the main thread, such as parsing large payloads, validating batches, compression, or expensive transforms.
|
|
1144
|
+
Callbacks are serialized into workers, so they must be self-contained: define helper functions inside the callback or pass data as arguments.
|
|
1145
|
+
|
|
1146
|
+
#### Lifecycle
|
|
1147
|
+
|
|
1148
|
+
Configure the worker pool before the first multithread operation starts:
|
|
1149
|
+
|
|
1150
|
+
```ts
|
|
1151
|
+
import { Multithread } from "@nicolastoulemont/std"
|
|
1152
|
+
|
|
1153
|
+
const configured = Multithread.configure({ maxWorkers: 4 })
|
|
1154
|
+
// => { _tag: "Ok", value: undefined }
|
|
1155
|
+
```
|
|
1156
|
+
|
|
1157
|
+
`maxWorkers` is the maximum number of worker threads in the shared runtime pool.
|
|
1158
|
+
Per-operation `parallelism` controls how many jobs an operation tries to keep in flight, but the runtime can only execute up to `maxWorkers` worker jobs at the same time.
|
|
1159
|
+
|
|
1160
|
+
For `Fx` programs, prefer the scoped layer so the worker runtime is shut down with the scope:
|
|
1161
|
+
|
|
1162
|
+
```ts
|
|
1163
|
+
import { Fx, Multithread, Provide } from "@nicolastoulemont/std"
|
|
1164
|
+
|
|
1165
|
+
const program = Provide.layer(Multithread.layer({ maxWorkers: 4 }))(
|
|
1166
|
+
Fx.gen(async function* () {
|
|
1167
|
+
return yield* Multithread.fx(() => 42)
|
|
1168
|
+
}),
|
|
1169
|
+
)
|
|
1170
|
+
|
|
1171
|
+
const exit = await Fx.run(program)
|
|
1172
|
+
// => { _tag: "Ok", value: 42 }
|
|
1173
|
+
```
|
|
1174
|
+
|
|
1175
|
+
#### Inside `Fx.gen`
|
|
1176
|
+
|
|
1177
|
+
Use `Multithread.fx` inside reusable `Fx.gen` programs. It creates a fresh worker operation for each `Fx` execution, so separate runs do not accidentally share cancellation or memoized state.
|
|
1178
|
+
|
|
1179
|
+
```ts
|
|
1180
|
+
import { Fx, Multithread, Provide } from "@nicolastoulemont/std"
|
|
1181
|
+
|
|
1182
|
+
type PurchaseEvent = {
|
|
1183
|
+
readonly id: string
|
|
1184
|
+
readonly accountId: string
|
|
1185
|
+
readonly cents: number
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
const ndjsonLines = [
|
|
1189
|
+
'{"id":"evt_1","accountId":"acct_1","cents":1200}',
|
|
1190
|
+
'{"id":"evt_2","accountId":"acct_2","cents":3400}',
|
|
1191
|
+
]
|
|
1192
|
+
|
|
1193
|
+
const program = Provide.layer(Multithread.layer({ maxWorkers: 4 }))(
|
|
1194
|
+
Fx.gen(async function* () {
|
|
1195
|
+
const events = yield* Multithread.fx((lines, ctx) => {
|
|
1196
|
+
const parsed: PurchaseEvent[] = []
|
|
1197
|
+
|
|
1198
|
+
for (const line of lines) {
|
|
1199
|
+
ctx.throwIfCancelled()
|
|
1200
|
+
|
|
1201
|
+
let value: Partial<PurchaseEvent>
|
|
1202
|
+
try {
|
|
1203
|
+
value = JSON.parse(line) as Partial<PurchaseEvent>
|
|
1204
|
+
} catch {
|
|
1205
|
+
return { _tag: "Err" as const, error: { _tag: "InvalidPurchaseEvent" as const, line } }
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
if (typeof value.id !== "string" || typeof value.accountId !== "string" || typeof value.cents !== "number") {
|
|
1209
|
+
return { _tag: "Err" as const, error: { _tag: "InvalidPurchaseEvent" as const, line } }
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
parsed.push({
|
|
1213
|
+
id: value.id,
|
|
1214
|
+
accountId: value.accountId,
|
|
1215
|
+
cents: value.cents,
|
|
1216
|
+
})
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
return parsed
|
|
1220
|
+
}, ndjsonLines)
|
|
1221
|
+
|
|
1222
|
+
return events.reduce((sum, event) => sum + event.cents, 0)
|
|
1223
|
+
}),
|
|
1224
|
+
)
|
|
1225
|
+
|
|
1226
|
+
const exit = await Fx.run(program)
|
|
1227
|
+
// => { _tag: "Ok", value: 4600 }
|
|
1228
|
+
```
|
|
1229
|
+
|
|
1230
|
+
Fiber interruption aborts the worker operation. Worker cancellation is cooperative, so long-running callbacks should call `ctx.throwIfCancelled()` inside loops.
|
|
1231
|
+
|
|
1232
|
+
#### Direct Operation API
|
|
1233
|
+
|
|
1234
|
+
Use `Multithread.run` when you need the lower-level operation handle for direct Result-first usage, manual cancellation, explicit sharing, or composition with `race` and `firstSuccess`.
|
|
902
1235
|
|
|
903
1236
|
```ts
|
|
904
1237
|
import { Multithread } from "@nicolastoulemont/std"
|
|
@@ -909,36 +1242,54 @@ const op = Multithread.run((input: string, ctx) => {
|
|
|
909
1242
|
}, "hello")
|
|
910
1243
|
|
|
911
1244
|
const result = await op.result()
|
|
1245
|
+
// => { _tag: "Ok", value: "HELLO" }
|
|
912
1246
|
```
|
|
913
1247
|
|
|
914
|
-
|
|
1248
|
+
`MultithreadOp` is cold and memoized. Once started, repeated `result()` calls observe the same worker result.
|
|
1249
|
+
If you share one operation between multiple fibers or callers, cancellation is also shared: `abort()` cancels it for all current and future waiters.
|
|
915
1250
|
|
|
916
1251
|
```ts
|
|
917
|
-
|
|
1252
|
+
const op = Multithread.run((ctx) => {
|
|
1253
|
+
while (true) {
|
|
1254
|
+
ctx.throwIfCancelled()
|
|
1255
|
+
}
|
|
1256
|
+
})
|
|
918
1257
|
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
try {
|
|
925
|
-
return JSON.parse(line) as { id: string; email: string }
|
|
926
|
-
} catch {
|
|
927
|
-
return { _tag: "Err" as const, error: { _tag: "ParseError" as const, line } }
|
|
928
|
-
}
|
|
929
|
-
},
|
|
930
|
-
{ parallelism: 4 },
|
|
931
|
-
)
|
|
1258
|
+
op.abort()
|
|
1259
|
+
|
|
1260
|
+
const result = await op.result()
|
|
1261
|
+
// => { _tag: "Err", error: { _tag: "MultithreadCancelledError", ... } }
|
|
1262
|
+
```
|
|
932
1263
|
|
|
933
|
-
|
|
1264
|
+
#### Collection Work
|
|
934
1265
|
|
|
935
|
-
|
|
936
|
-
|
|
1266
|
+
Use `map`, `forEach`, `filter`, and `flatMap` for independent collection work.
|
|
1267
|
+
Each worker receives `(value, index, ctx)`.
|
|
937
1268
|
|
|
938
|
-
|
|
1269
|
+
```ts
|
|
1270
|
+
import { Multithread } from "@nicolastoulemont/std"
|
|
1271
|
+
|
|
1272
|
+
const result = await Multithread.map(
|
|
1273
|
+
[35, 36, 37],
|
|
1274
|
+
(n, _index, ctx) => {
|
|
1275
|
+
const fib = (value: number): number => (value <= 1 ? value : fib(value - 1) + fib(value - 2))
|
|
1276
|
+
|
|
1277
|
+
ctx.throwIfCancelled()
|
|
1278
|
+
return fib(n)
|
|
1279
|
+
},
|
|
1280
|
+
{ parallelism: 3 },
|
|
1281
|
+
).result()
|
|
1282
|
+
// => { _tag: "Ok", value: [9227465, 14930352, 24157817] }
|
|
939
1283
|
```
|
|
940
1284
|
|
|
941
|
-
|
|
1285
|
+
`race` returns the first settled operation and aborts the rest.
|
|
1286
|
+
`firstSuccess` returns the first successful operation and aggregates failures if all operations fail.
|
|
1287
|
+
|
|
1288
|
+
Performance depends on worker startup, callback serialization, payload transfer, and task size. Small jobs can be slower in workers. There is an opt-in probe for local measurement:
|
|
1289
|
+
|
|
1290
|
+
```bash
|
|
1291
|
+
RUN_MULTITHREAD_PERFORMANCE=1 pnpm --filter @nicolastoulemont/std test src/multithread/tests/multithreading.performance.test.ts
|
|
1292
|
+
```
|
|
942
1293
|
|
|
943
1294
|
### pipe / flow
|
|
944
1295
|
|
package/dist/brand/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as brand_d_exports } from "../index-
|
|
1
|
+
import { t as brand_d_exports } from "../index-BDUhDs4D.mjs";
|
|
2
2
|
export { brand_d_exports as Brand };
|
package/dist/brand/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{t as e}from"../brand-
|
|
1
|
+
import{t as e}from"../brand-DP-C92GS.mjs";export{e as Brand};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{t as e}from"./chunk-
|
|
2
|
-
//# sourceMappingURL=brand-
|
|
1
|
+
import{t as e}from"./chunk-6rpU2rUb.mjs";import{i as t,t as n}from"./result-C74pRN2x.mjs";var r=e({is:()=>o,make:()=>i,refine:()=>s,unsafeMake:()=>a});const i=e=>e,a=e=>e,o=e=>t=>e(t),s=(e,r)=>i=>e(i)?t(i):n({_tag:`BrandError`,value:i,message:typeof r==`function`?r(i):r??`Brand validation failed`});export{i as n,r as t};
|
|
2
|
+
//# sourceMappingURL=brand-DP-C92GS.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"brand-
|
|
1
|
+
{"version":3,"file":"brand-DP-C92GS.mjs","names":["Result.err","Result.ok"],"sources":["../src/brand/brand.ts"],"sourcesContent":["/**\n * Nominal branding utilities for lightweight domain types.\n *\n * **Mental model**\n * - Brands let you distinguish semantically different values with the same runtime shape.\n * - `Brand.refine` validates and returns `Result`, while `Brand.make` is unchecked.\n *\n * **Common tasks**\n * - Create branded values with `Brand.make`.\n * - Add runtime validation with `Brand.refine`.\n * - Build guards with `Brand.is`.\n *\n * **Gotchas**\n * - Branding is a type-level operation and does not change runtime representation.\n * - `unsafeMake` should be reserved for trusted sources.\n *\n * **Quickstart**\n *\n * @example\n * ```ts\n * import { Brand } from \"@nicolastoulemont/std\"\n *\n * type UserId = Brand.Branded<string, \"UserId\">\n * const raw = \"user_123\" as UserId\n * const userId = Brand.make<UserId>(raw)\n * // => brand-preserving cast\n * ```\n *\n * @module\n */\nimport { Result } from \"../result\"\nimport type { Result as ResultType } from \"../result/result.types\"\nimport type { BrandError as BrandErrorType, Branded as BrandedType, Unbrand, Validator } from \"./brand.types\"\n\n/**\n * Re-exported nominal brand helper type.\n *\n * @example\n * ```ts\n * import { Brand } from \"@nicolastoulemont/std\"\n *\n * type Example = typeof Brand\n * ```\n */\nexport type Branded<T, K extends string> = BrandedType<T, K>\n\n/**\n * Re-exported brand validation error type.\n *\n * @example\n * ```ts\n * import { Brand } from \"@nicolastoulemont/std\"\n *\n * type Example = typeof Brand\n * ```\n */\nexport type BrandError<T> = BrandErrorType<T>\n\n/* oxlint-disable no-unsafe-type-assertion -- Branding is a deliberate nominal cast at the type level and requires explicit assertions. */\n\n/**\n * Create a branded value without validation.\n * This is a type-level cast with zero runtime cost.\n *\n * Use this when you trust the value source (e.g., from a database)\n * or when validation happens elsewhere.\n *\n * @template B - The branded type\n * @param value - The value to brand\n * @returns The value as a branded type\n *\n * @example\n * ```ts\n * import { Brand } from \"@nicolastoulemont/std\"\n *\n * type UserId = Brand.Branded<string, \"UserId\">\n * const raw = \"user_123\" as UserId\n * const userId = Brand.make<UserId>(raw)\n * // => brand-preserving cast\n * ```\n */\nexport const make = <B extends Branded<unknown, string>>(value: Unbrand<B>): B => {\n return value as B\n}\n\n/**\n * Alias for make() - explicitly indicates no validation occurs.\n * Prefer this when readability about the lack of validation is important.\n *\n * @template B - The branded type\n * @param value - The value to brand (unchecked)\n * @returns The value as a branded type\n *\n * @example\n * ```ts\n * import { Brand } from \"@nicolastoulemont/std\"\n *\n * type UserId = Brand.Branded<string, \"UserId\">\n * const raw = \"user_123\" as UserId\n * const userId = Brand.unsafeMake<UserId>(raw)\n * // => unchecked brand cast\n * ```\n */\nexport const unsafeMake = <B extends Branded<unknown, string>>(value: Unbrand<B>): B => {\n return value as B\n}\n\n/**\n * Create a type guard with validation for a branded type.\n * Returns a refinement predicate that narrows to the branded type.\n *\n * @template T - The base type\n * @template K - The brand key (string literal)\n * @param validator - A function that validates the base value\n * @returns A type guard that returns true if validation passes\n *\n * @example\n * ```ts\n * import { Brand } from \"@nicolastoulemont/std\"\n *\n * type UserId = Brand.Branded<string, \"UserId\">\n * const isUserId = Brand.is<string, \"UserId\">((value) => value.startsWith(\"user_\"))\n * const valid = isUserId(\"user_123\")\n * // => true\n * ```\n */\nexport const is = <T, K extends string>(validator: Validator<T>): ((value: T) => value is Branded<T, K>) => {\n return (value: T): value is Branded<T, K> => validator(value)\n}\n\n/**\n * Create a validated branded value wrapped in a Result.\n * Returns `Result.ok(brandedValue)` on success, `Result.err(BrandError)` on failure.\n *\n * The returned Result is yieldable in Fx.gen computations via `yield*`.\n *\n * @template B - The branded type\n * @param validator - A function that validates the base value\n * @param errorMessage - Optional custom error message (or function)\n * @returns A function that takes a value and returns a Result\n *\n * @example\n * ```ts\n * import { Brand } from \"@nicolastoulemont/std\"\n *\n * type UserId = Brand.Branded<string, \"UserId\">\n * const toUserId = Brand.refine<UserId>((value) => value.startsWith(\"user_\"), \"Invalid user id\")\n * const parsed = toUserId(\"user_123\")\n * // => { _tag: \"Ok\", value: \"user_123\" }\n * ```\n */\nexport const refine = <B extends Branded<unknown, string>>(\n validator: Validator<Unbrand<B>>,\n errorMessage?: string | ((value: Unbrand<B>) => string),\n): ((value: Unbrand<B>) => ResultType<B, BrandError<Unbrand<B>>>) => {\n return (value: Unbrand<B>) => {\n if (!validator(value)) {\n const msg = typeof errorMessage === \"function\" ? errorMessage(value) : (errorMessage ?? \"Brand validation failed\")\n return Result.err({ _tag: \"BrandError\" as const, value, message: msg })\n }\n return Result.ok(value as B)\n }\n}\n\n/* oxlint-enable no-unsafe-type-assertion */\n"],"mappings":"uJAiFA,MAAa,EAA4C,GAChD,EAqBI,EAAkD,GACtD,EAsBI,EAA2B,GAC9B,GAAqC,EAAU,EAAM,CAwBlD,GACX,EACA,IAEQ,GACD,EAAU,EAAM,CAIdC,EAAU,EAAW,CAFnBD,EAAW,CAAE,KAAM,aAAuB,QAAO,QAD5C,OAAO,GAAiB,WAAa,EAAa,EAAM,CAAI,GAAgB,0BAClB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"brand.types-
|
|
1
|
+
{"version":3,"file":"brand.types-C_7QgCA4.d.mts","names":[],"sources":["../src/brand/brand.types.ts"],"sourcesContent":[],"mappings":";;;AAIwC;AA8BxC;cA9Bc,WA8B6B,EAAA,OAAA,MAAA;;;;AAQ3C;;;KA9BK,KA8B6D,CAAA,UAAA,MAAA,CAAA,GAAA;EAAC,UA7BvD,WAAA,CA6BuD,EA7BzC,CA6ByC;AAQnE,CAAA;AAQA;;;;;;;;;;;;;;;;;;KAxBY,+BAA+B,IAAI,MAAM;;;;;;;KAQzC,aAAa,UAAU,+BAA+B;;;;;;;KAQtD,uBAAuB;;;;;;;KAQvB;;kBAEM"}
|
package/dist/context/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as context_d_exports } from "../context-
|
|
1
|
+
import { n as context_d_exports } from "../context-B9oWzbwF.mjs";
|
|
2
2
|
export { context_d_exports as Context };
|
package/dist/context/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{t as e}from"../context-
|
|
1
|
+
import{t as e}from"../context-7oKePrBY.mjs";export{e as Context};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{t as e}from"./chunk-
|
|
2
|
-
//# sourceMappingURL=context-
|
|
1
|
+
import{t as e}from"./chunk-6rpU2rUb.mjs";var t=e({add:()=>i,empty:()=>n,get:()=>o,has:()=>s,make:()=>r,merge:()=>a,size:()=>c});const n=()=>({_tag:`Context`,_services:new Map,_Services:void 0}),r=(e,t)=>({_tag:`Context`,_services:new Map([[e.key,t]]),_Services:void 0}),i=(e,t)=>n=>({_tag:`Context`,_services:new Map([...n._services,[e.key,t]]),_Services:void 0}),a=(e,t)=>({_tag:`Context`,_services:new Map([...e._services,...t._services]),_Services:void 0});function o(e,t){if(t===void 0){let t=e;return e=>{let n=e._services.get(t.key);if(n===void 0)throw Error(`Service "${t.key}" not found in context. Available services: [${[...e._services.keys()].join(`, `)}]`);return n}}let n=e,r=t,i=n._services.get(r.key);if(i===void 0)throw Error(`Service "${r.key}" not found in context. Available services: [${[...n._services.keys()].join(`, `)}]`);return i}const s=(e,t)=>e._services.has(t.key),c=e=>e._services.size;export{a as i,n,r,t};
|
|
2
|
+
//# sourceMappingURL=context-7oKePrBY.mjs.map
|