| 219 | * note. |
| 220 | */ |
| 221 | export function prefixedStringId<Type extends StringId>() { |
| 222 | return <Prefix extends string, Separator extends string = "-">( |
| 223 | prefix: Prefix, |
| 224 | name: string, |
| 225 | separator?: Separator |
| 226 | ) => { |
| 227 | type FullPrefix = `${Prefix}${Separator}` |
| 228 | const pref = `${prefix}${separator ?? "-"}` as FullPrefix |
| 229 | const arb = (): S.Arbitrary<Type> => (fc) => |
| 230 | StringIdArb()(fc).map( |
| 231 | (x) => (pref + x.substring(0, 50 - pref.length)) as Type |
| 232 | ) |
| 233 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment |
| 234 | const s = StringIdSchemaBase |
| 235 | .pipe( |
| 236 | S.refine((x: string): x is Type => x.startsWith(pref), { |
| 237 | identifier: name |
| 238 | }), |
| 239 | S.annotate({ |
| 240 | toArbitrary: () => (fc) => arb()(fc) |
| 241 | }) |
| 242 | ) |
| 243 | const schema = s.pipe(withDefaultMake) |
| 244 | const make = () => (pref + StringId.make().substring(0, 50 - pref.length)) as Type |
| 245 | |
| 246 | return extendM( |
| 247 | schema, |
| 248 | (ex): PrefixedStringUtils<Type, Prefix, Separator> => ({ |
| 249 | make, |
| 250 | /** |
| 251 | * Automatically adds the prefix. |
| 252 | */ |
| 253 | unsafeFrom: (str: string) => ex(pref + str), |
| 254 | /** |
| 255 | * Must provide a literal string starting with prefix. |
| 256 | */ |
| 257 | prefixSafe: <REST extends string>(str: `${Prefix}${Separator}${REST}`) => ex(str), |
| 258 | prefix, |
| 259 | /** |
| 260 | * Construction-only default: fresh prefixed id. Applied only when |
| 261 | * the field is omitted from `.make(...)` input. NOT applied during |
| 262 | * decode — cannot be used to JIT-migrate database fields. See |
| 263 | * file-level note. |
| 264 | */ |
| 265 | withConstructorDefault: schema.pipe( |
| 266 | S.withConstructorDefault<S.Codec<Type, string> & S.WithoutConstructorDefault>( |
| 267 | Effect.sync(make) |
| 268 | ) |
| 269 | ) |
| 270 | }) |
| 271 | ) |
| 272 | } |
| 273 | } |
| 274 | |
| 275 | /** |
| 276 | * Build a branded `StringId` schema for the given branded `Id` type. |