@afora/core 0.0.4 → 0.0.6

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.
Files changed (36) hide show
  1. package/README.md +2 -0
  2. package/dist/core/application/exceptions.d.ts +9 -0
  3. package/dist/core/application/providers.d.ts +46 -0
  4. package/dist/core/application/types/application.interface.d.ts +70 -15
  5. package/dist/core/blueprint/types/slice.interface.d.ts +5 -2
  6. package/dist/core/entity/abstract-entity.d.ts +1 -1
  7. package/dist/core/entity/types/entity.interface.d.ts +13 -7
  8. package/dist/core/exception/exception.d.ts +10 -1
  9. package/dist/core/output/status.d.ts +2 -2
  10. package/dist/core/property/property.d.ts +4 -4
  11. package/dist/core/property/types/optional-property.type.d.ts +2 -2
  12. package/dist/core/property/types/property-type.interface.d.ts +17 -0
  13. package/dist/core/record/abstract-record.d.ts +4 -4
  14. package/dist/core/record/exceptions.d.ts +2 -0
  15. package/dist/core/record/name.d.ts +16 -0
  16. package/dist/core/record/record.d.ts +4 -3
  17. package/dist/core/record/types/plugin.interface.d.ts +11 -0
  18. package/dist/core/record/types/record.interface.d.ts +71 -10
  19. package/dist/core/repository/cursor.d.ts +13 -0
  20. package/dist/core/repository/exceptions.d.ts +10 -0
  21. package/dist/core/repository/query.d.ts +4 -4
  22. package/dist/core/repository/repository.d.ts +1 -1
  23. package/dist/core/repository/state.d.ts +3 -2
  24. package/dist/core/repository/types/query.interface.d.ts +37 -0
  25. package/dist/core/repository/types/repository.interface.d.ts +10 -6
  26. package/dist/core/use-case/steps/types/steps.interface.d.ts +1 -1
  27. package/dist/experimental.js +3 -2
  28. package/dist/{index-hn82hm31.js → index-3nd9bjnc.js} +69 -10
  29. package/dist/{index-ewm6r4nw.js → index-gbgc40xe.js} +452 -96
  30. package/dist/index.d.ts +9 -7
  31. package/dist/index.js +16 -2
  32. package/dist/std/properties/password.d.ts +5 -1
  33. package/dist/std/record-plugins.d.ts +5 -5
  34. package/dist/testing/index.js +91 -2
  35. package/dist/testing/provider-contract.d.ts +5 -0
  36. package/package.json +1 -1
package/README.md CHANGED
@@ -335,6 +335,8 @@ Where the others win: Zod and Valibot are faster, have far larger ecosystems and
335
335
 
336
336
  ## Contributing
337
337
 
