@typed/id 1.0.0-beta.0 → 1.0.0-beta.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/README.md CHANGED
@@ -24,17 +24,17 @@
24
24
  ```ts
25
25
  import { Ids, Uuid5Namespace } from "@typed/id";
26
26
 
27
- const id = yield* Ids.cuid;
28
- const id = yield* Ids.ksuid;
29
- const id = yield* Ids.nanoId;
30
- const id = yield* Ids.ulid;
31
- const id = yield* Ids.uuid4;
32
- const id = yield* Ids.uuid5('https://effect.website', Uuid5Namespace.URL);
33
- const id = yield* Ids.uuid7;
27
+ const id = yield * Ids.cuid;
28
+ const id = yield * Ids.ksuid;
29
+ const id = yield * Ids.nanoId;
30
+ const id = yield * Ids.ulid;
31
+ const id = yield * Ids.uuid4;
32
+ const id = yield * Ids.uuid5("https://effect.website", Uuid5Namespace.URL);
33
+ const id = yield * Ids.uuid7;
34
34
 
35
35
  // Provide Ids services
36
- Effect.provide(Ids.Default)
37
- Effect.provide(Ids.Test())
36
+ Effect.provide(Ids.Default);
37
+ Effect.provide(Ids.Test());
38
38
  ```
39
39
 
40
40
  ## API reference
@@ -43,17 +43,17 @@ Effect.provide(Ids.Test())
43
43
 
44
44
  Unified service for generating all ID types. Requires `DateTimes`, `RandomValues`, `CuidState`, and `Uuid7State` (use `Ids.Default` or `Ids.Test()` to provide them).
45
45
 
46
- | Member | Type | Description |
47
- |--------|------|-------------|
48
- | `Ids.cuid` | `Effect<Cuid, never, Ids>` | Generate a Cuid. |
49
- | `Ids.ksuid` | `Effect<Ksuid, never, Ids>` | Generate a Ksuid. |
50
- | `Ids.nanoId` | `Effect<NanoId, never, Ids>` | Generate a NanoId. |
51
- | `Ids.ulid` | `Effect<Ulid, never, Ids>` | Generate a ULID. |
52
- | `Ids.uuid4` | `Effect<Uuid4, never, Ids>` | Generate a UUID v4. |
53
- | `Ids.uuid5` | `(name, namespace) => Effect<Uuid5, never, Ids>` | Generate a UUID v5 from a name and namespace. Also has `Ids.uuid5.dns`, `.url`, `.oid`, `.x500` taking a single `name`. |
54
- | `Ids.uuid7` | `Effect<Uuid7, never, Ids>` | Generate a UUID v7. |
55
- | `Ids.Default` | `Layer<Ids \| DateTimes \| RandomValues>` | Layer that provides `Ids` with default Cuid/Uuid7/DateTimes/RandomValues. |
56
- | `Ids.Test(options?)` | `Layer<Ids \| DateTimes \| RandomValues>` | Layer for tests; optional `currentTime` and `envData`. |
46
+ | Member | Type | Description |
47
+ | -------------------- | ------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------- |
48
+ | `Ids.cuid` | `Effect<Cuid, never, Ids>` | Generate a Cuid. |
49
+ | `Ids.ksuid` | `Effect<Ksuid, never, Ids>` | Generate a Ksuid. |
50
+ | `Ids.nanoId` | `Effect<NanoId, never, Ids>` | Generate a NanoId. |
51
+ | `Ids.ulid` | `Effect<Ulid, never, Ids>` | Generate a ULID. |
52
+ | `Ids.uuid4` | `Effect<Uuid4, never, Ids>` | Generate a UUID v4. |
53
+ | `Ids.uuid5` | `(name, namespace) => Effect<Uuid5, never, Ids>` | Generate a UUID v5 from a name and namespace. Also has `Ids.uuid5.dns`, `.url`, `.oid`, `.x500` taking a single `name`. |
54
+ | `Ids.uuid7` | `Effect<Uuid7, never, Ids>` | Generate a UUID v7. |
55
+ | `Ids.Default` | `Layer<Ids \| DateTimes \| RandomValues>` | Layer that provides `Ids` with default Cuid/Uuid7/DateTimes/RandomValues. |
56
+ | `Ids.Test(options?)` | `Layer<Ids \| DateTimes \| RandomValues>` | Layer for tests; optional `currentTime` and `envData`. |
57
57
 
58
58
  **TestOptions:** `{ currentTime?: number | string | Date; envData?: string }`
59
59
 
