effect-app 4.0.0-beta.253 → 4.0.0-beta.255

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/dist/ids.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ids.d.ts","sourceRoot":"","sources":["../src/ids.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,KAAK,EAAE,iBAAiB,EAAY,KAAK,aAAa,EAAmB,MAAM,mBAAmB,CAAA;AACjI,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,0BAA0B,CAAA;AAEjD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,KAAK,CAAC,MAAM,aAAa,CAAA;AAGhC,MAAM,WAAW,cAAe,SAAQ,aAAa;IACnD,QAAQ,CAAC,SAAS,EAAE,OAAO,MAAM,CAAA;CAClC;AAED,MAAM,MAAM,SAAS,GAAG,iBAAiB,CAAA;AACzC;;;;;;;;;GASG;AACH,eAAO,MAAM,SAAS;gBAIkB,iBAAiB;IAGnD;;;;;OAKG;;;;IALH;;;;;OAKG;;CAKe,CAAA;AAExB,MAAM,WAAW,kBAAmB,SAAQ,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC,GAAG,aAAa,CAAC;CAAG;AACjG,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,kBAAkB,CAAA;AACvD;;;;;GAKG;AACH,eAAO,MAAM,aAAa;;;;;;8DAAmC,CAAA"}
1
+ {"version":3,"file":"ids.d.ts","sourceRoot":"","sources":["../src/ids.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,KAAK,EAAE,iBAAiB,EAAY,KAAK,aAAa,EAAmB,MAAM,mBAAmB,CAAA;AACjI,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,0BAA0B,CAAA;AAEjD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,KAAK,CAAC,MAAM,aAAa,CAAA;AAGhC,MAAM,WAAW,cAAe,SAAQ,aAAa;IACnD,QAAQ,CAAC,SAAS,EAAE,OAAO,MAAM,CAAA;CAClC;AAED,MAAM,MAAM,SAAS,GAAG,iBAAiB,CAAA;AACzC;;;;;;;;;GASG;AACH,eAAO,MAAM,SAAS;gBAIkB,iBAAiB;IAGnD;;;;;OAKG;;;;IALH;;;;;OAKG;;CAKe,CAAA;AAExB,MAAM,WAAW,kBAAmB,SAAQ,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC,GAAG,aAAa,CAAC;CAAG;AACjG,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,kBAAkB,CAAA;AACvD;;;;;GAKG;AACH,eAAO,MAAM,aAAa,wCAAmC,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "effect-app",
3
- "version": "4.0.0-beta.253",
3
+ "version": "4.0.0-beta.255",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "dependencies": {
@@ -24,7 +24,7 @@
24
24
  "vitest": "^4.1.5"
25
25
  },
26
26
  "peerDependencies": {
27
- "effect": "^4.0.0-beta.70"
27
+ "effect": "^4.0.0-beta.71"
28
28
  },
29
29
  "typesVersions": {
30
30
  "*": {
@@ -2,6 +2,7 @@
2
2
 
3
3
  import * as Equivalence from "effect/Equivalence"
4
4
  import { flow, pipe } from "effect/Function"
5
+ import * as HashMap from "effect/HashMap"
5
6
  import * as HashSet from "effect/HashSet"
6
7
  import * as Pipeable from "effect/Pipeable"
7
8
  import * as Ref from "effect/Ref"
@@ -21,10 +22,10 @@ import * as Option from "../../../Option.js"
21
22
  import * as S from "../../../Schema.js"
22
23
  import { type Codec, NonNegativeInt } from "../../../Schema.js"
23
24
  import { setupRequestContextFromCurrent } from "../../../setupRequest.ts"
24
- import { type FilterArgs, getContextMap, type PersistenceModelType, type StoreConfig, StoreMaker } from "../../../Store.js"
25
+ import { type FilterArgs, getContextMap, type PersistenceModelType, type StoreConfig, storeId, StoreMaker } from "../../../Store.js"
25
26
  import type { FieldValues } from "../../filter/types.js"
26
27
  import * as Q from "../../query.js"
27
- import type { ChangeFeed, Repository } from "../service.js"
28
+ import type { ChangeFeed, ChangeFeedEvent, Repository } from "../service.js"
28
29
  import { ValidationError, ValidationResult } from "../validation.js"
29
30
 
30
31
  const dedupe = Array.dedupeWith(Equivalence.String)
@@ -108,22 +109,48 @@ export function makeRepoInternal<
108
109
  ? args.publishEvents
109
110
  : () => Effect.void
110
111
 
111
- type ChangeFeedEvt = [T[], "save" | "remove"]
112
- type ChangeFeedHandler = (evt: ChangeFeedEvt) => Effect.Effect<void>
113
- const changeFeedHandlers = yield* Ref.make(HashSet.empty<ChangeFeedHandler>())
112
+ type ChangeFeedHandler = (evt: ChangeFeedEvent<T>) => Effect.Effect<void>
113
+ const changeFeedByNamespace = yield* Ref.make(HashMap.empty<string, HashSet.HashSet<ChangeFeedHandler>>())
114
+ const changeFeedWildcard = yield* Ref.make(HashSet.empty<ChangeFeedHandler>())
114
115
  // clear all handlers when the repository's scope closes
115
- yield* Effect.addFinalizer(() => Ref.set(changeFeedHandlers, HashSet.empty<ChangeFeedHandler>()))
116
+ yield* Effect.addFinalizer(() =>
117
+ Effect.all([
118
+ Ref.set(changeFeedByNamespace, HashMap.empty<string, HashSet.HashSet<ChangeFeedHandler>>()),
119
+ Ref.set(changeFeedWildcard, HashSet.empty<ChangeFeedHandler>())
120
+ ], { discard: true })
121
+ )
116
122
  const changeFeed: ChangeFeed<T> = {
117
- publish: (evt) =>
118
- Effect.flatMap(
119
- Ref.get(changeFeedHandlers),
120
- (hs) => Effect.forEach(hs, (h) => h(evt), { concurrency: "unbounded", discard: true })
121
- ),
122
- subscribe: (handler) =>
123
- Effect.acquireRelease(
124
- Ref.update(changeFeedHandlers, HashSet.add(handler)),
125
- () => Ref.update(changeFeedHandlers, HashSet.remove(handler))
123
+ publish: ([items, op]) =>
124
+ Effect.gen(function*() {
125
+ const ns = yield* storeId
126
+ const evt: ChangeFeedEvent<T> = [items, op, ns]
127
+ const map = yield* Ref.get(changeFeedByNamespace)
128
+ const wild = yield* Ref.get(changeFeedWildcard)
129
+ const targeted = Option.getOrElse(HashMap.get(map, ns), () => HashSet.empty<ChangeFeedHandler>())
130
+ const all = HashSet.union(targeted, wild)
131
+ yield* Effect.forEach(all, (h) => h(evt), { concurrency: "unbounded", discard: true })
132
+ }),
133
+ subscribe: (handler, options) => {
134
+ const ns = options?.namespace
135
+ if (ns === undefined) {
136
+ return Effect.acquireRelease(
137
+ Ref.update(changeFeedWildcard, HashSet.add(handler)),
138
+ () => Ref.update(changeFeedWildcard, HashSet.remove(handler))
139
+ )
140
+ }
141
+ return Effect.acquireRelease(
142
+ Ref.update(changeFeedByNamespace, (m) => {
143
+ const cur = Option.getOrElse(HashMap.get(m, ns), () => HashSet.empty<ChangeFeedHandler>())
144
+ return HashMap.set(m, ns, HashSet.add(cur, handler))
145
+ }),
146
+ () =>
147
+ Ref.update(changeFeedByNamespace, (m) => {
148
+ const cur = Option.getOrElse(HashMap.get(m, ns), () => HashSet.empty<ChangeFeedHandler>())
149
+ const next = HashSet.remove(cur, handler)
150
+ return HashSet.size(next) === 0 ? HashMap.remove(m, ns) : HashMap.set(m, ns, next)
151
+ })
126
152
  )
153
+ }
127
154
  }
128
155
 
129
156
  const allE = cms
@@ -10,19 +10,30 @@ import type { QAll, Query, QueryProjection, RawQuery } from "../query.js"
10
10
  import type { Mapped } from "./legacy.js"
11
11
  import type { ValidationResult } from "./validation.js"
12
12
 
13
+ /**
14
+ * Event emitted by a repository's ChangeFeed.
15
+ *
16
+ * Tuple shape: `[items, op, namespace]` — `namespace` is the `storeId`
17
+ * context value at publish time (defaults to `"primary"`).
18
+ */
19
+ export type ChangeFeedEvent<T> = readonly [items: T[], op: "save" | "remove", namespace: string]
20
+
13
21
  /**
14
22
  * Synchronous broadcast channel for repository change events.
15
23
  *
16
- * `publish` only completes after every currently-subscribed handler's Effect
17
- * has finished — callers must await all handlers before continuing. Handlers
18
- * registered via `subscribe` are auto-removed when the subscriber's Scope
19
- * closes, and the full handler set is cleared when the repository's own scope
20
- * closes.
24
+ * `publish` only completes after every matching handler's Effect has
25
+ * finished — callers must await all handlers before continuing. Subscribers
26
+ * can target a single namespace via `options.namespace`, or omit it to
27
+ * receive events from every namespace (wildcard).
28
+ *
29
+ * Handlers are auto-removed when the subscriber's Scope closes; the full
30
+ * handler set is cleared when the repository's own scope closes.
21
31
  */
22
32
  export interface ChangeFeed<T> {
23
33
  readonly publish: (evt: [T[], "save" | "remove"]) => Effect.Effect<void>
24
34
  readonly subscribe: (
25
- handler: (evt: [T[], "save" | "remove"]) => Effect.Effect<void>
35
+ handler: (evt: ChangeFeedEvent<T>) => Effect.Effect<void>,
36
+ options?: { readonly namespace?: string }
26
37
  ) => Effect.Effect<void, never, Scope.Scope>
27
38
  }
28
39
 
@@ -21,6 +21,9 @@ import { withDefaultMake, type WithDefaults } from "./ext.js"
21
21
  import { type B } from "./schema.js"
22
22
  import type { NonEmptyString255Brand, NonEmptyStringBrand } from "./strings.js"
23
23
 
24
+ type BrandedStringSchema<A extends string> = S.Codec<A, string> & WithDefaults<S.Codec<A, string>>
25
+ type ConstructorDefaultBaseSchema<A> = S.Codec<A, string> & S.WithoutConstructorDefault
26
+ type WithConstructorDefaultSchema<A> = S.withConstructorDefault<ConstructorDefaultBaseSchema<A>>
24
27
  const nonEmptyString = S.NonEmptyString
25
28
 
26
29
  /**
@@ -36,7 +39,8 @@ export type NonEmptyString50 = string & NonEmptyString50Brand
36
39
  /**
37
40
  * A string that is at least 1 character long and a maximum of 50.
38
41
  */
39
- export const NonEmptyString50 = nonEmptyString.pipe(
42
+ export interface NonEmptyString50Schema extends BrandedStringSchema<NonEmptyString50> {}
43
+ export const NonEmptyString50: NonEmptyString50Schema = nonEmptyString.pipe(
40
44
  S.check(S.isMaxLength(50)),
41
45
  fromBrand<NonEmptyString50>(nominal<NonEmptyString50>(), {
42
46
  identifier: "NonEmptyString50",
@@ -58,7 +62,8 @@ export type NonEmptyString64 = string & NonEmptyString64Brand
58
62
  /**
59
63
  * A string that is at least 1 character long and a maximum of 64.
60
64
  */
61
- export const NonEmptyString64 = nonEmptyString.pipe(
65
+ export interface NonEmptyString64Schema extends BrandedStringSchema<NonEmptyString64> {}
66
+ export const NonEmptyString64: NonEmptyString64Schema = nonEmptyString.pipe(
62
67
  S.check(S.isMaxLength(64)),
63
68
  fromBrand<NonEmptyString64>(nominal<NonEmptyString64>(), {
64
69
  identifier: "NonEmptyString64",
@@ -81,7 +86,8 @@ export type NonEmptyString80 = string & NonEmptyString80Brand
81
86
  * A string that is at least 1 character long and a maximum of 80.
82
87
  */
83
88
 
84
- export const NonEmptyString80 = nonEmptyString.pipe(
89
+ export interface NonEmptyString80Schema extends BrandedStringSchema<NonEmptyString80> {}
90
+ export const NonEmptyString80: NonEmptyString80Schema = nonEmptyString.pipe(
85
91
  S.check(S.isMaxLength(80)),
86
92
  fromBrand<NonEmptyString80>(nominal<NonEmptyString80>(), {
87
93
  identifier: "NonEmptyString80",
@@ -103,7 +109,8 @@ export type NonEmptyString100 = string & NonEmptyString100Brand
103
109
  /**
104
110
  * A string that is at least 1 character long and a maximum of 100.
105
111
  */
106
- export const NonEmptyString100 = nonEmptyString.pipe(
112
+ export interface NonEmptyString100Schema extends BrandedStringSchema<NonEmptyString100> {}
113
+ export const NonEmptyString100: NonEmptyString100Schema = nonEmptyString.pipe(
107
114
  S.check(S.isMaxLength(100)),
108
115
  fromBrand<NonEmptyString100>(nominal<NonEmptyString100>(), {
109
116
  identifier: "NonEmptyString100",
@@ -125,7 +132,8 @@ export type Min3String255 = string & Min3String255Brand
125
132
  /**
126
133
  * A string that is at least 3 character long and a maximum of 255.
127
134
  */
128
- export const Min3String255 = pipe(
135
+ export interface Min3String255Schema extends BrandedStringSchema<Min3String255> {}
136
+ export const Min3String255: Min3String255Schema = pipe(
129
137
  S.String,
130
138
  S.check(S.isMinLength(3), S.isMaxLength(255)),
131
139
  fromBrand<Min3String255>(nominal<Min3String255>(), {
@@ -145,12 +153,22 @@ export interface StringIdBrand extends Simplify<B.Brand<"StringId"> & NonEmptySt
145
153
  */
146
154
  export type StringId = string & StringIdBrand
147
155
 
148
- const makeStringId = (s?: string): StringId =>
149
- s !== undefined ? S.decodeSync(StringId)(s) : nanoid() as unknown as StringId
150
156
  const minLength = 6
151
157
  const maxLength = 50
152
158
  const size = 21
153
159
  const length = 10 * size
160
+ /** Base `StringId` codec (without constructor default extensions). */
161
+ const StringIdSchemaBase = pipe(
162
+ S.String,
163
+ S.check(S.isMinLength(minLength), S.isMaxLength(maxLength)),
164
+ fromBrand<StringId>(nominal<StringId>(), {
165
+ identifier: "StringId",
166
+ toArbitrary: () => (fc) => StringIdArb()(fc),
167
+ jsonSchema: {}
168
+ })
169
+ )
170
+ const makeStringId = (s?: string): StringId =>
171
+ s !== undefined ? S.decodeSync(StringIdSchemaBase)(s) : nanoid() as unknown as StringId
154
172
  const StringIdArb = (): S.LazyArbitrary<StringId> => (fc) =>
155
173
  fc
156
174
  .uint8Array({ minLength: length, maxLength: length })
@@ -161,16 +179,12 @@ const StringIdArb = (): S.LazyArbitrary<StringId> => (fc) =>
161
179
  * `.withConstructorDefault` => fresh `nanoid()` (construction-only; not
162
180
  * applied during decode — see file-level note).
163
181
  */
164
- export const StringId = extendM(
165
- pipe(
166
- S.String,
167
- S.check(S.isMinLength(minLength), S.isMaxLength(maxLength)),
168
- fromBrand<StringId>(nominal<StringId>(), {
169
- identifier: "StringId",
170
- toArbitrary: () => (fc) => StringIdArb()(fc),
171
- jsonSchema: {}
172
- })
173
- ),
182
+ export interface StringIdSchema extends BrandedStringSchema<StringId> {
183
+ readonly make: (s?: string) => StringId
184
+ readonly withConstructorDefault: WithConstructorDefaultSchema<StringId>
185
+ }
186
+ export const StringId: StringIdSchema = extendM(
187
+ StringIdSchemaBase,
174
188
  (s) => ({
175
189
  make: makeStringId,
176
190
  /**
@@ -209,7 +223,7 @@ export function prefixedStringId<Type extends StringId>() {
209
223
  (x) => (pref + x.substring(0, 50 - pref.length)) as Type
210
224
  )
211
225
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
212
- const s = StringId
226
+ const s = StringIdSchemaBase
213
227
  .pipe(
214
228
  S.refine((x: string): x is Type => x.startsWith(pref), {
215
229
  identifier: name
@@ -241,7 +255,7 @@ export function prefixedStringId<Type extends StringId>() {
241
255
  * file-level note.
242
256
  */
243
257
  withConstructorDefault: schema.pipe(
244
- S.withConstructorDefault<S.Codec<Type, string> & S.WithoutConstructorDefault>(
258
+ S.withConstructorDefault<ConstructorDefaultBaseSchema<Type>>(
245
259
  Effect.sync(make)
246
260
  )
247
261
  )
@@ -258,18 +272,9 @@ export function prefixedStringId<Type extends StringId>() {
258
272
  */
259
273
  export const brandedStringId = <
260
274
  Id
261
- >() =>
275
+ >(): BrandedStringIdSchema<Id> =>
262
276
  withDefaultMake(
263
- Object.assign(Object.create(StringId), StringId) as S.Codec<Id, string> & {
264
- /**
265
- * Construction-only default: fresh `nanoid()`-shaped id. Applied only
266
- * when the field is omitted from `.make(...)` input. NOT applied
267
- * during decode — cannot be used to JIT-migrate database fields. See
268
- * file-level note.
269
- */
270
- withConstructorDefault: S.withConstructorDefault<S.Codec<Id, string> & S.WithoutConstructorDefault>
271
- make: () => Id
272
- } & WithDefaults<S.Codec<Id, string>>
277
+ Object.assign(Object.create(StringId), StringId) as BrandedStringIdSchema<Id>
273
278
  )
274
279
 
275
280
  export interface PrefixedStringUtils<
@@ -286,7 +291,12 @@ export interface PrefixedStringUtils<
286
291
  * field is omitted from `.make(...)` input. NOT applied during decode —
287
292
  * cannot be used to JIT-migrate database fields. See file-level note.
288
293
  */
289
- readonly withConstructorDefault: S.withConstructorDefault<S.Codec<Type, string> & S.WithoutConstructorDefault>
294
+ readonly withConstructorDefault: WithConstructorDefaultSchema<Type>
295
+ }
296
+
297
+ export interface BrandedStringIdSchema<Id> extends S.Codec<Id, string>, WithDefaults<S.Codec<Id, string>> {
298
+ readonly withConstructorDefault: WithConstructorDefaultSchema<Id>
299
+ readonly make: () => Id
290
300
  }
291
301
 
292
302
  export interface UrlBrand extends Simplify<B.Brand<"Url"> & NonEmptyStringBrand> {}
@@ -297,7 +307,8 @@ const isUrl: Refinement<string, Url> = (s: string): s is Url => {
297
307
  return validator.default.isURL(s, { require_tld: false })
298
308
  }
299
309
 
300
- export const Url = S
310
+ export interface UrlSchema extends BrandedStringSchema<Url> {}
311
+ export const Url: UrlSchema = S
301
312
  .String
302
313
  .pipe(
303
314
  S.annotate({
@@ -13,14 +13,20 @@ import * as Effect from "effect/Effect"
13
13
  import * as S from "effect/Schema"
14
14
  import type { Simplify } from "effect/Types"
15
15
  import { fromBrand, nominal } from "./brand.js"
16
- import { withDefaultMake } from "./ext.js"
16
+ import { withDefaultMake, type WithDefaults } from "./ext.js"
17
17
  import { type B } from "./schema.js"
18
18
 
19
+ type BrandedNumberSchema<A extends number> = S.Codec<A, number> & WithDefaults<S.Codec<A, number>>
20
+ type BrandedNumberSchemaWithConstructorDefault<A extends number> = BrandedNumberSchema<A> & {
21
+ readonly withConstructorDefault: S.Codec<A, number>
22
+ }
23
+
19
24
  export interface PositiveIntBrand
20
25
  extends Simplify<B.Brand<"PositiveInt"> & NonNegativeIntBrand & PositiveNumberBrand>
21
26
  {}
22
27
  /** Positive integer. `.withConstructorDefault` => `1` (construction-only). */
23
- export const PositiveInt = extendM(
28
+ export interface PositiveIntSchema extends BrandedNumberSchemaWithConstructorDefault<PositiveInt> {}
29
+ export const PositiveInt: PositiveIntSchema = extendM(
24
30
  S.Int.pipe(
25
31
  S.check(S.isGreaterThan(0)),
26
32
  fromBrand<PositiveInt>(nominal<PositiveInt>(), { identifier: "PositiveInt", jsonSchema: {} }),
@@ -39,7 +45,8 @@ export type PositiveInt = number & PositiveIntBrand
39
45
 
40
46
  export interface NonNegativeIntBrand extends Simplify<B.Brand<"NonNegativeInt"> & IntBrand & NonNegativeNumberBrand> {}
41
47
  /** Non-negative integer. `.withConstructorDefault` => `0` (construction-only). */
42
- export const NonNegativeInt = extendM(
48
+ export interface NonNegativeIntSchema extends BrandedNumberSchemaWithConstructorDefault<NonNegativeInt> {}
49
+ export const NonNegativeInt: NonNegativeIntSchema = extendM(
43
50
  S.Int.pipe(
44
51
  S.check(S.isGreaterThanOrEqualTo(0)),
45
52
  fromBrand<NonNegativeInt>(nominal<NonNegativeInt>(), {
@@ -61,7 +68,8 @@ export type NonNegativeInt = number & NonNegativeIntBrand
61
68
 
62
69
  export interface IntBrand extends Simplify<B.Brand<"Int">> {}
63
70
  /** Integer. `.withConstructorDefault` => `0` (construction-only). */
64
- export const Int = extendM(
71
+ export interface IntSchema extends BrandedNumberSchemaWithConstructorDefault<Int> {}
72
+ export const Int: IntSchema = extendM(
65
73
  S.Int.pipe(fromBrand<Int>(nominal<Int>(), { identifier: "Int", jsonSchema: {} }), withDefaultMake),
66
74
  (s) => ({
67
75
  /**
@@ -76,7 +84,8 @@ export type Int = number & IntBrand
76
84
 
77
85
  export interface PositiveNumberBrand extends Simplify<B.Brand<"PositiveNumber"> & NonNegativeNumberBrand> {}
78
86
  /** Positive finite number. `.withConstructorDefault` => `1` (construction-only). */
79
- export const PositiveNumber = extendM(
87
+ export interface PositiveNumberSchema extends BrandedNumberSchemaWithConstructorDefault<PositiveNumber> {}
88
+ export const PositiveNumber: PositiveNumberSchema = extendM(
80
89
  S.Finite.pipe(
81
90
  S.check(S.isGreaterThan(0)),
82
91
  fromBrand<PositiveNumber>(nominal<PositiveNumber>(), {
@@ -98,7 +107,8 @@ export type PositiveNumber = number & PositiveNumberBrand
98
107
 
99
108
  export interface NonNegativeNumberBrand extends Simplify<B.Brand<"NonNegativeNumber">> {}
100
109
  /** Non-negative finite number. `.withConstructorDefault` => `0` (construction-only). */
101
- export const NonNegativeNumber = extendM(
110
+ export interface NonNegativeNumberSchema extends BrandedNumberSchemaWithConstructorDefault<NonNegativeNumber> {}
111
+ export const NonNegativeNumber: NonNegativeNumberSchema = extendM(
102
112
  S
103
113
  .Finite
104
114
  .pipe(
@@ -2,11 +2,14 @@ import type * as B from "effect/Brand"
2
2
  import * as S from "effect/Schema"
3
3
  import type { Simplify } from "effect/Types"
4
4
  import { fromBrand, nominal } from "./brand.js"
5
- import { withDefaultMake } from "./ext.js"
5
+ import { withDefaultMake, type WithDefaults } from "./ext.js"
6
+
7
+ type BrandedStringSchema<A extends string> = S.Codec<A, string> & WithDefaults<S.Codec<A, string>>
6
8
 
7
9
  export type NonEmptyStringBrand = B.Brand<"NonEmptyString">
8
10
  export type NonEmptyString = string & NonEmptyStringBrand
9
- export const NonEmptyString = S
11
+ export interface NonEmptyStringSchema extends BrandedStringSchema<NonEmptyString> {}
12
+ export const NonEmptyString: NonEmptyStringSchema = S
10
13
  .NonEmptyString
11
14
  .pipe(
12
15
  fromBrand<NonEmptyString>(nominal<NonEmptyString>(), {
@@ -18,7 +21,8 @@ export const NonEmptyString = S
18
21
 
19
22
  export interface NonEmptyString64kBrand extends Simplify<B.Brand<"NonEmptyString64k"> & NonEmptyStringBrand> {}
20
23
  export type NonEmptyString64k = string & NonEmptyString64kBrand
21
- export const NonEmptyString64k = S
24
+ export interface NonEmptyString64kSchema extends BrandedStringSchema<NonEmptyString64k> {}
25
+ export const NonEmptyString64k: NonEmptyString64kSchema = S
22
26
  .NonEmptyString
23
27
  .pipe(
24
28
  S.check(S.isMaxLength(64 * 1024)),
@@ -31,7 +35,8 @@ export const NonEmptyString64k = S
31
35
 
32
36
  export interface NonEmptyString2kBrand extends Simplify<B.Brand<"NonEmptyString2k"> & NonEmptyString64kBrand> {}
33
37
  export type NonEmptyString2k = string & NonEmptyString2kBrand
34
- export const NonEmptyString2k = S
38
+ export interface NonEmptyString2kSchema extends BrandedStringSchema<NonEmptyString2k> {}
39
+ export const NonEmptyString2k: NonEmptyString2kSchema = S
35
40
  .NonEmptyString
36
41
  .pipe(
37
42
  S.check(S.isMaxLength(2 * 1024)),
@@ -44,7 +49,8 @@ export const NonEmptyString2k = S
44
49
 
45
50
  export interface NonEmptyString255Brand extends Simplify<B.Brand<"NonEmptyString255"> & NonEmptyString2kBrand> {}
46
51
  export type NonEmptyString255 = string & NonEmptyString255Brand
47
- export const NonEmptyString255 = S
52
+ export interface NonEmptyString255Schema extends BrandedStringSchema<NonEmptyString255> {}
53
+ export const NonEmptyString255: NonEmptyString255Schema = S
48
54
  .NonEmptyString
49
55
  .pipe(
50
56
  S.check(S.isMaxLength(255)),