338
+ Issues open from a template: bug report, feature request, or feedback from building on afora. Questions go to [Discussions](https://github.com/afora-framework/core/discussions). Pull requests follow [the PR template](.github/PULL_REQUEST_TEMPLATE.md).
339
+
338
340
  Commits follow [Conventional Commits](https://www.conventionalcommits.org) (`feat(repository): …`, `fix: …`), checked by commitlint in a Husky `commit-msg` hook; the `pre-commit` hook runs Biome and Knip. CI runs lint, Knip, type checks, unit and interop tests, the build and the Node smoke test on every push and pull request. Pushing a `v*` tag that matches `package.json` publishes to npm with provenance.
339
341
 
340
342
  Scripts:
@@ -40,6 +40,15 @@ export declare const ApplicationNotStarted: import("../../index.js").DeclaredExc
40
40
  /** The application, or none when `useApplication()` found none started. */
41
41
  readonly application?: string;
42
42
  }>;
43
+ /**
44
+ * A repository read or wrote through a provider of `providersOf` made by
45
+ * `providerFrom` outside an application: an application that registers the
46
+ * storage makes it on start, and its bound repositories use it.
47
+ */
48
+ export declare const ProviderNotBound: import("../../index.js").DeclaredExceptionClass<{
49
+ /** The key of the provider in its storage. */
50
+ readonly provider: string;
51
+ }>;
43
52
  /**
44
53
  * `app.start()` was called while another application is the started one of
45
54
  * the process; `start({ replace: true })` stops it and takes its place.
@@ -0,0 +1,46 @@
1
+ import type { IRepositoryProvider } from "../repository/types/repository.interface.js";
2
+ import { Kind } from "../symbols/kind.js";
3
+ import { type ProviderFactory } from "./start.js";
4
+ /**
5
+ * One provider of a storage made by `providersOf`, e.g. `Providers.audit`:
6
+ * what a repository (`repositoryOf({ provider: Providers.audit })`) or an
7
+ * application (`provider: Providers.main`) names. Each application that
8
+ * registers the storage reads and writes through its own instance of it.
9
+ */
10
+ export type ProviderSlot<Key extends string = string> = IRepositoryProvider & {
11
+ readonly [Kind]: "provider-slot";
12
+ /** Its key in the storage. */
13
+ readonly key: Key;
14
+ };
15
+ /** The providers of `providersOf`, by key. */
16
+ export type ProviderStorage<Keys extends string = string> = {
17
+ readonly [Key in Keys]: ProviderSlot<Key>;
18
+ } & {
19
+ readonly [Kind]: "provider-storage";
20
+ };
21
+ /** What a slot stands for. */
22
+ interface SlotEntry {
23
+ readonly key: string;
24
+ readonly storage: object;
25
+ readonly value: IRepositoryProvider | ProviderFactory;
26
+ }
27
+ /**
28
+ * Named providers, declared once in a module of their own so that
29
+ * repositories can name one before any application exists, e.g.
30
+ * `const Providers = providersOf({ main: providerFrom(Database, ...), audit: auditProvider() })`,
31
+ * then `repositoryOf({ ..., provider: Providers.audit })` and
32
+ * `applicationOf({ providers: Providers, provider: Providers.main, ... })`.
33
+ * Each value is a provider or a factory of `providerFrom`; an application
34
+ * that registers the storage makes each factory on `start()`, its own
35
+ * instance (an application that extends it shares them).
36
+ */
37
+ export declare function providersOf<const Entries extends Readonly<Record<string, IRepositoryProvider | ProviderFactory>>>(entries: Entries): ProviderStorage<keyof Entries & string>;
38
+ /** Whether `value` is a provider of a storage made by `providersOf`. */
39
+ export declare function isProviderSlot(value: unknown): value is ProviderSlot;
40
+ /** Whether `value` is a storage made by `providersOf`. */
41
+ export declare function isProviderStorage(value: unknown): value is ProviderStorage;
42
+ /** The slots of `storage`, in the order of its keys. @internal */
43
+ export declare function slotsOf(storage: ProviderStorage): readonly ProviderSlot[];
44
+ /** What `slot` stands for. @internal */
45
+ export declare function slotEntry(slot: ProviderSlot): SlotEntry;
46
+ export {};
@@ -1,3 +1,4 @@
1
+ import type { AnyBlueprint, BlueprintInput, IBlueprint } from "../../blueprint/types/blueprint.interface.js";
1
2
  import type { AnyCommand } from "../../command/types/command.interface.js";
2
3
  import type { AnyEnvironment, EnvironmentValue } from "../../environment/environment.js";
3
4
  import type { ExceptionLayers } from "../../exception/enums/exception-layers.js";
@@ -8,9 +9,32 @@ import type { ProviderDescription } from "../../repository/types/query.interface
8
9
  import type { AnyRepository, IRepositoryProvider } from "../../repository/types/repository.interface.js";
9
10
  import type { Kind } from "../../symbols/kind.js";
10
11
  import type { UseCaseStepDescription } from "../../use-case/steps/types/steps.interface.js";
11
- import type { AnyExceptionClass, AnyUseCaseClass } from "../../use-case/use-case.js";
12
+ import type { AnyExceptionClass, AnyUseCaseClass, UseCaseInput, UseCaseResponse } from "../../use-case/use-case.js";
13
+ import type { ProviderSlot, ProviderStorage } from "../providers.js";
12
14
  import type { LogFactory, ProviderFactory } from "../start.js";
13
15
  type Named<Value> = Readonly<Record<string, Value>>;
16
+ /**
17
+ * The options of `app.run`: the `context` of the use case, required when it
18
+ * declares one, and `dependencies`, what its `execute` takes to replace them.
19
+ */
20
+ export type ApplicationRunOptions<UseCase extends AnyUseCaseClass> = (UseCase["context"] extends IBlueprint<infer Properties> ? {
21
+ readonly context: BlueprintInput<Properties>;
22
+ } : {
23
+ readonly context?: undefined;
24
+ }) & {
25
+ readonly dependencies?: Parameters<InstanceType<UseCase>["execute"]>[0];
26
+ };
27
+ /**
28
+ * What `app.run` takes after the use case: its input, optional when no key
29
+ * of it is required, and the options, required when it declares a context.
30
+ */
31
+ export type ApplicationRunArgs<UseCase extends AnyUseCaseClass> = UseCase["context"] extends AnyBlueprint ? [input: UseCaseInput<UseCase>, options: ApplicationRunOptions<UseCase>] : {} extends UseCaseInput<UseCase> ? [
32
+ input?: UseCaseInput<UseCase>,
33
+ options?: ApplicationRunOptions<UseCase>
34
+ ] : [
35
+ input: UseCaseInput<UseCase>,
36
+ options?: ApplicationRunOptions<UseCase>
37
+ ];
14
38
  /** The declarations of an application, by name, one collection for each kind. */
15
39
  export interface ApplicationCollections {
16
40
  readonly properties: Named<unknown>;
@@ -53,9 +77,18 @@ export interface ApplicationSettings<Name extends string, Extended extends reado
53
77
  * The provider of the repositories that did not declare one; the
54
78
  * repositories of the application read and write through it. With
55
79
  * `providerFrom`, it is made by `app.start()` from the variables of its
56
- * environments, and calls before that throw `ApplicationNotStarted`.
80
+ * environments, and calls before that throw `ApplicationNotStarted`. A
81
+ * provider of `providers` (`Providers.main`) names the instance this
82
+ * application makes of it.
57
83
  */
58
84
  readonly provider?: IRepositoryProvider | ProviderFactory;
85
+ /**
86
+ * Named providers made by `providersOf`, which repositories name
87
+ * (`repositoryOf({ provider: Providers.audit })`). The application makes
88
+ * its own instance of each `providerFrom` on `start()` and binds the
89
+ * repositories to it; an application that extends it shares them.
90
+ */
91
+ readonly providers?: ProviderStorage;
59
92
  /**
60
93
  * How it logs: the providers its lines go to and the lowest level kept.
61
94
  * Use cases, commands and repository calls of the application are logged
@@ -105,25 +138,29 @@ export interface IApplication<Name extends string, Collections extends Applicati
105
138
  readonly entities: Collections["entities"];
106
139
  readonly environments: Collections["environments"];
107
140
  /**
108
- * Its repositories, inherited ones included. With a `provider` they are
109
- * bound to it: they share the declaration of the original but read and
110
- * write through that provider (unless they declared their own).
141
+ * Its repositories, inherited ones included. With a `provider` (or
142
+ * `providers`) they are bound to it: they share the declaration of the
143
+ * original but read and write through that provider (unless they declared
144
+ * their own).
111
145
  */
112
146
  readonly repositories: Collections["repositories"];
113
147
  /**
114
- * Its use cases. With a `provider` they are subclasses whose repositories
115
- * are the bound ones, through child use cases too; the same subclass every
116
- * time. Without one they are the declared classes.
148
+ * Its use cases. With a `provider`, `providers` or `log` they are
149
+ * subclasses whose repositories are the bound ones, through child use
150
+ * cases too; the same subclass every time. Without any of them they are
151
+ * the declared classes.
117
152
  */
118
153
  readonly useCases: Collections["useCases"];
119
154
  /**
120
- * Its commands. With a `provider` they are rebuilt over the bound use
121
- * cases (same settings), memoized; without one, the declared commands.
155
+ * Its commands. With a `provider`, `providers` or `log` they are rebuilt
156
+ * over the bound use cases (same settings), memoized; without any of them,
157
+ * the declared commands.
122
158
  */
123
159
  readonly commands: Collections["commands"];
124
160
  /**
125
- * Its outputs. With a `provider` they are rebuilt over the bound commands
126
- * (same name, statuses and stages), memoized; without one, the declared outputs.
161
+ * Its outputs. With a `provider`, `providers` or `log` they are rebuilt
162
+ * over the bound commands (same name, statuses and stages), memoized;
163
+ * without any of them, the declared outputs.
127
164
  */
128
165
  readonly outputs: Collections["outputs"];
129
166
  /** Its exceptions. */
@@ -132,6 +169,13 @@ export interface IApplication<Name extends string, Collections extends Applicati
132
169
  repository<Repository extends AnyRepository>(repository: Repository): Repository;
133
170
  /** The bound version of a use case registered in the application. */
134
171
  useCase<UseCase extends AnyUseCaseClass>(useCase: UseCase): UseCase;
172
+ /**
173
+ * Runs a use case registered in the application over its bound
174
+ * repositories: `new (app.useCase(UseCase))(input, { context }).execute(dependencies)`.
175
+ * It rejects with what the constructor or `execute` throw. The events the
176
+ * use case collected are not handled; a command is what dispatches them.
177
+ */
178
+ run<UseCase extends AnyUseCaseClass>(useCase: UseCase, ...args: ApplicationRunArgs<UseCase>): Promise<UseCaseResponse<UseCase>>;
135
179
  /** The bound version of a command registered in the application. */
136
180
  command<Command extends AnyCommand>(command: Command): Command;
137
181
  /** The bound version of an output registered in the application. */
@@ -163,6 +207,8 @@ export interface IApplication<Name extends string, Collections extends Applicati
163
207
  get<UseCase extends AnyUseCaseClass>(useCase: UseCase): UseCase;
164
208
  /** The bound version of a repository registered in the application. */
165
209
  get<Repository extends AnyRepository>(repository: Repository): Repository;
210
+ /** The instance this application reads and writes through for a provider of its `providers`. */
211
+ get(provider: ProviderSlot): IRepositoryProvider;
166
212
  /** Whether `start()` completed and `stop()` was not called since. */
167
213
  readonly started: boolean;
168
214
  }
@@ -345,13 +391,22 @@ export interface LogDescription {
345
391
  readonly kind: "opaque";
346
392
  })[];