@@ -61,104 +61,104 @@ Unified service for generating all ID types. Requires `DateTimes`, `RandomValues
61
61
 
62
62
  ### Cuid
63
63
 
64
- | Export | Type | Description |
65
- |--------|------|-------------|
66
- | `Cuid` | `Schema<string, Cuid>` | Branded schema for Cuid strings. |
67
- | `Cuid` (type) | `string` | Branded Cuid type. |
68
- | `isCuid` | `(value: string) => value is Cuid` | Type guard. |
69
- | `CuidState` | Service | Provides `next: Effect<CuidSeed>`. Used by `cuid`. |
70
- | `CuidState.Default` | `Layer<CuidState>` | Default CuidState (uses `"node"` envData). |
71
- | `cuid` | `Effect<Cuid, never, CuidState>` | Generate a Cuid. |
64
+ | Export | Type | Description |
65
+ | ------------------- | ---------------------------------- | -------------------------------------------------- |
66
+ | `Cuid` | `Schema<string, Cuid>` | Branded schema for Cuid strings. |
67
+ | `Cuid` (type) | `string` | Branded Cuid type. |
68
+ | `isCuid` | `(value: string) => value is Cuid` | Type guard. |
69
+ | `CuidState` | Service | Provides `next: Effect<CuidSeed>`. Used by `cuid`. |
70
+ | `CuidState.Default` | `Layer<CuidState>` | Default CuidState (uses `"node"` envData). |
71
+ | `cuid` | `Effect<Cuid, never, CuidState>` | Generate a Cuid. |
72
72
 
73
73
  ---
74
74
 
75
75
  ### Ksuid
76
76
 
77
- | Export | Type | Description |
78
- |--------|------|-------------|
79
- | `Ksuid` | `Schema<string, Ksuid>` | Branded schema for 27-char base62 Ksuids. |
80
- | `Ksuid` (type) | `string` | Branded Ksuid type. |
81
- | `isKsuid` | `(value: string) => value is Ksuid` | Type guard. |
82
- | `ksuid` | `Effect<Ksuid, never, DateTimes \| RandomValues>` | Generate a Ksuid. |
77
+ | Export | Type | Description |
78
+ | -------------- | ------------------------------------------------- | ----------------------------------------- |
79
+ | `Ksuid` | `Schema<string, Ksuid>` | Branded schema for 27-char base62 Ksuids. |
80
+ | `Ksuid` (type) | `string` | Branded Ksuid type. |
81
+ | `isKsuid` | `(value: string) => value is Ksuid` | Type guard. |
82
+ | `ksuid` | `Effect<Ksuid, never, DateTimes \| RandomValues>` | Generate a Ksuid. |
83
83
 
84
84
  ---
85
85
 
86
86
  ### NanoId
87
87
 
88
- | Export | Type | Description |
89
- |--------|------|-------------|
90
- | `NanoId` | `Schema<string, NanoId>` | Branded schema for `[0-9a-zA-Z_-]+` strings. |
91
- | `NanoId` (type) | `string` | Branded NanoId type. |
92
- | `isNanoId` | `(value: string) => value is NanoId` | Type guard. |
93
- | `nanoId` | `Effect<NanoId, never, RandomValues>` | Generate a 21-char NanoId. |
88
+ | Export | Type | Description |
89
+ | --------------- | ------------------------------------- | -------------------------------------------- |
90
+ | `NanoId` | `Schema<string, NanoId>` | Branded schema for `[0-9a-zA-Z_-]+` strings. |
91
+ | `NanoId` (type) | `string` | Branded NanoId type. |
92
+ | `isNanoId` | `(value: string) => value is NanoId` | Type guard. |
93
+ | `nanoId` | `Effect<NanoId, never, RandomValues>` | Generate a 21-char NanoId. |
94
94
 
95
95
  ---
96
96
 
97
97
  ### Ulid
98
98
 
99
- | Export | Type | Description |
100
- |--------|------|-------------|
101
- | `Ulid` | `Schema<string, Ulid>` | Branded schema for ULID strings. |
102
- | `Ulid` (type) | `string` | Branded Ulid type. |
103
- | `isUlid` | `(value: string) => value is Ulid` | Type guard. |
104
- | `ulid` | `Effect<Ulid, never, DateTimes \| RandomValues>` | Generate a ULID. |
99
+ | Export | Type | Description |
100
+ | ------------- | ------------------------------------------------ | -------------------------------- |
101
+ | `Ulid` | `Schema<string, Ulid>` | Branded schema for ULID strings. |
102
+ | `Ulid` (type) | `string` | Branded Ulid type. |
103
+ | `isUlid` | `(value: string) => value is Ulid` | Type guard. |
104
+ | `ulid` | `Effect<Ulid, never, DateTimes \| RandomValues>` | Generate a ULID. |
105
105
 
106
106
  ---
107
107
 
108
108
  ### Uuid4
109
109
 
110
- | Export | Type | Description |
111
- |--------|------|-------------|
112
- | `Uuid4` | `Schema<string, Uuid4>` | Branded schema for UUID v4. |
113
- | `Uuid4` (type) | `string` | Branded Uuid4 type. |
114
- | `isUuid4` | `(value: string) => value is Uuid4` | Type guard. |
115
- | `uuid4` | `Effect<Uuid4, never, RandomValues>` | Generate a UUID v4. |
110
+ | Export | Type | Description |
111
+ | -------------- | ------------------------------------ | --------------------------- |
112
+ | `Uuid4` | `Schema<string, Uuid4>` | Branded schema for UUID v4. |
113
+ | `Uuid4` (type) | `string` | Branded Uuid4 type. |
114
+ | `isUuid4` | `(value: string) => value is Uuid4` | Type guard. |
115
+ | `uuid4` | `Effect<Uuid4, never, RandomValues>` | Generate a UUID v4. |
116
116
 
117
117
  ---
118
118
 
119
119
  ### Uuid5
120
120
 
121
- | Export | Type | Description |
122
- |--------|------|-------------|
123
- | `Uuid5` | `Schema<string, Uuid5>` | Branded schema for UUID v5. |
124
- | `Uuid5` (type) | `string` | Branded Uuid5 type. |
125
- | `Uuid5Namespace` | `Uint8Array` (type) + const object | Namespace type; const has `DNS`, `URL`, `OID`, `X500`. |
126
- | `isUuid5` | `(value: string) => value is Uuid5` | Type guard. |
127
- | `uuid5` | `(name, namespace) => Effect<Uuid5>` or `(namespace) => (name) => Effect<Uuid5>` | Generate UUID v5 from name + namespace. |
128
- | `dnsUuid5`, `urlUuid5`, `oidUuid5`, `x500Uuid5` | `(name: string) => Effect<Uuid5>` | Pre-bound effects for standard namespaces. |
121
+ | Export | Type | Description |
122
+ | ----------------------------------------------- | -------------------------------------------------------------------------------- | ------------------------------------------------------ |
123
+ | `Uuid5` | `Schema<string, Uuid5>` | Branded schema for UUID v5. |
124
+ | `Uuid5` (type) | `string` | Branded Uuid5 type. |
125
+ | `Uuid5Namespace` | `Uint8Array` (type) + const object | Namespace type; const has `DNS`, `URL`, `OID`, `X500`. |
126
+ | `isUuid5` | `(value: string) => value is Uuid5` | Type guard. |
127
+ | `uuid5` | `(name, namespace) => Effect<Uuid5>` or `(namespace) => (name) => Effect<Uuid5>` | Generate UUID v5 from name + namespace. |
128
+ | `dnsUuid5`, `urlUuid5`, `oidUuid5`, `x500Uuid5` | `(name: string) => Effect<Uuid5>` | Pre-bound effects for standard namespaces. |
129
129
 
130
130
  ---
131
131
 
132
132
  ### Uuid7
133
133
 
134
- | Export | Type | Description |
135
- |--------|------|-------------|
136
- | `Uuid7` | `Schema<string, Uuid7>` | Branded schema for UUID v7. |
137
- | `Uuid7` (type) | `string` | Branded Uuid7 type. |
138
- | `isUuid7` | `(value: string) => value is Uuid7` | Type guard. |
139
- | `Uuid7State` | Service | Provides `next: Effect<Uuid7Seed>`. Used by `uuid7`. |
140
- | `Uuid7State.Default` | `Layer<Uuid7State>` | Default Uuid7State. |
141
- | `uuid7` | `Effect<Uuid7, never, Uuid7State>` | Generate a UUID v7. |
134
+ | Export | Type | Description |
135
+ | -------------------- | ----------------------------------- | ---------------------------------------------------- |
136
+ | `Uuid7` | `Schema<string, Uuid7>` | Branded schema for UUID v7. |
137
+ | `Uuid7` (type) | `string` | Branded Uuid7 type. |
138
+ | `isUuid7` | `(value: string) => value is Uuid7` | Type guard. |
139
+ | `Uuid7State` | Service | Provides `next: Effect<Uuid7Seed>`. Used by `uuid7`. |
140
+ | `Uuid7State.Default` | `Layer<Uuid7State>` | Default Uuid7State. |
141
+ | `uuid7` | `Effect<Uuid7, never, Uuid7State>` | Generate a UUID v7. |
142
142
 
143
143
  ---
144
144
 
145
145
  ### DateTimes
146
146
 
147
- | Export | Type | Description |
148
- |--------|------|-------------|
149
- | `DateTimes` | Service | Provides `now: Effect<number>`, `date: Effect<Date>`. |
150
- | `DateTimes.now` | `Effect<number, never, DateTimes>` | Current time in ms. |
151
- | `DateTimes.date` | `Effect<Date, never, DateTimes>` | Current date. |
152
- | `DateTimes.Default` | `Layer<DateTimes>` | Real clock. |
153
- | `DateTimes.Fixed(baseDate)` | `Layer<DateTimes>` | Fixed time for tests; `baseDate` is `number \| string \| Date`. |
147
+ | Export | Type | Description |
148
+ | --------------------------- | ---------------------------------- | --------------------------------------------------------------- |
149
+ | `DateTimes` | Service | Provides `now: Effect<number>`, `date: Effect<Date>`. |
150
+ | `DateTimes.now` | `Effect<number, never, DateTimes>` | Current time in ms. |
151
+ | `DateTimes.date` | `Effect<Date, never, DateTimes>` | Current date. |
152
+ | `DateTimes.Default` | `Layer<DateTimes>` | Real clock. |
153
+ | `DateTimes.Fixed(baseDate)` | `Layer<DateTimes>` | Fixed time for tests; `baseDate` is `number \| string \| Date`. |
154
154
 
155
155
  ---
156
156
 
157
157
  ### RandomValues
158
158
 
159
- | Export | Type | Description |
160
- |--------|------|-------------|
161
- | `RandomValues` | Service | Provides a function `(length) => Effect<Uint8Array>`. |
162
- | `RandomValues.call(length)` | `Effect<Uint8Array, never, RandomValues>` | Request `length` cryptographically random bytes. |
163
- | `RandomValues.Default` | `Layer<RandomValues>` | Uses `crypto.getRandomValues`. |
164
- | `RandomValues.Random` | `Layer<RandomValues>` | Uses Effect `Random` (e.g. for tests). |
159
+ | Export | Type | Description |
160
+ | --------------------------- | ----------------------------------------- | ----------------------------------------------------- |
161
+ | `RandomValues` | Service | Provides a function `(length) => Effect<Uint8Array>`. |
162
+ | `RandomValues.call(length)` | `Effect<Uint8Array, never, RandomValues>` | Request `length` cryptographically random bytes. |
163
+ | `RandomValues.Default` | `Layer<RandomValues>` | Uses `crypto.getRandomValues`. |
164
+ | `RandomValues.Random` | `Layer<RandomValues>` | Uses Effect `Random` (e.g. for tests). |
package/dist/Ids.d.ts CHANGED
@@ -12,6 +12,7 @@ import type { Uuid4 } from "./Uuid4.js";
12
12
  import { type Uuid5, Uuid5Namespace } from "./Uuid5.js";
13
13
  import type { Uuid7 } from "./Uuid7.js";
14
14
  import { Uuid7State } from "./Uuid7.js";
15
+ import { TestClock } from "effect/testing";
15
16
  declare const Ids_base: ServiceMap.ServiceClass<Ids, "@typed/id/Ids", {
16
17
  cuid: Effect.Effect<string & import("effect/Brand").Brand<"@typed/id/CUID">, never, never>;
17
18
  ksuid: Effect.Effect<string & import("effect/Brand").Brand<"@typed/id/KSUID">, never, never>;
@@ -61,7 +62,7 @@ export declare class Ids extends Ids_base {
61
62
  };
62
63
  static readonly uuid7: Effect.Effect<Uuid7, never, Ids>;
63
64
  static readonly Default: Layer.Layer<Ids | DateTimes | RandomValues, never, never>;
64
- static readonly Test: (options?: TestOptions) => Layer.Layer<Ids | DateTimes | RandomValues>;
65
+ static readonly Test: (options?: TestOptions) => Layer.Layer<Ids | DateTimes | RandomValues | TestClock.TestClock>;
65
66
  }
66
67
  export type TestOptions = {
67
68
  readonly currentTime?: number | string | Date;
package/dist/Ids.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"Ids.d.ts","sourceRoot":"","sources":["../src/Ids.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Ids.d.ts","sourceRoot":"","sources":["../src/Ids.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,KAAK,UAAU,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAQ,SAAS,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC,OAAO,EAAE,KAAK,KAAK,EAAS,cAAc,EAAE,MAAM,YAAY,CAAC;AAC/D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAS,UAAU,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;;;;;;;;oBAOzB,cAAc,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC;eAC5D,MAAM,aAAa,cAAc,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC;sBACjD,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC;sBACtC,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC;sBACtC,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC;uBACrC,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC;;;;;;;;;;;wBALzC,cAAc,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC;mBAC5D,MAAM,aAAa,cAAc,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC;0BACjD,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC;0BACtC,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC;0BACtC,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC;2BACrC,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC;;;;;AAV3D,qBAAa,GAAI,SAAQ,QAiCvB;IACA,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,CAGnD;IAEF,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC,CAGrD;IAEF,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,CAGvD;IAEF,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,CAGnD;IAEF,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC,CAGrD;IAEF,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE;QACrB,CAAC,SAAS,EAAE,cAAc,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;QAChF,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,cAAc,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;QAC5E,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;QACjE,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;QACjE,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;QACjE,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;KACnE,CAUC;IACF,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC,CAGrD;IAEF,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,GAAG,SAAS,GAAG,YAAY,EAAE,KAAK,EAAE,KAAK,CAAC,CAMhF;IAEF,MAAM,CAAC,QAAQ,CAAC,IAAI,GAClB,UAAU,WAAW,KACpB,KAAK,CAAC,KAAK,CAAC,GAAG,GAAG,SAAS,GAAG,YAAY,GAAG,SAAS,CAAC,SAAS,CAAC,CAQhE;CACL;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC9C,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC"}
package/dist/Ids.js CHANGED
@@ -11,6 +11,7 @@ import { ulid } from "./Ulid.js";
11
11
  import { uuid4 } from "./Uuid4.js";
12
12
  import { uuid5, Uuid5Namespace } from "./Uuid5.js";
13
13
  import { uuid7, Uuid7State } from "./Uuid7.js";
14
+ import { TestClock } from "effect/testing";
14
15
  export class Ids extends ServiceMap.Service()("@typed/id/Ids", {
15
16
  make: Effect.gen(function* () {
16
17
  const services = yield* Effect.services();
@@ -47,5 +48,5 @@ export class Ids extends ServiceMap.Service()("@typed/id/Ids", {
47
48
  static Test = (options) => Layer.effect(Ids, Ids.make).pipe(Layer.provide([
48
49
  Layer.effect(CuidState, CuidState.make(options?.envData ?? "node")),
49
50
  Uuid7State.Default,
50
- ]), Layer.provideMerge([DateTimes.Fixed(options?.currentTime ?? 0), RandomValues.Random]));
51
+ ]), Layer.provideMerge([DateTimes.Fixed(options?.currentTime ?? 0), RandomValues.Random]), Layer.provideMerge(TestClock.layer({})));
51
52
  }
package/dist/Ksuid.js CHANGED
@@ -9,7 +9,7 @@ const PAYLOAD_BYTES = 16;
9
9
  const TOTAL_BYTES = TIMESTAMP_BYTES + PAYLOAD_BYTES;
10
10
  const STRING_LENGTH = 27;
11
11
  // Schema
12
- export const Ksuid = Schema.String.pipe(Schema.check(Schema.isPattern(/^[0-9a-zA-Z]{27}$/)), Schema.brand("@typed/id/KSUID"));
12
+ export const Ksuid = Schema.String.pipe(Schema.check(Schema.isPattern(/^[0-9A-Za-z]{27}$/)), Schema.brand("@typed/id/KSUID"));
13
13
  export const isKsuid = Schema.is(Ksuid);
14
14
  // Public API
15
15
  export const ksuid = Effect.zipWith(DateTimes.now, RandomValues.call(PAYLOAD_BYTES), (timestamp, payload) => {
package/package.json CHANGED
@@ -1,9 +1,6 @@
1
1
  {
2
2
  "name": "@typed/id",
3
- "version": "1.0.0-beta.0",
4
- "publishConfig": {
5
- "access": "public"
6
- },
3
+ "version": "1.0.0-beta.2",
7
4
  "type": "module",
8
5
  "exports": {
9
6
  ".": {
@@ -15,15 +12,22 @@
15
12
  "import": "./dist/*.js"
16
13
  }
17
14
  },
15
+ "publishConfig": {
16
+ "access": "public"
17
+ },
18
18
  "dependencies": {
19
- "effect": "4.0.0-beta.4"
19
+ "effect": "4.0.0-beta.21"
20
20
  },
21
21
  "devDependencies": {
22
22
  "typescript": "5.9.3",
23
23
  "vitest": "4.0.18"
24
24
  },
25
+ "files": [
26
+ "dist",
27
+ "src"
28
+ ],
25
29
  "scripts": {
26
- "build": "tsc",
30
+ "build": "[ -d dist ] || rm -f tsconfig.tsbuildinfo; tsc",
27
31
  "test": "vitest run --passWithNoTests"
28
32
  }
29
33
  }
package/src/Id.test.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import * as Effect from "effect/Effect";
2
- import { assert, describe, expect, it } from "vitest";
2
+ import * as FastCheck from "effect/testing/FastCheck";
3
+ import { describe, expect, it } from "vitest";
3
4
  import * as Cuid from "./Cuid.js";
4
5
  import { Ids } from "./Ids.js";
5
6
  import * as Ksuid from "./Ksuid.js";
@@ -15,91 +16,116 @@ const run = <A, E>(effect: Effect.Effect<A, E, Ids>) =>
15
16
  Effect.runPromise(Effect.provide(effect, Ids.Test()));
16
17
 
17
18
  describe("@typed/id", () => {
18
- describe("type guards", () => {
19
+ describe("type guards (property-based)", () => {
19
20
  describe("isUuid4", () => {
20
- it("accepts valid UUID v4", () => {
21
- expect(Uuid4.isUuid4("f47ac10b-58cc-4372-a567-0e02b2c3d479")).toBe(true);
22
- expect(Uuid4.isUuid4("550e8400-e29b-41d4-a716-446655440000")).toBe(true);
23
- });
24
- it("rejects invalid formats", () => {
25
- expect(Uuid4.isUuid4("")).toBe(false);
26
- expect(Uuid4.isUuid4("not-a-uuid")).toBe(false);
27
- expect(Uuid4.isUuid4("f47ac10b-58cc-4372-a567-0e02b2c3d479".replace(/-/g, ""))).toBe(false);
28
- expect(Uuid4.isUuid4("g47ac10b-58cc-4372-a567-0e02b2c3d479")).toBe(false); // invalid hex
21
+ it("accepts any UUID v4", () => {
22
+ FastCheck.assert(
23
+ FastCheck.property(FastCheck.uuid({ version: 4 }), (s) => Uuid4.isUuid4(s)),
24
+ );
29
25
  });
30
- it("rejects UUID v5 as UUID v4", () => {
31
- expect(Uuid4.isUuid4("886313e1-3b8a-5372-9b90-0c9aee199e5d")).toBe(false);
26
+ it("rejects non-UUID-v4 strings", () => {
27
+ const invalid = FastCheck.oneof(
28
+ FastCheck.constant(""),
29
+ FastCheck.string({ maxLength: 35 }).filter((s) => s.length < 36),
30
+ FastCheck.string().filter(
31
+ (s) =>
32
+ !/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(s),
33
+ ),
34
+ );
35
+ FastCheck.assert(FastCheck.property(invalid, (s) => !Uuid4.isUuid4(s)));
32
36
  });
33
37
  });
34
38
 
35
39
  describe("isUuid5", () => {
36
- it("accepts valid UUID v5", () => {
37
- expect(Uuid5.isUuid5("886313e1-3b8a-5372-9b90-0c9aee199e5d")).toBe(true);
38
- expect(Uuid5.isUuid5("c2ee5f2e-5b2e-5f2e-8b2e-5b2e5f2e8b2e")).toBe(true);
40
+ it("accepts any UUID v5", () => {
41
+ FastCheck.assert(
42
+ FastCheck.property(FastCheck.uuid({ version: 5 }), (s) => Uuid5.isUuid5(s)),
43
+ );
39
44
  });
40
- it("rejects invalid formats", () => {
41
- expect(Uuid5.isUuid5("")).toBe(false);
42
- expect(Uuid5.isUuid5("f47ac10b-58cc-4372-a567-0e02b2c3d479")).toBe(false); // v4
45
+ it("rejects non-UUID-v5 strings", () => {
46
+ const invalid = FastCheck.oneof(
47
+ FastCheck.constant(""),
48
+ FastCheck.string().filter(
49
+ (s) =>
50
+ !/^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(s),
51
+ ),
52
+ );
53
+ FastCheck.assert(FastCheck.property(invalid, (s) => !Uuid5.isUuid5(s)));
43
54
  });
44
55
  });
45
56
 
46
57
  describe("isUuid7", () => {
47
- it("accepts valid UUID v7", () => {
48
- expect(Uuid7.isUuid7("018eebb4-1f2c-7c3a-8b4d-123456789abc")).toBe(true);
58
+ it("accepts any UUID v7", () => {
59
+ FastCheck.assert(
60
+ FastCheck.property(FastCheck.uuid({ version: 7 }), (s) => Uuid7.isUuid7(s)),
61
+ );
49
62
  });
50
- it("rejects invalid formats", () => {
51
- expect(Uuid7.isUuid7("")).toBe(false);
52
- expect(Uuid7.isUuid7("f47ac10b-58cc-4372-a567-0e02b2c3d479")).toBe(false); // v4
63
+ it("rejects non-UUID-v7 strings", () => {
64
+ const invalid = FastCheck.oneof(
65
+ FastCheck.constant(""),
66
+ FastCheck.string().filter(
67
+ (s) =>
68
+ !/^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(s),
69
+ ),
70
+ );
71
+ FastCheck.assert(FastCheck.property(invalid, (s) => !Uuid7.isUuid7(s)));
53
72
  });
54
73
  });
55
74
 
56
75
  describe("isCuid", () => {
57
- it("accepts valid CUID-like strings (lowercase letter + base36)", () => {
58
- expect(Cuid.isCuid("c1234567890abcdefghijklmn")).toBe(true);
59
- expect(Cuid.isCuid("a0")).toBe(true);
60
- });
61
- it("rejects invalid formats", () => {
62
- expect(Cuid.isCuid("")).toBe(false);
63
- expect(Cuid.isCuid("A123")).toBe(false); // must start with lowercase
64
- expect(Cuid.isCuid("1abc")).toBe(false); // must start with letter
65
- expect(Cuid.isCuid("c123-456")).toBe(false); // no hyphen
76
+ it("accepts any CUID-like string (lowercase letter + base36)", () => {
77
+ const cuidArb = FastCheck.stringMatching(/^[a-z][0-9a-z]+$/);
78
+ FastCheck.assert(FastCheck.property(cuidArb, (s) => Cuid.isCuid(s)));
79
+ });
80
+ it("rejects strings that do not match CUID pattern", () => {
81
+ const invalid = FastCheck.oneof(
82
+ FastCheck.constant(""),
83
+ FastCheck.string().filter((s) => !/^[a-z][0-9a-z]+$/.test(s)),
84
+ );
85
+ FastCheck.assert(FastCheck.property(invalid, (s) => !Cuid.isCuid(s)));
66
86
  });
67
87
  });
68
88
 
69
89
  describe("isKsuid", () => {
70
- it("accepts 27-char base62 strings", () => {
71
- assert(Ksuid.isKsuid("0123456789ABCDEFGHIJKLMNOPQ"));
72
- assert(Ksuid.isKsuid("000000000000000000000000000"));
73
- });
74
- it("rejects invalid formats", () => {
75
- expect(Ksuid.isKsuid("")).toBe(false);
76
- expect(Ksuid.isKsuid("short")).toBe(false);
77
- expect(Ksuid.isKsuid("0ujsszwN8NRYtYaMBZrYCVp4O1!")).toBe(false); // 28 chars, invalid char
90
+ it("accepts any 27-char base62 string", () => {
91
+ const ksuidArb = FastCheck.stringMatching(/^[0-9A-Za-z]{27}$/);
92
+ FastCheck.assert(FastCheck.property(ksuidArb, (s) => Ksuid.isKsuid(s)));
93
+ });
94
+ it("rejects non-KSUID strings", () => {
95
+ const invalid = FastCheck.oneof(
96
+ FastCheck.constant(""),
97
+ FastCheck.string().filter((s) => s.length !== 27 || !/^[0-9A-Za-z]{27}$/.test(s)),
98
+ );
99
+ FastCheck.assert(FastCheck.property(invalid, (s) => !Ksuid.isKsuid(s)));
78
100
  });
79
101
  });
80
102
 
81
103
  describe("isNanoId", () => {
82
- it("accepts strings with only 0-9a-zA-Z_-", () => {
83
- assert(NanoId.isNanoId("V1StGXR8_Z5jdHi6B-myT"));
84
- assert(NanoId.isNanoId("abc123"));
85
- assert(NanoId.isNanoId("_-_"));
86
- });
87
- it("rejects invalid characters", () => {
88
- expect(NanoId.isNanoId("")).toBe(false);
89
- expect(NanoId.isNanoId(" ")).toBe(false);
90
- expect(NanoId.isNanoId("!@#$%")).toBe(false);
104
+ it("accepts any string with only 0-9a-zA-Z_-", () => {
105
+ const nanoIdArb = FastCheck.stringMatching(/^[0-9a-zA-Z_-]+$/);
106
+ FastCheck.assert(FastCheck.property(nanoIdArb, (s) => NanoId.isNanoId(s)));
107
+ });
108
+ it("rejects strings with invalid characters", () => {
109
+ const invalid = FastCheck.oneof(
110
+ FastCheck.constant(""),
111
+ FastCheck.string().filter((s) => !/^[0-9a-zA-Z_-]+$/.test(s)),
112
+ );
113
+ FastCheck.assert(FastCheck.property(invalid, (s) => !NanoId.isNanoId(s)));
91
114
  });
92
115
  });
93
116
 
94
117
  describe("isUlid", () => {
95
- it("accepts valid ULIDs (26 chars, Effect alphabet 0-9A-HJKMNP-TV-Z)", () => {
96
- expect(Ulid.isUlid("01D78XYFJ1PRM1WPBCBT3VHMNV")).toBe(true);
97
- });
98
- it("rejects invalid formats", () => {
99
- expect(Ulid.isUlid("")).toBe(false);
100
- expect(Ulid.isUlid("01ARZ3NDEKTSV4RRFFQ69G5FA")).toBe(false); // wrong length
101
- expect(Ulid.isUlid("01ARZ3NDEKTSV4RRFFQ69G5FAV0")).toBe(false); // wrong length
102
- expect(Ulid.isUlid("01ARZ3NDEKTSV4RRFFQ69G5FAI")).toBe(false); // I not in Crockford base32
118
+ it("accepts any valid ULID", () => {
119
+ FastCheck.assert(FastCheck.property(FastCheck.ulid(), (s) => Ulid.isUlid(s)));
120
+ });
121
+ it("rejects non-ULID strings", () => {
122
+ const invalid = FastCheck.oneof(
123
+ FastCheck.constant(""),
124
+ FastCheck.string().filter(
125
+ (s) => s.length !== 26 || !/^[0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{26}$/.test(s),
126
+ ),
127
+ );
128
+ FastCheck.assert(FastCheck.property(invalid, (s) => !Ulid.isUlid(s)));
103
129
  });
104
130
  });
105
131
  });
@@ -107,13 +133,11 @@ describe("@typed/id", () => {
107
133
  describe("Ids service", () => {
108
134
  it("uuid4 produces valid UUID v4", async () => {
109
135
  const id = await run(Ids.uuid4);
110
- expect(id).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i);
111
136
  expect(Uuid4.isUuid4(id)).toBe(true);
112
137
  });
113
138
 
114
139
  it("uuid5 produces valid UUID v5", async () => {
115
140
  const id = await run(Ids.uuid5("hello", Uuid5.Uuid5Namespace.DNS));
116
- expect(id).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i);
117
141
  expect(Uuid5.isUuid5(id)).toBe(true);
118
142
  });
119
143
 
@@ -143,27 +167,23 @@ describe("@typed/id", () => {
143
167
 
144
168
  it("uuid7 produces valid UUID v7", async () => {
145
169
  const id = await run(Ids.uuid7);
146
- expect(id).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i);
147
170
  expect(Uuid7.isUuid7(id)).toBe(true);
148
171
  });
149
172
 
150
173
  it("cuid produces valid CUID", async () => {
151
174
  const id = await run(Ids.cuid);
152
175
  expect(Cuid.isCuid(id)).toBe(true);
153
- expect(id).toMatch(/^[a-z][0-9a-z]+$/);
154
176
  });
155
177
 
156
178
  it("ksuid produces valid KSUID", async () => {
157
179
  const id = await run(Ids.ksuid);
158
180
  expect(Ksuid.isKsuid(id)).toBe(true);
159
181
  expect(id).toHaveLength(27);
160
- expect(id).toMatch(/^[0-9a-zA-Z]{27}$/);
161
182
  });
162
183
 
163
184
  it("nanoId produces valid NanoId", async () => {
164
185
  const id = await run(Ids.nanoId);
165
186
  expect(NanoId.isNanoId(id)).toBe(true);
166
- expect(id).toMatch(/^[0-9a-zA-Z_-]+$/);
167
187
  expect(id).toHaveLength(21);
168
188
  });
169
189
 
@@ -171,17 +191,11 @@ describe("@typed/id", () => {
171
191
  const id = await run(Ids.ulid);
172
192
  expect(Ulid.isUlid(id)).toBe(true);
173
193
  expect(id).toHaveLength(26);
174
- expect(id).toMatch(/^[0-9A-HJKMNP-TV-Z]{26}$/);
175
194
  });
176
195
 
177
196
  it("Ids.Test with fixed time yields deterministic time-based prefixes", async () => {
178
197
  const runFixed = <A, E>(effect: Effect.Effect<A, E, Ids>) =>
179
- effect.pipe(
180
- Effect.provide(Ids.Test({})),
181
- Effect.provide(TestClock.layer({})),
182
- Random.withSeed(42),
183
- Effect.runPromise,
184
- );
198
+ effect.pipe(Effect.provide(Ids.Test({})), Random.withSeed(0), Effect.runPromise);
185
199
 
186
200
  const [ulid1, ulid2, ksuid1, ksuid2] = await runFixed(
187
201
  Effect.all([Ids.ulid, Ids.ulid, Ids.ksuid, Ids.ksuid], { concurrency: "unbounded" }),
package/src/Ids.ts CHANGED
@@ -17,6 +17,7 @@ import { uuid4 } from "./Uuid4.js";
17
17
  import { type Uuid5, uuid5, Uuid5Namespace } from "./Uuid5.js";
18
18
  import type { Uuid7 } from "./Uuid7.js";
19
19
  import { uuid7, Uuid7State } from "./Uuid7.js";
20
+ import { TestClock } from "effect/testing";
20
21
 
21
22
  export class Ids extends ServiceMap.Service<Ids>()("@typed/id/Ids", {
22
23
  make: Effect.gen(function* () {
@@ -108,13 +109,16 @@ export class Ids extends ServiceMap.Service<Ids>()("@typed/id/Ids", {
108
109
  Layer.provideMerge([DateTimes.Default, RandomValues.Default]),
109
110
  );
110
111
 
111
- static readonly Test = (options?: TestOptions): Layer.Layer<Ids | DateTimes | RandomValues> =>
112
+ static readonly Test = (
113
+ options?: TestOptions,
114
+ ): Layer.Layer<Ids | DateTimes | RandomValues | TestClock.TestClock> =>
112
115
  Layer.effect(Ids, Ids.make).pipe(
113
116
  Layer.provide([
114
117
  Layer.effect(CuidState, CuidState.make(options?.envData ?? "node")),
115
118
  Uuid7State.Default,
116
119
  ]),
117
120
  Layer.provideMerge([DateTimes.Fixed(options?.currentTime ?? 0), RandomValues.Random]),
121
+ Layer.provideMerge(TestClock.layer({})),
118
122
  );
119
123
  }
120
124
 
package/src/Ksuid.ts CHANGED
@@ -12,7 +12,7 @@ const STRING_LENGTH = 27;
12
12
 
13
13
  // Schema
14
14
  export const Ksuid = Schema.String.pipe(
15
- Schema.check(Schema.isPattern(/^[0-9a-zA-Z]{27}$/)),
15
+ Schema.check(Schema.isPattern(/^[0-9A-Za-z]{27}$/)),
16
16
  Schema.brand("@typed/id/KSUID"),
17
17
  );
18
18
  export type Ksuid = typeof Ksuid.Type;
package/dist/Id.test.d.ts DELETED
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=Id.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Id.test.d.ts","sourceRoot":"","sources":["../src/Id.test.ts"],"names":[],"mappings":""}
package/dist/Id.test.js DELETED
@@ -1,190 +0,0 @@
1
- import * as Effect from "effect/Effect";
2
- import { describe, expect, it } from "vitest";
3
- import * as Cuid from "./Cuid.js";
4
- import { Ids } from "./Ids.js";
5
- import * as Ksuid from "./Ksuid.js";
6
- import * as NanoId from "./NanoId.js";
7
- import * as Ulid from "./Ulid.js";
8
- import * as Uuid4 from "./Uuid4.js";
9
- import * as Uuid5 from "./Uuid5.js";
10
- import * as Uuid7 from "./Uuid7.js";
11
- const run = (effect) => Effect.runPromise(Effect.provide(effect, Ids.Test()));
12
- describe("@typed/id", () => {
13
- describe("type guards", () => {
14
- describe("isUuid4", () => {
15
- it("accepts valid UUID v4", () => {
16
- expect(Uuid4.isUuid4("f47ac10b-58cc-4372-a567-0e02b2c3d479")).toBe(true);
17
- expect(Uuid4.isUuid4("550e8400-e29b-41d4-a716-446655440000")).toBe(true);
18
- });
19
- it("rejects invalid formats", () => {
20
- expect(Uuid4.isUuid4("")).toBe(false);
21
- expect(Uuid4.isUuid4("not-a-uuid")).toBe(false);
22
- expect(Uuid4.isUuid4("f47ac10b-58cc-4372-a567-0e02b2c3d479".replace(/-/g, ""))).toBe(false);
23
- expect(Uuid4.isUuid4("g47ac10b-58cc-4372-a567-0e02b2c3d479")).toBe(false); // invalid hex
24
- });
25
- it("rejects UUID v5 as UUID v4", () => {
26
- expect(Uuid4.isUuid4("886313e1-3b8a-5372-9b90-0c9aee199e5d")).toBe(false);
27
- });
28
- });
29
- describe("isUuid5", () => {
30
- it("accepts valid UUID v5", () => {
31
- expect(Uuid5.isUuid5("886313e1-3b8a-5372-9b90-0c9aee199e5d")).toBe(true);
32
- expect(Uuid5.isUuid5("c2ee5f2e-5b2e-5f2e-8b2e-5b2e5f2e8b2e")).toBe(true);
33
- });
34
- it("rejects invalid formats", () => {
35
- expect(Uuid5.isUuid5("")).toBe(false);
36
- expect(Uuid5.isUuid5("f47ac10b-58cc-4372-a567-0e02b2c3d479")).toBe(false); // v4
37
- });
38
- });
39
- describe("isUuid7", () => {
40
- it("accepts valid UUID v7", () => {
41
- expect(Uuid7.isUuid7("018eebb4-1f2c-7c3a-8b4d-123456789abc")).toBe(true);
42
- });
43
- it("rejects invalid formats", () => {
44
- expect(Uuid7.isUuid7("")).toBe(false);
45
- expect(Uuid7.isUuid7("f47ac10b-58cc-4372-a567-0e02b2c3d479")).toBe(false); // v4
46
- });
47
- });
48
- describe("isCuid", () => {
49
- it("accepts valid CUID-like strings (lowercase letter + base36)", () => {
50
- expect(Cuid.isCuid("c1234567890abcdefghijklmn")).toBe(true);
51
- expect(Cuid.isCuid("a0")).toBe(true);
52
- });
53
- it("rejects invalid formats", () => {
54
- expect(Cuid.isCuid("")).toBe(false);
55
- expect(Cuid.isCuid("A123")).toBe(false); // must start with lowercase
56
- expect(Cuid.isCuid("1abc")).toBe(false); // must start with letter
57
- expect(Cuid.isCuid("c123-456")).toBe(false); // no hyphen
58
- });
59
- });
60
- describe("isKsuid", () => {
61
- it("accepts 27-char base62 strings", () => {
62
- expect(Ksuid.isKsuid("0ujsszwN8NRYtYaMBZrYCVp4O1")).toBe(true);
63
- expect(Ksuid.isKsuid("0123456789ABCDEFGHIJKLMNOP")).toBe(true);
64
- });
65
- it("rejects invalid formats", () => {
66
- expect(Ksuid.isKsuid("")).toBe(false);
67
- expect(Ksuid.isKsuid("short")).toBe(false);
68
- expect(Ksuid.isKsuid("0ujsszwN8NRYtYaMBZrYCVp4O1!")).toBe(false); // 28 chars, invalid char
69
- });
70
- });
71
- describe("isNanoId", () => {
72
- it("accepts strings with only 0-9a-zA-Z_-", () => {
73
- expect(NanoId.isNanoId("V1StGXR8_Z5jdHi6B-myT")).toBe(true);
74
- expect(NanoId.isNanoId("abc123")).toBe(true);
75
- expect(NanoId.isNanoId("_-_")).toBe(true);
76
- });
77
- it("rejects invalid characters", () => {
78
- expect(NanoId.isNanoId("")).toBe(false);
79
- expect(NanoId.isNanoId("space in id")).toBe(false);
80
- expect(NanoId.isNanoId("dot.id")).toBe(false);
81
- });
82
- });
83
- describe("isUlid", () => {
84
- it("accepts valid ULIDs", () => {
85
- expect(Ulid.isUlid("01ARZ3NDEKTSV4RRFFQ69G5FAV")).toBe(true);
86
- expect(Ulid.isUlid("0123456789ABCDEFGHJKMNPQR")).toBe(true);
87
- });
88
- it("rejects invalid formats", () => {
89
- expect(Ulid.isUlid("")).toBe(false);
90
- expect(Ulid.isUlid("01ARZ3NDEKTSV4RRFFQ69G5FA")).toBe(false); // wrong length
91
- expect(Ulid.isUlid("01ARZ3NDEKTSV4RRFFQ69G5FAV0")).toBe(false); // wrong length
92
- expect(Ulid.isUlid("01ARZ3NDEKTSV4RRFFQ69G5FAI")).toBe(false); // I not in Crockford base32
93
- });
94
- });
95
- });
96
- describe("Ids service", () => {
97
- it("uuid4 produces valid UUID v4", async () => {
98
- const id = await run(Ids.uuid4);
99
- expect(id).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i);
100
- expect(Uuid4.isUuid4(id)).toBe(true);
101
- });
102
- it("uuid5 produces valid UUID v5", async () => {
103
- const id = await run(Ids.uuid5("hello", Uuid5.Uuid5Namespace.DNS));
104
- expect(id).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i);
105
- expect(Uuid5.isUuid5(id)).toBe(true);
106
- });
107
- it("uuid5 is deterministic for same name and namespace", async () => {
108
- const a = await run(Ids.uuid5("test", Uuid5.Uuid5Namespace.URL));
109
- const b = await run(Ids.uuid5("test", Uuid5.Uuid5Namespace.URL));
110
- expect(a).toBe(b);
111
- });
112
- it("uuid5 differs for different names", async () => {
113
- const a = await run(Ids.uuid5("a", Uuid5.Uuid5Namespace.DNS));
114
- const b = await run(Ids.uuid5("b", Uuid5.Uuid5Namespace.DNS));
115
- expect(a).not.toBe(b);
116
- });
117
- it("uuid5 predefined namespaces work", async () => {
118
- const dns = await run(Ids.uuid5.dns("example.com"));
119
- const url = await run(Ids.uuid5.url("https://example.com"));
120
- const oid = await run(Ids.uuid5.oid("1.2.3"));
121
- const x500 = await run(Ids.uuid5.x500("cn=test"));
122
- expect(Uuid5.isUuid5(dns)).toBe(true);
123
- expect(Uuid5.isUuid5(url)).toBe(true);
124
- expect(Uuid5.isUuid5(oid)).toBe(true);
125
- expect(Uuid5.isUuid5(x500)).toBe(true);
126
- expect(new Set([dns, url, oid, x500]).size).toBe(4);
127
- });
128
- it("uuid7 produces valid UUID v7", async () => {
129
- const id = await run(Ids.uuid7);
130
- expect(id).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i);
131
- expect(Uuid7.isUuid7(id)).toBe(true);
132
- });
133
- it("cuid produces valid CUID", async () => {
134
- const id = await run(Ids.cuid);
135
- expect(Cuid.isCuid(id)).toBe(true);
136
- expect(id).toMatch(/^[a-z][0-9a-z]+$/);
137
- });
138
- it("ksuid produces valid KSUID", async () => {
139
- const id = await run(Ids.ksuid);
140
- expect(Ksuid.isKsuid(id)).toBe(true);
141
- expect(id).toHaveLength(27);
142
- expect(id).toMatch(/^[0-9a-zA-Z]{27}$/);
143
- });
144
- it("nanoId produces valid NanoId", async () => {
145
- const id = await run(Ids.nanoId);
146
- expect(NanoId.isNanoId(id)).toBe(true);
147
- expect(id).toMatch(/^[0-9a-zA-Z_-]+$/);
148
- expect(id).toHaveLength(21);
149
- });
150
- it("ulid produces valid ULID", async () => {
151
- const id = await run(Ids.ulid);
152
- expect(Ulid.isUlid(id)).toBe(true);
153
- expect(id).toHaveLength(26);
154
- expect(id).toMatch(/^[0-9A-HJKMNP-TV-Z]{26}$/);
155
- });
156
- it("Ids.Test with fixed time yields deterministic time-based prefixes", async () => {
157
- const fixedTime = new Date("2025-01-15T12:00:00Z").getTime();
158
- const runFixed = (effect) => Effect.runPromise(Effect.provide(effect, Ids.Test({ currentTime: fixedTime })));
159
- const ulid1 = await runFixed(Ids.ulid);
160
- const ulid2 = await runFixed(Ids.ulid);
161
- const ksuid1 = await runFixed(Ids.ksuid);
162
- const ksuid2 = await runFixed(Ids.ksuid);
163
- expect(ulid1.slice(0, 10)).toBe(ulid2.slice(0, 10));
164
- expect(ksuid1.slice(0, 5)).toBe(ksuid2.slice(0, 5));
165
- });
166
- });
167
- describe("Ids.Default", () => {
168
- it("runs all generators with default layer", async () => {
169
- const program = Effect.gen(function* () {
170
- const ids = yield* Ids;
171
- const u4 = yield* ids.uuid4;
172
- const u5 = yield* ids.uuid5("default-test", Uuid5.Uuid5Namespace.DNS);
173
- const u7 = yield* ids.uuid7;
174
- const c = yield* ids.cuid;
175
- const k = yield* ids.ksuid;
176
- const n = yield* ids.nanoId;
177
- const u = yield* ids.ulid;
178
- return { u4, u5, u7, c, k, n, u };
179
- });
180
- const result = await Effect.runPromise(Effect.provide(program, Ids.Default));
181
- expect(Uuid4.isUuid4(result.u4)).toBe(true);
182
- expect(Uuid5.isUuid5(result.u5)).toBe(true);
183
- expect(Uuid7.isUuid7(result.u7)).toBe(true);
184
- expect(Cuid.isCuid(result.c)).toBe(true);
185
- expect(Ksuid.isKsuid(result.k)).toBe(true);
186
- expect(NanoId.isNanoId(result.n)).toBe(true);
187
- expect(Ulid.isUlid(result.u)).toBe(true);
188
- });
189
- });
190
- });
package/tsconfig.json DELETED
@@ -1,6 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.base.json",
3
- "compilerOptions": { "rootDir": "src", "outDir": "dist" },
4
- "include": ["src"],
5
- "exclude": ["**/*.test.ts"]
6
- }
package/vitest.config.ts DELETED
@@ -1,8 +0,0 @@
1
- import { defineConfig } from "vitest/config";
2
-
3
- export default defineConfig({
4
- test: {
5
- include: ["src/**/*.{test,spec}.ts"],
6
- exclude: ["**/node_modules/**", "**/dist/**"],
7
- },
8
- });