347
393
  }
394
+ /** How `describe` names a provider of `providersOf`: by its key. */
395
+ export interface ProviderReference {
396
+ readonly kind: "ref";
397
+ readonly key: string;
398
+ }
348
399
  /** The graph `describe` gives: every declaration by name, with its origin in `from`. */
349
400
  export interface ApplicationDescription {
350
401
  readonly name: string;
351
402
  /** The names of the applications it extends directly. */
352
403
  readonly extends: readonly string[];
353
404
  /** The provider of the application (or of the last extended one), present only when configured. */
354
- readonly provider?: ProviderDescription;
405
+ readonly provider?: ProviderDescription | ProviderReference;
406
+ /** Its providers of `providersOf` (inherited ones included), by key; present only when it has some. */
407
+ readonly providers?: Readonly<Record<string, ProviderDescription & {
408
+ readonly from: string;
409
+ }>>;
355
410
  /** Its `log` (or the one of the last extended application), present only when configured. */
356
411
  readonly log?: LogDescription | {
357
412
  readonly kind: "opaque";
@@ -378,8 +433,8 @@ export interface ApplicationDescription {
378
433
  readonly relations: readonly string[];
379
434
  /** The names of its `environmentDependencies`. */
380
435
  readonly environments: readonly string[];
381
- /** Its own provider, present only when it declared one. */
382
- readonly provider?: ProviderDescription;
436
+ /** Its own provider, present only when it declared one; a provider of `providersOf` by its key. */
437
+ readonly provider?: ProviderDescription | ProviderReference;
383
438
  }>>;
384
439
  readonly useCases: Readonly<Record<string, UseCaseDescription>>;
385
440
  readonly commands: Readonly<Record<string, CommandDescription>>;
@@ -1,4 +1,4 @@
1
- import type { RecordUniqueKeys } from "../../record/types/plugin.interface.js";
1
+ import type { RecordSensitiveKeys, RecordUniqueKeys } from "../../record/types/plugin.interface.js";
2
2
  import type { RecordConstructor } from "../../record/types/record.interface.js";
3
3
  import type { Meta } from "../../symbols/meta.js";
4
4
  import type { AnyBlueprint, AnyBlueprintList, AnyOptionalRecord, AnyRecordClass, BlueprintData, BlueprintField, IBlueprint, IBlueprintCollection, IBlueprintList, OptionalRecord } from "./blueprint.interface.js";
@@ -37,13 +37,16 @@ type SlicedRecord<Original extends AnyRecordClass, Mask> = SlicedProperties<Orig
37
37
  readonly [Meta]: {
38
38
  readonly blueprint: Original["blueprint"];
39
39
  };
40
- }> : never;
40
+ }, KeptSensitive<Original, keyof Properties>> : never;
41
41
  type KeptChange<Instance, Keys> = Instance extends {
42
42
  readonly change: infer Change;
43
43
  } ? Extract<keyof Change, Keys & string> : never;
44
44
  type KeptUnique<Original, Keys> = Original extends {
45
45
  readonly [RecordUniqueKeys]?: infer Unique;
46
46
  } ? Extract<Unique, Keys & string> : never;
47
+ type KeptSensitive<Original, Keys> = Original extends {
48
+ readonly [RecordSensitiveKeys]?: infer Sensitive;
49
+ } ? Extract<Sensitive, Keys & string> : never;
47
50
  /** The properties of `Blueprint`, read lazily so generic uses stay shallow. */
48
51
  export type PropertiesOf<Blueprint> = Blueprint extends IBlueprint<infer Properties, boolean> ? Properties : never;
49
52
  /** The blueprint of `Properties` that keeps only the keys of `Mask`. */
@@ -17,7 +17,7 @@ export declare const EntityUniqueKeys: unique symbol;
17
17
  * and never changed after, an `updatedAt` set by each change, and a
18
18
  * `deletedAt` set by `destroy`, after which it no longer changes.
19
19
  */
20
- export declare abstract class AbstractEntity<Properties extends IBlueprintCollection> extends AbstractRecord<Properties> {
20
+ export declare abstract class AbstractEntity<Properties extends IBlueprintCollection, Sensitive extends string = never> extends AbstractRecord<Properties, Sensitive> {
21
21
  constructor(value: BlueprintInput<Properties>);
22
22
  /** Queues an event for this entity without running its handler. */
23
23
  protected emit(event: EventDefinition): void;
@@ -1,6 +1,6 @@
1
1
  import type { BlueprintField, BlueprintInput, BlueprintValue, IBlueprint, IBlueprintCollection, JoinedProperties } from "../../blueprint/types/blueprint.interface.js";
2
2
  import type { ParseResult } from "../../parse/types/parse.interface.js";
3
- import type { AnyRecordPlugin, IRecordPluginCatalog, PluginMembers, PluginUniqueKey, WithPluginProperties } from "../../record/types/plugin.interface.js";
3
+ import type { AnyRecordPlugin, IRecordPluginCatalog, PluginMembers, PluginSensitiveKey, PluginUniqueKey, WithPluginProperties } from "../../record/types/plugin.interface.js";
4
4
  import type { MarkedUniqueKey, RecordChanges, RecordKey, RecordOptions, RecordSettings } from "../../record/types/record.interface.js";
5
5
  import type { StandardSchemaV1 } from "../../standard/standard-schema.js";
6
6
  import type { Kind } from "../../symbols/kind.js";
@@ -45,32 +45,38 @@ export type EntityProperties<Identity extends IEntityIdentity, Properties extend
45
45
  */
46
46
  export type EntityInput<Identity extends IEntityIdentity, Properties extends IBlueprintCollection> = BlueprintInput<Properties> & Partial<BlueprintInput<Identity>>;
47
47
  /** What `entityOf` takes, after the blueprint, to describe its keys. */
48
- export type EntityOptions<Properties extends IBlueprintCollection, Change extends readonly RecordKey<Properties>[] = readonly RecordKey<Properties>[], Unique extends readonly RecordKey<Properties>[] = readonly RecordKey<Properties>[], Plugins extends readonly AnyRecordPlugin[] = readonly AnyRecordPlugin[], Context = unknown> = RecordOptions<Properties, Change, Unique, Plugins, Context> & EntityEvents;
48
+ export type EntityOptions<Properties extends IBlueprintCollection, Change extends readonly RecordKey<Properties>[] = readonly RecordKey<Properties>[], Unique extends readonly RecordKey<Properties>[] = readonly RecordKey<Properties>[], Plugins extends readonly AnyRecordPlugin[] = readonly AnyRecordPlugin[], Context = unknown, Sensitive extends readonly RecordKey<Properties>[] = readonly RecordKey<Properties>[]> = RecordOptions<Properties, Change, Unique, Plugins, Context, Sensitive> & EntityEvents;
49
49
  /**
50
50
  * The unique keys of an entity: `id`, those listed in `options.unique`, and
51
51
  * those holding a property declared `unique`, as `Slug`.
52
52
  */
53
53
  export type EntityUniqueKey<Identity extends IEntityIdentity, Properties extends IBlueprintCollection, Unique extends readonly string[]> = "id" | Unique[number] | MarkedUniqueKey<EntityProperties<Identity, Properties>>;
54
54
  /** The entities of the class `entityOf` returns, with what its plugins add, `Members`. */
55
- export type DeclaredEntityInstance<Identity extends IEntityIdentity, Properties extends IBlueprintCollection, Change extends readonly string[], Members = unknown> = AbstractEntity<EntityProperties<Identity, Properties>> & Readonly<BlueprintValue<EntityProperties<Identity, Properties>>> & RecordChanges<Properties, Change> & Members;
55
+ export type DeclaredEntityInstance<Identity extends IEntityIdentity, Properties extends IBlueprintCollection, Change extends readonly string[], Members = unknown, Sensitive extends string = never> = AbstractEntity<EntityProperties<Identity, Properties>, Sensitive> & Readonly<BlueprintValue<EntityProperties<Identity, Properties>>> & RecordChanges<Properties, Change> & Members;
56
56
  /** What `entityOf` returns: a record class with identity. */
57
- export type EntityConstructor<Identity extends IEntityIdentity, Properties extends IBlueprintCollection, Change extends readonly string[], Unique extends readonly string[] = [], Members = unknown> = {
57
+ export type EntityConstructor<Identity extends IEntityIdentity, Properties extends IBlueprintCollection, Change extends readonly string[], Unique extends readonly string[] = [], Members = unknown, Sensitive extends string = never> = {
58
58
  readonly [EntityClassBrand]: true;
59
59
  /** The keys no two entities may share, as `options.unique` lists them. */
60
60
  readonly [EntityUniqueKeys]: readonly EntityUniqueKey<Identity, Properties, Unique>[];
61
- new (value: EntityInput<Identity, Properties>): DeclaredEntityInstance<Identity, Properties, Change, Members>;
61
+ new (value: EntityInput<Identity, Properties>): DeclaredEntityInstance<Identity, Properties, Change, Members, Sensitive>;
62
62
  /**
63
63
  * The Standard Schema of the class: `validate` gives an instance of it, or
64
64
  * the issues that reject the value. Typed with the instance of the class
65
65
  * `entityOf` returns, not of a subclass.
66
66
  */
67
- readonly "~standard": StandardSchemaV1.Props<EntityInput<Identity, Properties>, DeclaredEntityInstance<Identity, Properties, Change, Members>>;
67
+ readonly "~standard": StandardSchemaV1.Props<EntityInput<Identity, Properties>, DeclaredEntityInstance<Identity, Properties, Change, Members, Sensitive>>;
68
68
  /**
69
69
  * An instance of the class (a subclass included) for `value`, or every
70
70
  * issue that rejects it; never throws for an invalid value.
71
71
  */
72
72
  safeParse<Self extends new (value: never) => object>(this: Self, value: unknown): ParseResult<InstanceType<Self>>;
73
73
  readonly [Kind]: "record";
74
+ /**
75
+ * The name of the class as declared: the `name` option, which a bundler
76
+ * does not rename, or the name of the class without one. A subclass may
77
+ * give its own with `static override readonly recordName = "..."`.
78
+ */
79
+ readonly recordName: string;
74
80
  readonly blueprint: IBlueprint<EntityProperties<Identity, Properties>>;
75
81
  readonly options: RecordSettings<EntityProperties<Identity, Properties>>;
76
82
  /** Its plugins, those they require included, each once. */
@@ -80,4 +86,4 @@ export type EntityConstructor<Identity extends IEntityIdentity, Properties exten
80
86
  * What `entityBuilderOf` returns: `entityOf`, declaring entities with the
81
87
  * identity it was given, whose `options.plugins` picks from `Context`.
82
88
  */
83
- export type EntityBuilder<Identity extends IEntityIdentity, Context = Record<never, never>> = <Properties extends IBlueprintCollection & Partial<Record<EntityIdentityKey, never>>, const Change extends readonly RecordKey<Properties>[] = [], const Unique extends readonly RecordKey<Properties>[] = [], const Plugins extends readonly AnyRecordPlugin[] = []>(blueprint: IBlueprint<Properties, boolean>, options?: EntityOptions<NoInfer<Properties>, Change, Unique, Plugins, Context>) => EntityConstructor<Identity, WithPluginProperties<Properties, Plugins>, Change, readonly (Unique[number] | PluginUniqueKey<Plugins>)[], PluginMembers<Plugins>>;
89
+ export type EntityBuilder<Identity extends IEntityIdentity, Context = Record<never, never>> = <Properties extends IBlueprintCollection & Partial<Record<EntityIdentityKey, never>>, const Change extends readonly RecordKey<Properties>[] = [], const Unique extends readonly RecordKey<Properties>[] = [], const Plugins extends readonly AnyRecordPlugin[] = [], const Sensitive extends readonly RecordKey<Properties>[] = []>(blueprint: IBlueprint<Properties, boolean>, options?: EntityOptions<NoInfer<Properties>, Change, Unique, Plugins, Context, Sensitive>) => EntityConstructor<Identity, WithPluginProperties<Properties, Plugins>, Change, readonly (Unique[number] | PluginUniqueKey<Plugins>)[], PluginMembers<Plugins>, Sensitive[number] | PluginSensitiveKey<Plugins>>;
@@ -31,8 +31,17 @@ export declare class Exception<Details = unknown> extends Error {
31
31
  /**
32
32
  * Passes the exception to an upper layer: a new exception in `scope`, with
33
33
  * this one as its cause, keeping its name and message unless given others.
34
+ * With the same name and no new `details`, an exception of a class declared
35
+ * with `exceptionOf` (or of a subclass of one) stays an instance of that
36
+ * class and keeps its `details`, so `instanceof` holds across layers; the
37
+ * constructor of the class does not run again.
34
38
  */
35
- propagate<Next = undefined>({ layer, source, name, message, details, }: ExceptionPropagation<Next>): Exception<Next>;
39
+ propagate(scope: ExceptionScope & {
40
+ readonly name?: undefined;
41
+ readonly message?: string;
42
+ readonly details?: undefined;
43
+ }): Exception<Details>;
44
+ propagate<Next = undefined>(scope: ExceptionPropagation<Next>): Exception<Next>;
36
45
  toJSON(): ExceptionFrame<Details> & {
37
46
  trace: readonly ExceptionFrame[];
38
47
  };
@@ -2,8 +2,8 @@ import { Exception } from "../exception/exception.js";
2
2
  import type { OperationDescriptor, OutputStatuses } from "./types/output.interface.js";
3
3
  /**
4
4
  * The exception that raised `error` under its name: a use case or a command
5
- * passes an exception up as a copy with the same name, no details and the
6
- * original as its cause, so the original is the deepest one with that name.
5
+ * passes an exception up as a copy with the same name and the original as
6
+ * its cause, so the original is the deepest one with that name.
7
7
  */
8
8
  export declare function raisedOf(error: Exception): Exception;
9
9
  /**
@@ -6,20 +6,20 @@ import { AbstractDefineProperty } from "./define-property.js";
6
6
  import type { AnyPropertyConstructor } from "./types/property-collection.type.js";
7
7
  import type { PropertyValue } from "./types/property-constructor.type.js";
8
8
  import type { PropertyOptions } from "./types/property-options.interface.js";
9
- import type { IPropertyType, UniqueMark } from "./types/property-type.interface.js";
9
+ import type { IPropertyType, SensitiveMark, UniqueMark } from "./types/property-type.interface.js";
10
10
  type SchemaValueType<SchemaType extends TSchema, Optional extends boolean = false> = Optional extends true ? TOptional<SchemaType> : SchemaType;
11
11
  /**
12
12
  * What `propertyOf` takes. `SchemaType` is inferred from `schema` alone: the
13
13
  * value is `NoInfer`, since inferring through `Static` makes TypeScript
14
14
  * measure `Static` itself, which costs about a second on every check.
15
15
  */
16
- type PropertyOfArgs<SchemaType extends TSchema, Optional extends boolean = false, Unique extends boolean = false, Value = Static<SchemaType>> = {
16
+ type PropertyOfArgs<SchemaType extends TSchema, Optional extends boolean = false, Unique extends boolean = false, Sensitive extends boolean = false, Value = Static<SchemaType>> = {
17
17
  name: string;
18
18
  schema: SchemaType;
19
19
  default?: NoInfer<Value>;
20
20
  optional?: Optional;
21
21
  /** A record masks its value in `toJSON`, at any depth, as `Password`. */
22
- sensitive?: boolean;
22
+ sensitive?: Sensitive;
23
23
  /**
24
24
  * A record lists a key holding it in `options.unique`, as `Slug`; records
25
25
  * do not check it, repositories (or the database) enforce it.
@@ -53,5 +53,5 @@ export interface PropertyClass<SchemaType extends TSchema, Value = Static<Schema
53
53
  with<Self extends AnyPropertyConstructor>(this: Self, options: PropertyOptions<SchemaType, Value>): Self;
54
54
  demo<Self extends AbstractProperty<any>>(this: new (value: never) => Self): Self;
55
55
  }
56
- export declare function propertyOf<SchemaType extends TSchema, Optional extends boolean = false, const Unique extends boolean = false>({ name, schema, optional, sensitive, unique, default: _default, preHandle: onPreHandle, afterHandle: onAfterHandle, }: PropertyOfArgs<SchemaType, Optional, Unique>): PropertyClass<SchemaValueType<SchemaType, Optional>, Static<SchemaType>> & UniqueMark<Unique>;
56
+ export declare function propertyOf<SchemaType extends TSchema, Optional extends boolean = false, const Unique extends boolean = false, const Sensitive extends boolean = false>({ name, schema, optional, sensitive, unique, default: _default, preHandle: onPreHandle, afterHandle: onAfterHandle, }: PropertyOfArgs<SchemaType, Optional, Unique, Sensitive>): PropertyClass<SchemaValueType<SchemaType, Optional>, Static<SchemaType>> & UniqueMark<Unique> & SensitiveMark<Sensitive>;
57
57
  export {};
@@ -5,7 +5,7 @@ import type { AbstractProperty } from "../abstract-property.js";
5
5
  import type { AnyPropertyConstructor } from "./property-collection.type.js";
6
6
  import type { PropertyValue } from "./property-constructor.type.js";
7
7
  import type { CallableKey, StoredField } from "./property-options.interface.js";
8
- import type { IPropertyType, UniqueTypeOf } from "./property-type.interface.js";
8
+ import type { IPropertyType, SensitiveTypeOf, UniqueTypeOf } from "./property-type.interface.js";
9
9
  /**
10
10
  * `Property` accepting `undefined`: its schema becomes `TOptional`, while its
11
11
  * own methods and statics (e.g. `Password#verify`, `Id.generate`) are kept.
@@ -14,7 +14,7 @@ export type OptionalProperty<Property extends AnyPropertyConstructor> = Property
14
14
  readonly type: IPropertyType<infer SchemaType, infer Value>;
15
15
  } ? SchemaType extends TOptional<TSchema> ? StoredField<Property> : Omit<Property, "type" | "prototype" | "safeParse" | "~standard" | CallableKey> & {
16
16
  new (value: PropertyValue<TOptional<SchemaType>, Value>): AbstractProperty<TOptional<SchemaType>, Value> & Omit<InstanceType<Property>, keyof AbstractProperty<TSchema>>;
17
- readonly type: IPropertyType<TOptional<SchemaType>, Value> & UniqueTypeOf<Property>;
17
+ readonly type: IPropertyType<TOptional<SchemaType>, Value> & UniqueTypeOf<Property> & SensitiveTypeOf<Property>;
18
18
  safeParse(value: unknown): ParseResult<PropertyValue<TOptional<SchemaType>, Value>>;
19
19
  readonly "~standard": StandardSchemaV1.Props<PropertyValue<TOptional<SchemaType>, Value>, PropertyValue<TOptional<SchemaType>, Value>>;
20
20
  } : never;
@@ -25,6 +25,23 @@ export type UniqueMark<Unique extends boolean> = Unique extends true ? {
25
25
  readonly unique: true;
26
26
  };
27
27
  } : unknown;
28
+ /**
29
+ * The type of a property declared `sensitive`, as `Password`, so that the
30
+ * type of `toJSON` masks the keys holding it.
31
+ */
32
+ export type SensitiveMark<Sensitive extends boolean> = Sensitive extends true ? {
33
+ readonly type: {
34
+ readonly sensitive: true;
35
+ };
36
+ } : unknown;
37
+ /** `{ sensitive: true }` when `Property` is declared `sensitive`, for its optional version. */
38
+ export type SensitiveTypeOf<Property> = Property extends {
39
+ readonly type: {
40
+ readonly sensitive: true;
41
+ };
42
+ } ? {
43
+ readonly sensitive: true;
44
+ } : unknown;
28
45
  /** `{ unique: true }` when `Property` is declared `unique`, for its optional version. */
29
46
  export type UniqueTypeOf<Property> = Property extends {
30
47
  readonly type: {
@@ -1,8 +1,8 @@
1
- import type { BlueprintData, BlueprintField, BlueprintInput, BlueprintValue, IBlueprint, IBlueprintCollection } from "../blueprint/types/blueprint.interface.js";
1
+ import type { BlueprintField, BlueprintInput, BlueprintValue, IBlueprint, IBlueprintCollection } from "../blueprint/types/blueprint.interface.js";
2
2
  import type { ParseResult } from "../parse/types/parse.interface.js";
3
3
  import type { StandardSchemaV1 } from "../standard/standard-schema.js";
4
4
  import { Meta } from "../symbols/meta.js";
5
- import type { IRecord, RecordChange, RecordEffects, RecordSettings } from "./types/record.interface.js";
5
+ import type { IRecord, RecordChange, RecordEffects, RecordJson, RecordSettings } from "./types/record.interface.js";
6
6
  /** The statics of a class made by `recordOf`, that its records read. */
7
7
  interface RecordDeclaration<Properties extends IBlueprintCollection> {
8
8
  readonly blueprint: IBlueprint<Properties>;
@@ -54,7 +54,7 @@ export declare const RecordPluginHooks: unique symbol;
54
54
  * having `key`, then those of the record, for a stored change.
55
55
  */
56
56
  export declare function runRecordEffects<Self extends object>(record: Self, key: PropertyKey, next: unknown, previous: unknown): Self;
57
- export declare abstract class AbstractRecord<Properties extends IBlueprintCollection> implements IRecord<Properties> {
57
+ export declare abstract class AbstractRecord<Properties extends IBlueprintCollection, Sensitive extends string = never> implements IRecord<Properties, Sensitive> {
58
58
  get [Meta](): RecordDeclaration<Properties>;
59
59
  constructor(value: BlueprintInput<Properties>);
60
60
  /**
@@ -72,7 +72,7 @@ export declare abstract class AbstractRecord<Properties extends IBlueprintCollec
72
72
  * The value with the sensitive keys, and every property declared
73
73
  * `sensitive` (as `Password`), masked.
74
74
  */
75
- toJSON(): Readonly<Partial<BlueprintData<Properties>>>;
75
+ toJSON(): RecordJson<Properties, Sensitive>;
76
76
  /**
77
77
  * Validates `value`, stores it under `key`, then runs `onSetAll` and the
78
78
  * effect of `key` in `onSet`.
@@ -9,6 +9,8 @@ export declare const InvalidRecordValue: import("../../index.js").DeclaredExcept
9
9
  readonly errors: readonly string[];
10
10
  readonly issues: readonly Issue[];
11
11
  }>;
12
+ /** `recordOf` or `entityOf` got an option it cannot use, as a `name` that is not an identifier. */
13
+ export declare const InvalidRecordDeclaration: import("../../index.js").DeclaredExceptionClass<string>;
12
14
  /**
13
15
  * A record is given a key its blueprint does not declare, in its value or by
14
16
  * `set`; `key` is its path, e.g. `address.extra`, and `source` the record name.
@@ -0,0 +1,16 @@
1
+ /** Whether `name` can be the `name` option of `recordOf` or `entityOf`. */
2
+ export declare function isRecordName(name: unknown): name is string;
3
+ /**
4
+ * The name of a record or entity class: the `name` option it, or the class it
5
+ * extends, was declared with, which bundlers and minifiers do not change;
6
+ * without one, the name of the class.
7
+ */
8
+ export declare function recordNameOf(Class: object): string;
9
+ /** Keeps the `name` option `Class` was declared with. */
10
+ export declare function declareRecordName(Class: object, name: string): void;
11
+ /**
12
+ * The name `Class` declares: the `name` option of it or of a class it
13
+ * extends, or a `static recordName` of its own; `undefined` when the name of
14
+ * the record is only the name of its class.
15
+ */
16
+ export declare function declaredRecordNameOf(Class: object): string | undefined;
@@ -1,12 +1,12 @@
1
1
  import type { AnyBlueprint, IBlueprint, IBlueprintCollection } from "../blueprint/types/blueprint.interface.js";
2
2
  import { AbstractRecord } from "./abstract-record.js";
3
- import type { AnyRecordPlugin, IRecordPluginCatalog, PluginMembers, PluginUniqueKey, RecordPluginContext, WithPluginProperties } from "./types/plugin.interface.js";
3
+ import type { AnyRecordPlugin, IRecordPluginCatalog, PluginMembers, PluginSensitiveKey, PluginUniqueKey, RecordPluginContext, WithPluginProperties } from "./types/plugin.interface.js";
4
4
  import type { RecordConstructor, RecordKey, RecordOptions } from "./types/record.interface.js";
5
5
  /**
6
6
  * What `recordBuilderOf` returns: `recordOf`, whose `options.plugins` can
7
7
  * pick plugins from `Context`, the plugins of its catalogs by name.
8
8
  */
9
- export type RecordBuilder<Context> = <Properties extends IBlueprintCollection, const Change extends readonly RecordKey<Properties>[] = [], const Unique extends readonly RecordKey<Properties>[] = [], const Plugins extends readonly AnyRecordPlugin[] = []>(blueprint: IBlueprint<Properties, boolean>, options?: RecordOptions<NoInfer<Properties>, Change, Unique, Plugins, Context>) => RecordConstructor<WithPluginProperties<Properties, Plugins>, Change, Unique[number] | PluginUniqueKey<Plugins>, PluginMembers<Plugins>>;
9
+ export type RecordBuilder<Context> = <Properties extends IBlueprintCollection, const Change extends readonly RecordKey<Properties>[] = [], const Unique extends readonly RecordKey<Properties>[] = [], const Plugins extends readonly AnyRecordPlugin[] = [], const Sensitive extends readonly RecordKey<Properties>[] = []>(blueprint: IBlueprint<Properties, boolean>, options?: RecordOptions<NoInfer<Properties>, Change, Unique, Plugins, Context, Sensitive>) => RecordConstructor<WithPluginProperties<Properties, Plugins>, Change, Unique[number] | PluginUniqueKey<Plugins>, PluginMembers<Plugins>, never, Sensitive[number] | PluginSensitiveKey<Plugins>>;
10
10
  /**
11
11
  * A `recordOf` whose `options.plugins` can pick plugins from `catalogs`,
12
12
  * e.g. `recordBuilderOf(StdRecordPlugins)`, as `(ctx) => [ctx.publishable]`;
@@ -27,6 +27,7 @@ export declare function pluginContextOf(catalogs: readonly IRecordPluginCatalog[
27
27
  export declare const recordOf: RecordBuilder<Record<never, never>>;
28
28
  /** The options `declareRecord` takes, untyped. */
29
29
  interface RecordOptionsInput {
30
+ readonly name?: string;
30
31
  readonly sensitive?: readonly string[];
31
32
  readonly unique?: readonly string[];
32
33
  readonly change?: readonly string[];
@@ -37,5 +38,5 @@ interface RecordOptionsInput {
37
38
  * and named `name`; `reserved` are more names no key can take, the members
38
39
  * `Base` adds, and `context` the plugins `options.plugins` can pick from.
39
40
  */
40
- export declare function declareRecord(Base: abstract new (value: never) => AbstractRecord<IBlueprintCollection>, declared: AnyBlueprint, { plugins, ...declaredOptions }: RecordOptionsInput, name: string, reserved?: readonly string[], context?: Readonly<Record<string, AnyRecordPlugin>>): abstract new (value: never) => AbstractRecord<IBlueprintCollection>;
41
+ export declare function declareRecord(Base: abstract new (value: never) => AbstractRecord<IBlueprintCollection>, declared: AnyBlueprint, { plugins, name: declaredName, ...declaredOptions }: RecordOptionsInput, fallback: string, reserved?: readonly string[], context?: Readonly<Record<string, AnyRecordPlugin>>): abstract new (value: never) => AbstractRecord<IBlueprintCollection>;
41
42
  export {};
@@ -7,6 +7,11 @@ import type { RecordSettings } from "./record.interface.js";
7
7
  * class holds a value under it.
8
8
  */
9
9
  export declare const RecordUniqueKeys: unique symbol;
10
+ /**
11
+ * Where the type of the keys a record class lists in `options.sensitive` is
12
+ * kept, for the type of `toJSON` of what its plugin is applied to. Type only.
13
+ */
14
+ export declare const RecordSensitiveKeys: unique symbol;
10
15
  /** What `recordPluginOf` takes, after the record class. */
11
16
  export interface RecordPluginOptions {
12
17
  /** A semantic version, e.g. `1.2.0`; `1.0.0` when left out. */
@@ -139,4 +144,10 @@ export type PluginUniqueKey<Plugins extends readonly AnyRecordPlugin[]> = Plugin
139
144
  readonly [RecordUniqueKeys]?: infer Key;
140
145
  };
141
146
  } ? Extract<Key, string> : never : never;
147
+ /** The keys the record classes of the plugins list in `options.sensitive`. */
148
+ export type PluginSensitiveKey<Plugins extends readonly AnyRecordPlugin[]> = Plugins[number] extends infer Plugin ? Plugin extends {
149
+ readonly record: {
150
+ readonly [RecordSensitiveKeys]?: infer Key;
151
+ };
152
+ } ? Extract<Key, string> : never : never;
142
153
  export {};