@afora/core 0.0.6 → 0.0.8

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 (63) hide show
  1. package/README.md +36 -7
  2. package/dist/core/application/start.d.ts +7 -0
  3. package/dist/core/application/types/application.interface.d.ts +47 -2
  4. package/dist/core/command/command.d.ts +11 -4
  5. package/dist/core/command/types/command.interface.d.ts +26 -0
  6. package/dist/core/entity/abstract-entity.d.ts +28 -1
  7. package/dist/core/entity/entity.d.ts +1 -1
  8. package/dist/core/entity/event.d.ts +13 -2
  9. package/dist/core/entity/exceptions.d.ts +8 -0
  10. package/dist/core/entity/types/entity.interface.d.ts +66 -2
  11. package/dist/core/entity/version.d.ts +13 -0
  12. package/dist/core/event-policy/event-policy.d.ts +25 -0
  13. package/dist/core/event-policy/exceptions.d.ts +11 -0
  14. package/dist/core/event-policy/publish.d.ts +17 -0
  15. package/dist/core/event-policy/publishers.d.ts +18 -0
  16. package/dist/core/event-policy/types/event-policy.interface.d.ts +72 -0
  17. package/dist/core/output/coerce.d.ts +9 -0
  18. package/dist/core/output/openapi-sink.d.ts +138 -38
  19. package/dist/core/output/status.d.ts +2 -1
  20. package/dist/core/parse/parse.d.ts +10 -0
  21. package/dist/core/parse/strip.d.ts +13 -0
  22. package/dist/core/property/input.d.ts +20 -0
  23. package/dist/core/record/plugin.d.ts +6 -2
  24. package/dist/core/repository/exceptions.d.ts +30 -0
  25. package/dist/core/repository/in-memory.d.ts +14 -1
  26. package/dist/core/repository/plan.d.ts +20 -0
  27. package/dist/core/repository/provider.d.ts +1 -1
  28. package/dist/core/repository/query.d.ts +9 -2
  29. package/dist/core/repository/repository.d.ts +40 -8
  30. package/dist/core/repository/state.d.ts +39 -2
  31. package/dist/core/repository/types/query.interface.d.ts +106 -5
  32. package/dist/core/repository/types/repository.interface.d.ts +113 -7
  33. package/dist/core/use-case/exceptions.d.ts +36 -0
  34. package/dist/core/use-case/plugin-catalog.d.ts +11 -0
  35. package/dist/core/use-case/plugin.d.ts +25 -0
  36. package/dist/core/use-case/steps/types/steps.interface.d.ts +23 -6
  37. package/dist/core/use-case/target.d.ts +9 -0
  38. package/dist/core/use-case/types/plugin.interface.d.ts +185 -0
  39. package/dist/experimental.js +3 -3
  40. package/dist/{index-3nd9bjnc.js → index-qxfkyd6k.js} +646 -635
  41. package/dist/{index-gbgc40xe.js → index-t5qwtyj5.js} +2798 -295
  42. package/dist/index.d.ts +35 -15
  43. package/dist/index.js +99 -15
  44. package/dist/std/blueprint-plugin.d.ts +6 -2
  45. package/dist/std/blueprint.d.ts +3 -0
  46. package/dist/std/command.d.ts +11 -0
  47. package/dist/std/entity.d.ts +2 -1
  48. package/dist/std/event-policies.d.ts +2 -0
  49. package/dist/std/properties/exceptions.d.ts +11 -0
  50. package/dist/std/properties/json.d.ts +54 -0
  51. package/dist/std/properties/password.d.ts +16 -1
  52. package/dist/std/properties/time.d.ts +4 -0
  53. package/dist/std/use-cases/auth.d.ts +5 -0
  54. package/dist/std/use-cases/crud.d.ts +25 -0
  55. package/dist/std/use-cases/index.d.ts +11 -0
  56. package/dist/std/use-cases/lifecycle.d.ts +13 -0
  57. package/dist/std/use-cases/shared.d.ts +88 -0
  58. package/dist/std/use-cases/types.d.ts +291 -0
  59. package/dist/testing/index.d.ts +2 -0
  60. package/dist/testing/index.js +355 -2
  61. package/dist/testing/memory-publisher.d.ts +16 -0
  62. package/dist/testing/provider-contract.d.ts +14 -0
  63. package/package.json +3 -2
@@ -26,18 +26,18 @@ __export(exports_exceptions, {
26
26
  UnknownRecordPlugin: () => UnknownRecordPlugin2
27
27
  });
28
28
 
29
- // src/core/blueprint/exceptions.ts
29
+ // src/core/property/exceptions.ts
30
30
  var exports_exceptions2 = {};
31
31
  __export(exports_exceptions2, {
32
- ReservedBlueprintKey: () => ReservedBlueprintKey2,
33
- UndeclaredBlueprintProperty: () => UndeclaredBlueprintProperty2
32
+ InvalidPropertyValue: () => InvalidPropertyValue2,
33
+ PropertyRequired: () => PropertyRequired2
34
34
  });
35
35
 
36
- // src/core/property/exceptions.ts
36
+ // src/core/blueprint/exceptions.ts
37
37
  var exports_exceptions3 = {};
38
38
  __export(exports_exceptions3, {
39
- InvalidPropertyValue: () => InvalidPropertyValue2,
40
- PropertyRequired: () => PropertyRequired2
39
+ ReservedBlueprintKey: () => ReservedBlueprintKey2,
40
+ UndeclaredBlueprintProperty: () => UndeclaredBlueprintProperty2
41
41
  });
42
42
 
43
43
  // src/core/property/is-property.ts
@@ -989,293 +989,271 @@ class AbstractRecord2 {
989
989
  }
990
990
  }
991
991
 
992
- // src/core/record/plugin.ts
993
- var RecordPlugins = Symbol("recordPlugins");
994
- var IdentifiedRecord = Symbol("identifiedRecord");
995
- var SEMVER = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
996
- function isVersion(version) {
997
- return typeof version === "string" && SEMVER.test(version);
992
+ // src/core/blueprint/blueprint.ts
993
+ import { Type as Type3 } from "typebox";
994
+
995
+ // src/core/property/property.ts
996
+ import { Type } from "typebox";
997
+
998
+ // src/core/symbols/state.ts
999
+ var State2 = Symbol("state");
1000
+
1001
+ // src/core/property/abstract-property.ts
1002
+ var parsingDefinitions = new WeakMap;
1003
+ function parsingDefinitionOf(Class) {
1004
+ let definition = parsingDefinitions.get(Class);
1005
+ if (!definition) {
1006
+ definition = Object.create(Class.prototype).createDefinition();
1007
+ parsingDefinitions.set(Class, definition);
1008
+ }
1009
+ return definition;
998
1010
  }
999
- function versionParts(version) {
1000
- const [, major, minor, patch] = SEMVER.exec(version);
1001
- return [Number(major), Number(minor), Number(patch)];
1011
+
1012
+ class AbstractProperty2 {
1013
+ get [State2]() {
1014
+ return this.definition[State2];
1015
+ }
1016
+ definition;
1017
+ value;
1018
+ constructor(value) {
1019
+ this.definition = this.createDefinition();
1020
+ this.value = this.definition.make(value === Demo2 ? this.definition.type[Demo2] : value);
1021
+ }
1022
+ static demo() {
1023
+ return new this(Demo2);
1024
+ }
1025
+ static safeParse(value) {
1026
+ return parsingDefinitionOf(this).parse(value);
1027
+ }
1028
+ static with(options) {
1029
+ return refinePropertyOf(this, options);
1030
+ }
1031
+ static get "~standard"() {
1032
+ return standardOfClass(this);
1033
+ }
1002
1034
  }
1003
- var HOOKS = ["onSet", "onSetAll"];
1004
- var internals = new WeakMap;
1005
- function pluginsOf2(value) {
1006
- const Class = typeof value === "function" ? value : typeof value === "object" && value !== null ? value.constructor : undefined;
1007
- return Class?.[RecordPlugins] ?? [];
1035
+
1036
+ // src/core/property/enums/property-states.ts
1037
+ var PropertyStates2;
1038
+ ((PropertyStates) => {
1039
+ PropertyStates[PropertyStates["Declared"] = 0] = "Declared";
1040
+ PropertyStates[PropertyStates["PreHandling"] = 1] = "PreHandling";
1041
+ PropertyStates[PropertyStates["Handling"] = 2] = "Handling";
1042
+ PropertyStates[PropertyStates["AfterHandling"] = 3] = "AfterHandling";
1043
+ PropertyStates[PropertyStates["Done"] = 4] = "Done";
1044
+ })(PropertyStates2 ||= {});
1045
+
1046
+ // src/core/property/exceptions.ts
1047
+ var PropertyRequired2 = exceptionOf2({
1048
+ layer: "property" /* Property */,
1049
+ name: "PropertyRequired",
1050
+ message: (_, source) => `Property "${source}" is required.`
1051
+ });
1052
+ var InvalidPropertyValue2 = exceptionOf2({
1053
+ layer: "property" /* Property */,
1054
+ name: "InvalidPropertyValue",
1055
+ message: ({ errors }, source) => `Invalid value for property "${source}": ${errors.join("; ")}`
1056
+ });
1057
+
1058
+ // src/core/property/define-property.ts
1059
+ var MISMATCH = Symbol("mismatch");
1060
+ function once(build) {
1061
+ let result;
1062
+ return () => {
1063
+ result ??= build();
1064
+ return result;
1065
+ };
1008
1066
  }
1009
- function isRecordPlugin2(value) {
1010
- return typeof value === "object" && value !== null && value[Kind2] === "record-plugin";
1067
+ function segmentsOf(pointer) {
1068
+ if (pointer === "")
1069
+ return [];
1070
+ return pointer.slice(1).split("/").map((segment) => {
1071
+ const key = segment.replaceAll("~1", "/").replaceAll("~0", "~");
1072
+ return /^(0|[1-9]\d*)$/.test(key) ? Number(key) : key;
1073
+ });
1011
1074
  }
1012
- function hasPlugin2(value, plugin) {
1013
- const plugins = pluginsOf2(value);
1014
- if (typeof plugin === "string") {
1015
- return plugins.some((applied) => applied.name === plugin);
1075
+
1076
+ class AbstractDefineProperty2 {
1077
+ [State2] = 0 /* Declared */;
1078
+ make(value) {
1079
+ const attempt = this.attempt(value);
1080
+ if ("failure" in attempt)
1081
+ throw attempt.failure.exception();
1082
+ return attempt.value;
1016
1083
  }
1017
- if (plugins.includes(plugin))
1018
- return true;
1019
- return typeof value === "function" ? value === plugin.record || value.prototype instanceof plugin.record : value instanceof plugin.record;
1020
- }
1021
- function membersOf(Record, name) {
1022
- const descriptors = {};
1023
- const hooks = {};
1024
- for (let prototype = Record.prototype;prototype !== null && !Object.hasOwn(prototype.constructor, "blueprint"); prototype = Object.getPrototypeOf(prototype)) {
1025
- for (const member of Object.getOwnPropertyNames(prototype)) {
1026
- if (member === "constructor")
1027
- continue;
1028
- const descriptor = Object.getOwnPropertyDescriptor(prototype, member);
1029
- if (HOOKS.includes(member)) {
1030
- hooks[member] ??= descriptor.value;
1031
- continue;
1032
- }
1033
- if (RESERVED_RECORD_KEYS2.includes(member)) {
1034
- throw new InvalidRecordPlugin2({
1035
- source: name,
1036
- details: {
1037
- reason: `"${member}" is a member of every record and cannot be replaced`
1084
+ parse(value) {
1085
+ const attempt = this.attemptWithoutThrowing(value);
1086
+ if (!("failure" in attempt))
1087
+ return { value: attempt.value };
1088
+ const { exception, raised, issues } = attempt.failure;
1089
+ return {
1090
+ issues: issues.map((issue) => raised ? { ...issue, cause: exception() } : withLazyCause(issue, exception))
1091
+ };
1092
+ }
1093
+ preHandle(value) {
1094
+ return value;
1095
+ }
1096
+ afterHandle(value) {
1097
+ return value;
1098
+ }
1099
+ handle(value) {
1100
+ return this.type.schema.match(value) ? value : MISMATCH;
1101
+ }
1102
+ attemptWithoutThrowing(value) {
1103
+ try {
1104
+ return this.attempt(value);
1105
+ } catch (error) {
1106
+ return {
1107
+ failure: this.raise(error instanceof Exception2 ? error : this.invalidValue(error))
1108
+ };
1109
+ }
1110
+ }
1111
+ attempt(value) {
1112
+ const { name, optional } = this.type;
1113
+ this[State2] = 0 /* Declared */;
1114
+ if (value === undefined) {
1115
+ if (!optional) {
1116
+ return {
1117
+ failure: {
1118
+ exception: () => new PropertyRequired2({ source: name }),
1119
+ raised: false,
1120
+ issues: [
1121
+ {
1122
+ message: "is required",
1123
+ path: [],
1124
+ code: "required",
1125
+ source: name
1126
+ }
1127
+ ]
1038
1128
  }
1039
- });
1129
+ };
1040
1130
  }
1041
- descriptors[member] ??= descriptor;
1131
+ this[State2] = 4 /* Done */;
1132
+ return { value };
1042
1133
  }
1134
+ if (!this.type.schema.matchShape(value)) {
1135
+ return { failure: this.mismatch(value) };
1136
+ }
1137
+ try {
1138
+ this[State2] = 1 /* PreHandling */;
1139
+ value = this.runHandler(() => this.preHandle(value));
1140
+ this[State2] = 2 /* Handling */;
1141
+ const handled = this.handle(value);
1142
+ if (handled === MISMATCH) {
1143
+ return { failure: this.mismatch(value) };
1144
+ }
1145
+ value = handled;
1146
+ this[State2] = 3 /* AfterHandling */;
1147
+ value = this.runHandler(() => this.afterHandle(value));
1148
+ } catch (error) {
1149
+ if (error instanceof Exception2)
1150
+ return { failure: this.raise(error) };
1151
+ throw error;
1152
+ }
1153
+ this[State2] = 4 /* Done */;
1154
+ return { value };
1043
1155
  }
1044
- return { descriptors: Object.freeze(descriptors), hooks };
1045
- }
1046
- function recordPluginOf2(name, Record, { version = "1.0.0", description } = {}) {
1047
- if (typeof name !== "string" || name.length === 0) {
1048
- throw new InvalidRecordPlugin2({
1049
- source: String(name),
1050
- details: { reason: "its name must be a non-empty string" }
1051
- });
1052
- }
1053
- if (!isRecordClass2(Record)) {
1054
- throw new InvalidRecordPlugin2({
1055
- source: name,
1056
- details: { reason: "it must be made from a class of `recordOf`" }
1057
- });
1156
+ runHandler(handler) {
1157
+ try {
1158
+ return handler();
1159
+ } catch (error) {
1160
+ if (error instanceof Exception2)
1161
+ throw error;
1162
+ throw this.invalidValue(error);
1163
+ }
1058
1164
  }
1059
- if (Record[IdentifiedRecord]) {
1060
- throw new InvalidRecordPlugin2({
1061
- source: name,
1165
+ invalidValue(error) {
1166
+ return new InvalidPropertyValue2({
1167
+ source: this.type.name,
1062
1168
  details: {
1063
- reason: "it must be made from a record, not an entity: plugins have no identity"
1064
- }
1065
- });
1066
- }
1067
- if (!isVersion(version)) {
1068
- throw new InvalidRecordPlugin2({
1069
- source: name,
1070
- details: { reason: `"${version}" is not a semantic version` }
1169
+ errors: [error instanceof Error ? error.message : String(error)]
1170
+ },
1171
+ cause: error
1071
1172
  });
1072
1173
  }
1073
- const requires = pluginsOf2(Record);
1074
- const inherited = new Set(requires.flatMap((required) => required.keys));
1075
- const declaration = Record;
1076
- const keys = Object.freeze(Object.keys(declaration.blueprint.properties).filter((key) => !inherited.has(key)));
1077
- const own = membersOf(Record, name);
1078
- const plugin = Object.freeze({
1079
- [Kind2]: "record-plugin",
1080
- name,
1081
- version,
1082
- ...description === undefined ? {} : { description },
1083
- record: Record,
1084
- keys,
1085
- members: Object.freeze(Object.keys(own.descriptors)),
1086
- options: declaration.options,
1087
- requires,
1088
- is: (value) => hasPlugin2(value, plugin),
1089
- toJSON: () => descriptorOf(plugin)
1090
- });
1091
- internals.set(plugin, own);
1092
- return plugin;
1093
- }
1094
- function descriptorOf(plugin) {
1095
- return Object.freeze({
1096
- kind: "record-plugin",
1097
- name: plugin.name,
1098
- version: plugin.version,
1099
- ...plugin.description === undefined ? {} : { description: plugin.description },
1100
- keys: plugin.keys,
1101
- sensitive: plugin.options.sensitive,
1102
- unique: plugin.options.unique,
1103
- change: plugin.options.change,
1104
- members: plugin.members,
1105
- requires: Object.freeze(plugin.requires.map(({ name, version }) => Object.freeze({ name, version })))
1106
- });
1107
- }
1108
- function applyPlugins(blueprint, plugins, name, reserved) {
1109
- const flat = [];
1110
- for (const plugin of plugins) {
1111
- if (!isRecordPlugin2(plugin)) {
1112
- throw new InvalidRecordPlugin2({
1113
- source: name,
1114
- details: {
1115
- reason: "`options.plugins` takes plugins made by `recordPluginOf`"
1174
+ raise(exception) {
1175
+ return {
1176
+ exception: () => exception,
1177
+ raised: true,
1178
+ issues: [
1179
+ {
1180
+ message: exception.message,
1181
+ path: [],
1182
+ code: "invalid_value",
1183
+ source: this.type.name
1116
1184
  }
1117
- });
1118
- }
1119
- for (const applied of [...plugin.requires, plugin]) {
1120
- if (flat.includes(applied))
1121
- continue;
1122
- if (flat.some((other) => other.name === applied.name)) {
1123
- throw new RecordPluginConflict2({
1124
- source: name,
1125
- details: {
1126
- plugin: applied.name,
1127
- key: applied.name,
1128
- owner: "another plugin"
1129
- }
1130
- });
1131
- }
1132
- flat.push(applied);
1133
- }
1185
+ ]
1186
+ };
1134
1187
  }
1135
- const owners = new Map(Object.keys(blueprint.properties).map((key) => [key, `record "${name}"`]));
1136
- for (const member of reserved)
1137
- owners.set(member, `record "${name}"`);
1138
- const claim = (plugin, key) => {
1139
- const owner = owners.get(key);
1140
- if (owner !== undefined) {
1141
- throw new RecordPluginConflict2({
1188
+ mismatch(value) {
1189
+ const { name, schema } = this.type;
1190
+ const errors = schema.type.Errors(value);
1191
+ const issues = errors.map((error) => ({
1192
+ message: error.message,
1193
+ path: segmentsOf(error.instancePath),
1194
+ code: "invalid_value",
1195
+ source: name
1196
+ }));
1197
+ return {
1198
+ exception: once(() => new InvalidPropertyValue2({
1142
1199
  source: name,
1143
- details: { plugin: plugin.name, key, owner }
1144
- });
1145
- }
1146
- owners.set(key, `plugin "${plugin.name}"`);
1147
- };
1148
- const descriptors = {};
1149
- const hooks = [];
1150
- for (const plugin of flat) {
1151
- for (const key of plugin.keys)
1152
- claim(plugin, key);
1153
- const own = internals.get(plugin);
1154
- for (const member of plugin.members) {
1155
- claim(plugin, member);
1156
- descriptors[member] = own.descriptors[member];
1157
- }
1158
- if (own.hooks.onSet || own.hooks.onSetAll) {
1159
- hooks.push(Object.freeze({
1160
- plugin,
1161
- keys: Object.freeze(Object.keys(plugin.record.blueprint.properties)),
1162
- ...own.hooks
1163
- }));
1164
- }
1200
+ details: { errors: errors.map(describeError) }
1201
+ })),
1202
+ raised: false,
1203
+ issues: issues.length > 0 ? issues : [
1204
+ {
1205
+ message: "is invalid",
1206
+ path: [],
1207
+ code: "invalid_value",
1208
+ source: name
1209
+ }
1210
+ ]
1211
+ };
1165
1212
  }
1166
- return {
1167
- blueprint: flat.length === 0 ? blueprint : blueprint.join(...flat.map((plugin) => plugin.record.blueprint)),
1168
- plugins: Object.freeze(flat),
1169
- sensitive: flat.flatMap((plugin) => plugin.options.sensitive),
1170
- unique: flat.flatMap((plugin) => plugin.options.unique),
1171
- change: flat.flatMap((plugin) => plugin.options.change),
1172
- descriptors,
1173
- hooks: Object.freeze(hooks)
1174
- };
1175
1213
  }
1176
1214
 
1177
- // src/core/record/record.ts
1178
- function recordBuilderOf2(...catalogs) {
1179
- const context = pluginContextOf(catalogs);
1180
- return (blueprint, options = {}) => declareRecord(AbstractRecord2, blueprint, options, "record", [], context);
1181
- }
1182
- function pluginContextOf(catalogs) {
1183
- return Object.freeze(Object.assign({}, ...catalogs.map((catalog) => catalog.plugins)));
1184
- }
1185
- var recordOf3 = recordBuilderOf2();
1186
- function declareRecord(Base, declared, { plugins = [], name: declaredName, ...declaredOptions }, fallback, reserved = [], context = {}) {
1187
- if (declaredName !== undefined && !isRecordName(declaredName)) {
1188
- throw new InvalidRecordDeclaration2({
1189
- source: fallback,
1190
- details: `"name" must be an identifier, got ${JSON.stringify(declaredName)}.`
1191
- });
1192
- }
1193
- const name = declaredName ?? fallback;
1194
- const applied = applyPlugins(declared, typeof plugins === "function" ? plugins(context) : plugins, name, reserved);
1195
- const blueprint = applied.blueprint;
1196
- const sensitive = [
1197
- ...declaredOptions.sensitive ?? [],
1198
- ...applied.sensitive
1199
- ];
1200
- const unique = [...declaredOptions.unique ?? [], ...applied.unique];
1201
- const declaredChange = declaredOptions.change ?? [];
1202
- const keys = Object.keys(blueprint.properties);
1203
- const change = keys.filter((key) => declaredChange.includes(key) || applied.change.includes(key));
1204
- for (const key of [...sensitive, ...unique, ...declaredChange]) {
1205
- if (!keys.includes(key)) {
1206
- throw new UndeclaredRecordProperty2({
1207
- source: name,
1208
- details: { key }
1209
- });
1215
+ // src/core/property/property.ts
1216
+ function propertyOf2({
1217
+ name,
1218
+ schema,
1219
+ optional,
1220
+ sensitive = false,
1221
+ unique = false,
1222
+ default: _default,
1223
+ preHandle: onPreHandle,
1224
+ afterHandle: onAfterHandle
1225
+ }) {
1226
+ const valueSchema = optional ? Type.Optional(schema) : schema;
1227
+ const SchemaConstructor = schemaOf2(name, valueSchema);
1228
+ const type = {
1229
+ [Demo2]: _default,
1230
+ name,
1231
+ schema: new SchemaConstructor,
1232
+ optional: optional ?? false,
1233
+ sensitive,
1234
+ unique
1235
+ };
1236
+
1237
+ class DefineProperty extends AbstractDefineProperty2 {
1238
+ get type() {
1239
+ return type;
1210
1240
  }
1211
- }
1212
- const methods = change.length > 0 ? [CHANGE_MEMBER] : [];
1213
- for (const member of Object.keys(applied.descriptors)) {
1214
- if (methods.includes(member)) {
1215
- const plugin = applied.plugins.find((plugin) => plugin.members.includes(member));
1216
- throw new RecordPluginConflict2({
1217
- source: name,
1218
- details: {
1219
- plugin: plugin.name,
1220
- key: member,
1221
- owner: `record "${name}" (the \`change\` namespace)`
1222
- }
1223
- });
1241
+ preHandle(value) {
1242
+ return onPreHandle ? onPreHandle(value) : value;
1224
1243
  }
1225
- }
1226
- for (const key of keys) {
1227
- if (RESERVED_RECORD_KEYS2.includes(key) || reserved.includes(key) || methods.includes(key)) {
1228
- throw new ReservedRecordKey2({ source: name, details: { key } });
1244
+ afterHandle(value) {
1245
+ return onAfterHandle ? onAfterHandle(value) : value;
1229
1246
  }
1230
1247
  }
1231
- const options = Object.freeze({
1232
- sensitive: Object.freeze(keys.filter((key) => sensitive.includes(key) || isMarked(blueprint.properties[key], "sensitive", { lists: true }))),
1233
- unique: Object.freeze(keys.filter((key) => unique.includes(key) || isMarked(blueprint.properties[key], "unique"))),
1234
- change: Object.freeze([...change])
1235
- });
1236
1248
 
1237
- class DeclaredRecord extends Base {
1238
- static [Kind2] = "record";
1239
- static blueprint = blueprint;
1240
- static options = options;
1241
- static plugins = applied.plugins;
1242
- static [RecordPlugins] = applied.plugins;
1243
- static [RecordPluginHooks] = applied.hooks;
1244
- }
1245
- if (declaredName !== undefined) {
1246
- declareRecordName(DeclaredRecord, declaredName);
1247
- }
1248
- Object.defineProperty(DeclaredRecord, "recordName", {
1249
- get() {
1250
- return declaredName ?? this.name;
1251
- },
1252
- configurable: true
1253
- });
1254
- exposeRecordKeys(DeclaredRecord.prototype, keys);
1255
- for (const [member, descriptor] of Object.entries(applied.descriptors)) {
1256
- Object.defineProperty(DeclaredRecord.prototype, member, descriptor);
1257
- }
1258
- if (change.length > 0) {
1259
- Object.defineProperty(DeclaredRecord.prototype, CHANGE_MEMBER, {
1260
- get() {
1261
- let namespace = changeNamespaces.get(this);
1262
- if (namespace === undefined) {
1263
- namespace = Object.freeze(Object.fromEntries(change.map((key) => [
1264
- key,
1265
- (value) => this.set(key, value)
1266
- ])));
1267
- changeNamespaces.set(this, namespace);
1268
- }
1269
- return namespace;
1270
- },
1271
- configurable: true
1272
- });
1249
+ class Property extends AbstractProperty2 {
1250
+ static type = type;
1251
+ createDefinition() {
1252
+ return new DefineProperty;
1253
+ }
1273
1254
  }
1274
- Object.defineProperty(DeclaredRecord, "name", { value: name });
1275
- return DeclaredRecord;
1255
+ return Property;
1276
1256
  }
1277
- var CHANGE_MEMBER = "change";
1278
- var changeNamespaces = new WeakMap;
1279
1257
 
1280
1258
  // src/core/blueprint/exceptions.ts
1281
1259
  var ReservedBlueprintKey2 = exceptionOf2({
@@ -1289,422 +1267,455 @@ var UndeclaredBlueprintProperty2 = exceptionOf2({
1289
1267
  message: ({ key }) => `Property "${key}" of the blueprint is not declared.`
1290
1268
  });
1291
1269
 
1292
- // src/core/property/exceptions.ts
1293
- var PropertyRequired2 = exceptionOf2({
1294
- layer: "property" /* Property */,
1295
- name: "PropertyRequired",
1296
- message: (_, source) => `Property "${source}" is required.`
1297
- });
1298
- var InvalidPropertyValue2 = exceptionOf2({
1299
- layer: "property" /* Property */,
1300
- name: "InvalidPropertyValue",
1301
- message: ({ errors }, source) => `Invalid value for property "${source}": ${errors.join("; ")}`
1302
- });
1270
+ // src/core/blueprint/optional.ts
1271
+ import { Type as Type2 } from "typebox";
1272
+ var cache2 = new WeakMap;
1273
+ function optionalOf2(given) {
1274
+ const field = fieldOf(given);
1275
+ const cached = cache2.get(field);
1276
+ if (cached)
1277
+ return cached;
1278
+ if (isOptionalRecord2(field))
1279
+ return field;
1280
+ if (isRecordClass2(field)) {
1281
+ const optional = Object.freeze(withStandard({
1282
+ [Kind2]: "record",
1283
+ record: field,
1284
+ optional: true
1285
+ }, (value) => value === undefined ? { value } : field.safeParse(value)));
1286
+ cache2.set(field, optional);
1287
+ return optional;
1288
+ }
1289
+ if (isBlueprint2(field)) {
1290
+ if (field.optional)
1291
+ return field;
1292
+ const optional = {
1293
+ ...field,
1294
+ optional: true,
1295
+ safeParse: (value) => value === undefined ? { value } : field.safeParse(value)
1296
+ };
1297
+ Object.freeze(withStandard(optional, (value) => optional.safeParse(value)));
1298
+ cache2.set(field, optional);
1299
+ return optional;
1300
+ }
1301
+ if (isBlueprintList2(field)) {
1302
+ if (field.optional)
1303
+ return field;
1304
+ const optional = standardListOf({ ...field, optional: true });
1305
+ cache2.set(field, optional);
1306
+ return optional;
1307
+ }
1308
+ const Property = field;
1309
+ if (Property.type.optional)
1310
+ return Property;
1311
+ const base = Property.type;
1312
+ const SchemaConstructor = schemaOf2(base.name, Type2.Optional(base.schema.type.Type()));
1313
+ const type = {
1314
+ ...base,
1315
+ schema: new SchemaConstructor,
1316
+ optional: true
1317
+ };
1318
+ const Optional = derivePropertyOf(Property, type);
1319
+ recordPropertyLineage(Optional, { kind: "optional", base: Property });
1320
+ cache2.set(Property, Optional);
1321
+ return Optional;
1322
+ }
1303
1323
 
1304
1324
  // src/core/blueprint/blueprint.ts
1305
- import { Type as Type3 } from "typebox";
1306
-
1307
- // src/core/property/property.ts
1308
- import { Type } from "typebox";
1309
-
1310
- // src/core/symbols/state.ts
1311
- var State2 = Symbol("state");
1312
-
1313
- // src/core/property/abstract-property.ts
1314
- var parsingDefinitions = new WeakMap;
1315
- function parsingDefinitionOf(Class) {
1316
- let definition = parsingDefinitions.get(Class);
1317
- if (!definition) {
1318
- definition = Object.create(Class.prototype).createDefinition();
1319
- parsingDefinitions.set(Class, definition);
1325
+ function blueprintBuilderOf2(...base) {
1326
+ return (sourcesOrDefine, maybeDefine) => {
1327
+ const [sources, define] = typeof sourcesOrDefine === "function" ? [[], sourcesOrDefine] : [
1328
+ sourcesOrDefine,
1329
+ maybeDefine
1330
+ ];
1331
+ const all = [...base, ...sources];
1332
+ return create(all.flatMap((source) => isBlueprint2(source) ? source.plugins : [source]), { ...define(contextOf(all)) });
1333
+ };
1334
+ }
1335
+ function blueprintFromProperties(plugins, properties) {
1336
+ return create(plugins, { ...properties });
1337
+ }
1338
+ function create(plugins, properties) {
1339
+ const shape = {};
1340
+ for (const [key, field] of Object.entries(properties)) {
1341
+ properties[key] = fieldOf(field);
1342
+ shape[key] = schemaOfField(key, properties[key]);
1320
1343
  }
1321
- return definition;
1344
+ const SchemaConstructor = schemaOf2("blueprint", Type3.Object(shape));
1345
+ const blueprint = {
1346
+ [Kind2]: "blueprint",
1347
+ plugins: Object.freeze([...new Set(plugins)]),
1348
+ properties: Object.freeze(properties),
1349
+ schema: new SchemaConstructor,
1350
+ optional: false,
1351
+ safeParse: (value) => parseCollection(properties, value, [], "blueprint"),
1352
+ join: (...others) => create([blueprint, ...others].flatMap((source) => source.plugins), Object.assign({}, ...[blueprint, ...others].map((source) => source.properties)))
1353
+ };
1354
+ return Object.freeze(withStandard(blueprint, (value) => blueprint.safeParse(value)));
1322
1355
  }
1323
-
1324
- class AbstractProperty2 {
1325
- get [State2]() {
1326
- return this.definition[State2];
1356
+ var HELPERS = { listOf: listOf2, optionalOf: optionalOf2, propertyOf: propertyOf2 };
1357
+ var RESERVED_KEYS = ["optional", "list", ...Object.keys(HELPERS)];
1358
+ function contextOf(sources) {
1359
+ for (const key of RESERVED_KEYS) {
1360
+ const reserving = sources.find((source) => Object.hasOwn(source.properties, key));
1361
+ if (reserving) {
1362
+ throw new ReservedBlueprintKey2({
1363
+ source: isBlueprint2(reserving) ? "blueprint" : reserving.name,
1364
+ details: { key }
1365
+ });
1366
+ }
1327
1367
  }
1328
- definition;
1329
- value;
1330
- constructor(value) {
1331
- this.definition = this.createDefinition();
1332
- this.value = this.definition.make(value === Demo2 ? this.definition.type[Demo2] : value);
1368
+ const factories = Object.assign({}, ...sources.map((source) => isBlueprint2(source) ? {} : source.factories ?? {}));
1369
+ for (const key of RESERVED_KEYS) {
1370
+ if (Object.hasOwn(factories, key)) {
1371
+ const reserving = sources.find((source) => !isBlueprint2(source) && Object.hasOwn(source.factories ?? {}, key));
1372
+ throw new ReservedBlueprintKey2({
1373
+ source: reserving.name,
1374
+ details: { key }
1375
+ });
1376
+ }
1333
1377
  }
1334
- static demo() {
1335
- return new this(Demo2);
1378
+ const available = Object.assign({}, ...sources.map((source) => source.properties));
1379
+ for (const key of Object.keys(factories))
1380
+ delete available[key];
1381
+ const mirror = (fields, map) => Object.fromEntries(Object.entries(fields).map(([key, field]) => [key, map(field)]));
1382
+ const list = mirror(available, (field) => listOf2(field));
1383
+ const callable = (field) => isProperty2(field) ? callableOf(field) : field;
1384
+ return Object.freeze({
1385
+ ...mirror(available, callable),
1386
+ ...factories,
1387
+ ...HELPERS,
1388
+ optional: Object.freeze(mirror(available, (field) => callable(optionalOf2(field)))),
1389
+ list: Object.freeze({
1390
+ ...list,
1391
+ optional: Object.freeze(mirror(list, optionalOf2))
1392
+ })
1393
+ });
1394
+ }
1395
+ function schemaOfField(key, field) {
1396
+ if (isBlueprintList2(field)) {
1397
+ const { item, minItems, maxItems } = field;
1398
+ const schema = Type3.Array(schemaOfField(key, item), {
1399
+ ...minItems === undefined ? {} : { minItems },
1400
+ ...maxItems === undefined ? {} : { maxItems }
1401
+ });
1402
+ return field.optional ? Type3.Optional(schema) : schema;
1336
1403
  }
1337
- static safeParse(value) {
1338
- return parsingDefinitionOf(this).parse(value);
1404
+ if (isBlueprint2(field)) {
1405
+ const schema = field.schema.type.Type();
1406
+ return field.optional ? Type3.Optional(schema) : schema;
1339
1407
  }
1340
- static with(options) {
1341
- return refinePropertyOf(this, options);
1408
+ const record = recordFieldOf2(field);
1409
+ if (record) {
1410
+ const schema = record.record.blueprint.schema.type.Type();
1411
+ return record.optional ? Type3.Optional(schema) : schema;
1342
1412
  }
1343
- static get "~standard"() {
1344
- return standardOfClass(this);
1413
+ if (isProperty2(field)) {
1414
+ return field.type.schema.type.Type();
1345
1415
  }
1416
+ throw new UndeclaredBlueprintProperty2({
1417
+ source: "blueprint",
1418
+ details: { key }
1419
+ });
1346
1420
  }
1347
1421
 
1348
- // src/core/property/enums/property-states.ts
1349
- var PropertyStates2;
1350
- ((PropertyStates) => {
1351
- PropertyStates[PropertyStates["Declared"] = 0] = "Declared";
1352
- PropertyStates[PropertyStates["PreHandling"] = 1] = "PreHandling";
1353
- PropertyStates[PropertyStates["Handling"] = 2] = "Handling";
1354
- PropertyStates[PropertyStates["AfterHandling"] = 3] = "AfterHandling";
1355
- PropertyStates[PropertyStates["Done"] = 4] = "Done";
1356
- })(PropertyStates2 ||= {});
1357
-
1358
- // src/core/property/define-property.ts
1359
- var MISMATCH = Symbol("mismatch");
1360
- function once(build) {
1361
- let result;
1362
- return () => {
1363
- result ??= build();
1364
- return result;
1365
- };
1422
+ // src/core/record/plugin.ts
1423
+ var RecordPlugins = Symbol("recordPlugins");
1424
+ var IdentifiedRecord = Symbol("identifiedRecord");
1425
+ var SEMVER = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
1426
+ function isVersion(version) {
1427
+ return typeof version === "string" && SEMVER.test(version);
1366
1428
  }
1367
- function segmentsOf(pointer) {
1368
- if (pointer === "")
1369
- return [];
1370
- return pointer.slice(1).split("/").map((segment) => {
1371
- const key = segment.replaceAll("~1", "/").replaceAll("~0", "~");
1372
- return /^(0|[1-9]\d*)$/.test(key) ? Number(key) : key;
1373
- });
1429
+ function versionParts(version) {
1430
+ const [, major, minor, patch] = SEMVER.exec(version);
1431
+ return [Number(major), Number(minor), Number(patch)];
1374
1432
  }
1375
-
1376
- class AbstractDefineProperty2 {
1377
- [State2] = 0 /* Declared */;
1378
- make(value) {
1379
- const attempt = this.attempt(value);
1380
- if ("failure" in attempt)
1381
- throw attempt.failure.exception();
1382
- return attempt.value;
1383
- }
1384
- parse(value) {
1385
- const attempt = this.attemptWithoutThrowing(value);
1386
- if (!("failure" in attempt))
1387
- return { value: attempt.value };
1388
- const { exception, raised, issues } = attempt.failure;
1389
- return {
1390
- issues: issues.map((issue) => raised ? { ...issue, cause: exception() } : withLazyCause(issue, exception))
1391
- };
1392
- }
1393
- preHandle(value) {
1394
- return value;
1395
- }
1396
- afterHandle(value) {
1397
- return value;
1398
- }
1399
- handle(value) {
1400
- return this.type.schema.match(value) ? value : MISMATCH;
1401
- }
1402
- attemptWithoutThrowing(value) {
1403
- try {
1404
- return this.attempt(value);
1405
- } catch (error) {
1406
- return {
1407
- failure: this.raise(error instanceof Exception2 ? error : this.invalidValue(error))
1408
- };
1409
- }
1433
+ function satisfies(available, requested) {
1434
+ const [major, minor, patch] = versionParts(available);
1435
+ const [wantedMajor, wantedMinor, wantedPatch] = versionParts(requested);
1436
+ if (major !== wantedMajor)
1437
+ return false;
1438
+ if (major === 0 && minor !== wantedMinor)
1439
+ return false;
1440
+ if (minor !== wantedMinor)
1441
+ return minor > wantedMinor;
1442
+ return patch >= wantedPatch;
1443
+ }
1444
+ var HOOKS = ["onSet", "onSetAll"];
1445
+ var internals = new WeakMap;
1446
+ function pluginsOf2(value) {
1447
+ const Class = typeof value === "function" ? value : typeof value === "object" && value !== null ? value.constructor : undefined;
1448
+ return Class?.[RecordPlugins] ?? [];
1449
+ }
1450
+ function isRecordPlugin2(value) {
1451
+ return typeof value === "object" && value !== null && value[Kind2] === "record-plugin";
1452
+ }
1453
+ function hasPlugin2(value, plugin) {
1454
+ const plugins = pluginsOf2(value);
1455
+ if (typeof plugin === "string") {
1456
+ return plugins.some((applied) => applied.name === plugin);
1410
1457
  }
1411
- attempt(value) {
1412
- const { name, optional } = this.type;
1413
- this[State2] = 0 /* Declared */;
1414
- if (value === undefined) {
1415
- if (!optional) {
1416
- return {
1417
- failure: {
1418
- exception: () => new PropertyRequired2({ source: name }),
1419
- raised: false,
1420
- issues: [
1421
- {
1422
- message: "is required",
1423
- path: [],
1424
- code: "required",
1425
- source: name
1426
- }
1427
- ]
1428
- }
1429
- };
1458
+ if (plugins.includes(plugin))
1459
+ return true;
1460
+ return typeof value === "function" ? value === plugin.record || value.prototype instanceof plugin.record : value instanceof plugin.record;
1461
+ }
1462
+ function membersOf(Record, name) {
1463
+ const descriptors = {};
1464
+ const hooks = {};
1465
+ for (let prototype = Record.prototype;prototype !== null && !Object.hasOwn(prototype.constructor, "blueprint"); prototype = Object.getPrototypeOf(prototype)) {
1466
+ for (const member of Object.getOwnPropertyNames(prototype)) {
1467
+ if (member === "constructor")
1468
+ continue;
1469
+ const descriptor = Object.getOwnPropertyDescriptor(prototype, member);
1470
+ if (HOOKS.includes(member)) {
1471
+ hooks[member] ??= descriptor.value;
1472
+ continue;
1430
1473
  }
1431
- this[State2] = 4 /* Done */;
1432
- return { value };
1433
- }
1434
- if (!this.type.schema.matchShape(value)) {
1435
- return { failure: this.mismatch(value) };
1436
- }
1437
- try {
1438
- this[State2] = 1 /* PreHandling */;
1439
- value = this.runHandler(() => this.preHandle(value));
1440
- this[State2] = 2 /* Handling */;
1441
- const handled = this.handle(value);
1442
- if (handled === MISMATCH) {
1443
- return { failure: this.mismatch(value) };
1474
+ if (RESERVED_RECORD_KEYS2.includes(member)) {
1475
+ throw new InvalidRecordPlugin2({
1476
+ source: name,
1477
+ details: {
1478
+ reason: `"${member}" is a member of every record and cannot be replaced`
1479
+ }
1480
+ });
1444
1481
  }
1445
- value = handled;
1446
- this[State2] = 3 /* AfterHandling */;
1447
- value = this.runHandler(() => this.afterHandle(value));
1448
- } catch (error) {
1449
- if (error instanceof Exception2)
1450
- return { failure: this.raise(error) };
1451
- throw error;
1482
+ descriptors[member] ??= descriptor;
1452
1483
  }
1453
- this[State2] = 4 /* Done */;
1454
- return { value };
1455
1484
  }
1456
- runHandler(handler) {
1457
- try {
1458
- return handler();
1459
- } catch (error) {
1460
- if (error instanceof Exception2)
1461
- throw error;
1462
- throw this.invalidValue(error);
1463
- }
1485
+ return { descriptors: Object.freeze(descriptors), hooks };
1486
+ }
1487
+ function recordPluginOf2(name, Record, { version = "1.0.0", description } = {}) {
1488
+ if (typeof name !== "string" || name.length === 0) {
1489
+ throw new InvalidRecordPlugin2({
1490
+ source: String(name),
1491
+ details: { reason: "its name must be a non-empty string" }
1492
+ });
1464
1493
  }
1465
- invalidValue(error) {
1466
- return new InvalidPropertyValue2({
1467
- source: this.type.name,
1494
+ if (!isRecordClass2(Record)) {
1495
+ throw new InvalidRecordPlugin2({
1496
+ source: name,
1497
+ details: { reason: "it must be made from a class of `recordOf`" }
1498
+ });
1499
+ }
1500
+ if (Record[IdentifiedRecord]) {
1501
+ throw new InvalidRecordPlugin2({
1502
+ source: name,
1468
1503
  details: {
1469
- errors: [error instanceof Error ? error.message : String(error)]
1470
- },
1471
- cause: error
1504
+ reason: "it must be made from a record, not an entity: plugins have no identity"
1505
+ }
1472
1506
  });
1473
1507
  }
1474
- raise(exception) {
1475
- return {
1476
- exception: () => exception,
1477
- raised: true,
1478
- issues: [
1479
- {
1480
- message: exception.message,
1481
- path: [],
1482
- code: "invalid_value",
1483
- source: this.type.name
1484
- }
1485
- ]
1486
- };
1508
+ if (!isVersion(version)) {
1509
+ throw new InvalidRecordPlugin2({
1510
+ source: name,
1511
+ details: { reason: `"${version}" is not a semantic version` }
1512
+ });
1487
1513
  }
1488
- mismatch(value) {
1489
- const { name, schema } = this.type;
1490
- const errors = schema.type.Errors(value);
1491
- const issues = errors.map((error) => ({
1492
- message: error.message,
1493
- path: segmentsOf(error.instancePath),
1494
- code: "invalid_value",
1495
- source: name
1496
- }));
1497
- return {
1498
- exception: once(() => new InvalidPropertyValue2({
1514
+ const requires = pluginsOf2(Record);
1515
+ const inherited = new Set(requires.flatMap((required) => required.keys));
1516
+ const declaration = Record;
1517
+ const keys = Object.freeze(Object.keys(declaration.blueprint.properties).filter((key) => !inherited.has(key)));
1518
+ const own = membersOf(Record, name);
1519
+ const plugin = Object.freeze({
1520
+ [Kind2]: "record-plugin",
1521
+ name,
1522
+ version,
1523
+ ...description === undefined ? {} : { description },
1524
+ record: Record,
1525
+ keys,
1526
+ members: Object.freeze(Object.keys(own.descriptors)),
1527
+ options: declaration.options,
1528
+ requires,
1529
+ is: (value) => hasPlugin2(value, plugin),
1530
+ toJSON: () => descriptorOf(plugin)
1531
+ });
1532
+ internals.set(plugin, own);
1533
+ return plugin;
1534
+ }
1535
+ function descriptorOf(plugin) {
1536
+ return Object.freeze({
1537
+ kind: "record-plugin",
1538
+ name: plugin.name,
1539
+ version: plugin.version,
1540
+ ...plugin.description === undefined ? {} : { description: plugin.description },
1541
+ keys: plugin.keys,
1542
+ sensitive: plugin.options.sensitive,
1543
+ unique: plugin.options.unique,
1544
+ change: plugin.options.change,
1545
+ members: plugin.members,
1546
+ requires: Object.freeze(plugin.requires.map(({ name, version }) => Object.freeze({ name, version })))
1547
+ });
1548
+ }
1549
+ function applyPlugins(blueprint, plugins, name, reserved) {
1550
+ const flat = [];
1551
+ for (const plugin of plugins) {
1552
+ if (!isRecordPlugin2(plugin)) {
1553
+ throw new InvalidRecordPlugin2({
1499
1554
  source: name,
1500
- details: { errors: errors.map(describeError) }
1501
- })),
1502
- raised: false,
1503
- issues: issues.length > 0 ? issues : [
1504
- {
1505
- message: "is invalid",
1506
- path: [],
1507
- code: "invalid_value",
1508
- source: name
1555
+ details: {
1556
+ reason: "`options.plugins` takes plugins made by `recordPluginOf`"
1509
1557
  }
1510
- ]
1511
- };
1512
- }
1513
- }
1514
-
1515
- // src/core/property/property.ts
1516
- function propertyOf2({
1517
- name,
1518
- schema,
1519
- optional,
1520
- sensitive = false,
1521
- unique = false,
1522
- default: _default,
1523
- preHandle: onPreHandle,
1524
- afterHandle: onAfterHandle
1525
- }) {
1526
- const valueSchema = optional ? Type.Optional(schema) : schema;
1527
- const SchemaConstructor = schemaOf2(name, valueSchema);
1528
- const type = {
1529
- [Demo2]: _default,
1530
- name,
1531
- schema: new SchemaConstructor,
1532
- optional: optional ?? false,
1533
- sensitive,
1534
- unique
1535
- };
1536
-
1537
- class DefineProperty extends AbstractDefineProperty2 {
1538
- get type() {
1539
- return type;
1558
+ });
1540
1559
  }
1541
- preHandle(value) {
1542
- return onPreHandle ? onPreHandle(value) : value;
1560
+ for (const applied of [...plugin.requires, plugin]) {
1561
+ if (flat.includes(applied))
1562
+ continue;
1563
+ if (flat.some((other) => other.name === applied.name)) {
1564
+ throw new RecordPluginConflict2({
1565
+ source: name,
1566
+ details: {
1567
+ plugin: applied.name,
1568
+ key: applied.name,
1569
+ owner: "another plugin"
1570
+ }
1571
+ });
1572
+ }
1573
+ flat.push(applied);
1574
+ }
1575
+ }
1576
+ const owners = new Map(Object.keys(blueprint.properties).map((key) => [key, `record "${name}"`]));
1577
+ for (const member of reserved)
1578
+ owners.set(member, `record "${name}"`);
1579
+ const claim = (plugin, key) => {
1580
+ const owner = owners.get(key);
1581
+ if (owner !== undefined) {
1582
+ throw new RecordPluginConflict2({
1583
+ source: name,
1584
+ details: { plugin: plugin.name, key, owner }
1585
+ });
1543
1586
  }
1544
- afterHandle(value) {
1545
- return onAfterHandle ? onAfterHandle(value) : value;
1587
+ owners.set(key, `plugin "${plugin.name}"`);
1588
+ };
1589
+ const descriptors = {};
1590
+ const hooks = [];
1591
+ for (const plugin of flat) {
1592
+ for (const key of plugin.keys)
1593
+ claim(plugin, key);
1594
+ const own = internals.get(plugin);
1595
+ for (const member of plugin.members) {
1596
+ claim(plugin, member);
1597
+ descriptors[member] = own.descriptors[member];
1546
1598
  }
1547
- }
1548
-
1549
- class Property extends AbstractProperty2 {
1550
- static type = type;
1551
- createDefinition() {
1552
- return new DefineProperty;
1599
+ if (own.hooks.onSet || own.hooks.onSetAll) {
1600
+ hooks.push(Object.freeze({
1601
+ plugin,
1602
+ keys: Object.freeze(Object.keys(plugin.record.blueprint.properties)),
1603
+ ...own.hooks
1604
+ }));
1553
1605
  }
1554
1606
  }
1555
- return Property;
1556
- }
1557
-
1558
- // src/core/blueprint/optional.ts
1559
- import { Type as Type2 } from "typebox";
1560
- var cache2 = new WeakMap;
1561
- function optionalOf2(given) {
1562
- const field = fieldOf(given);
1563
- const cached = cache2.get(field);
1564
- if (cached)
1565
- return cached;
1566
- if (isOptionalRecord2(field))
1567
- return field;
1568
- if (isRecordClass2(field)) {
1569
- const optional = Object.freeze(withStandard({
1570
- [Kind2]: "record",
1571
- record: field,
1572
- optional: true
1573
- }, (value) => value === undefined ? { value } : field.safeParse(value)));
1574
- cache2.set(field, optional);
1575
- return optional;
1576
- }
1577
- if (isBlueprint2(field)) {
1578
- if (field.optional)
1579
- return field;
1580
- const optional = {
1581
- ...field,
1582
- optional: true,
1583
- safeParse: (value) => value === undefined ? { value } : field.safeParse(value)
1584
- };
1585
- Object.freeze(withStandard(optional, (value) => optional.safeParse(value)));
1586
- cache2.set(field, optional);
1587
- return optional;
1588
- }
1589
- if (isBlueprintList2(field)) {
1590
- if (field.optional)
1591
- return field;
1592
- const optional = standardListOf({ ...field, optional: true });
1593
- cache2.set(field, optional);
1594
- return optional;
1595
- }
1596
- const Property = field;
1597
- if (Property.type.optional)
1598
- return Property;
1599
- const base = Property.type;
1600
- const SchemaConstructor = schemaOf2(base.name, Type2.Optional(base.schema.type.Type()));
1601
- const type = {
1602
- ...base,
1603
- schema: new SchemaConstructor,
1604
- optional: true
1607
+ return {
1608
+ blueprint: flat.length === 0 ? blueprint : blueprint.join(...flat.map((plugin) => plugin.record.blueprint)),
1609
+ plugins: Object.freeze(flat),
1610
+ sensitive: flat.flatMap((plugin) => plugin.options.sensitive),
1611
+ unique: flat.flatMap((plugin) => plugin.options.unique),
1612
+ change: flat.flatMap((plugin) => plugin.options.change),
1613
+ descriptors,
1614
+ hooks: Object.freeze(hooks)
1605
1615
  };
1606
- const Optional = derivePropertyOf(Property, type);
1607
- recordPropertyLineage(Optional, { kind: "optional", base: Property });
1608
- cache2.set(Property, Optional);
1609
- return Optional;
1610
1616
  }
1611
1617
 
1612
- // src/core/blueprint/blueprint.ts
1613
- function blueprintBuilderOf2(...base) {
1614
- return (sourcesOrDefine, maybeDefine) => {
1615
- const [sources, define] = typeof sourcesOrDefine === "function" ? [[], sourcesOrDefine] : [
1616
- sourcesOrDefine,
1617
- maybeDefine
1618
- ];
1619
- const all = [...base, ...sources];
1620
- return create(all.flatMap((source) => isBlueprint2(source) ? source.plugins : [source]), { ...define(contextOf(all)) });
1621
- };
1618
+ // src/core/record/record.ts
1619
+ function recordBuilderOf2(...catalogs) {
1620
+ const context = pluginContextOf(catalogs);
1621
+ return (blueprint, options = {}) => declareRecord(AbstractRecord2, blueprint, options, "record", [], context);
1622
1622
  }
1623
- function blueprintFromProperties(plugins, properties) {
1624
- return create(plugins, { ...properties });
1623
+ function pluginContextOf(catalogs) {
1624
+ return Object.freeze(Object.assign({}, ...catalogs.map((catalog) => catalog.plugins)));
1625
1625
  }
1626
- function create(plugins, properties) {
1627
- const shape = {};
1628
- for (const [key, field] of Object.entries(properties)) {
1629
- properties[key] = fieldOf(field);
1630
- shape[key] = schemaOfField(key, properties[key]);
1626
+ var recordOf3 = recordBuilderOf2();
1627
+ function declareRecord(Base, declared, { plugins = [], name: declaredName, ...declaredOptions }, fallback, reserved = [], context = {}) {
1628
+ if (declaredName !== undefined && !isRecordName(declaredName)) {
1629
+ throw new InvalidRecordDeclaration2({
1630
+ source: fallback,
1631
+ details: `"name" must be an identifier, got ${JSON.stringify(declaredName)}.`
1632
+ });
1631
1633
  }
1632
- const SchemaConstructor = schemaOf2("blueprint", Type3.Object(shape));
1633
- const blueprint = {
1634
- [Kind2]: "blueprint",
1635
- plugins: Object.freeze([...new Set(plugins)]),
1636
- properties: Object.freeze(properties),
1637
- schema: new SchemaConstructor,
1638
- optional: false,
1639
- safeParse: (value) => parseCollection(properties, value, [], "blueprint"),
1640
- join: (...others) => create([blueprint, ...others].flatMap((source) => source.plugins), Object.assign({}, ...[blueprint, ...others].map((source) => source.properties)))
1641
- };
1642
- return Object.freeze(withStandard(blueprint, (value) => blueprint.safeParse(value)));
1643
- }
1644
- var HELPERS = { listOf: listOf2, optionalOf: optionalOf2, propertyOf: propertyOf2 };
1645
- var RESERVED_KEYS = ["optional", "list", ...Object.keys(HELPERS)];
1646
- function contextOf(sources) {
1647
- for (const key of RESERVED_KEYS) {
1648
- const reserving = sources.find((source) => Object.hasOwn(source.properties, key));
1649
- if (reserving) {
1650
- throw new ReservedBlueprintKey2({
1651
- source: isBlueprint2(reserving) ? "blueprint" : reserving.name,
1634
+ const name = declaredName ?? fallback;
1635
+ const applied = applyPlugins(declared, typeof plugins === "function" ? plugins(context) : plugins, name, reserved);
1636
+ const blueprint = applied.blueprint;
1637
+ const sensitive = [
1638
+ ...declaredOptions.sensitive ?? [],
1639
+ ...applied.sensitive
1640
+ ];
1641
+ const unique = [...declaredOptions.unique ?? [], ...applied.unique];
1642
+ const declaredChange = declaredOptions.change ?? [];
1643
+ const keys = Object.keys(blueprint.properties);
1644
+ const change = keys.filter((key) => declaredChange.includes(key) || applied.change.includes(key));
1645
+ for (const key of [...sensitive, ...unique, ...declaredChange]) {
1646
+ if (!keys.includes(key)) {
1647
+ throw new UndeclaredRecordProperty2({
1648
+ source: name,
1652
1649
  details: { key }
1653
1650
  });
1654
1651
  }
1655
1652
  }
1656
- const factories = Object.assign({}, ...sources.map((source) => isBlueprint2(source) ? {} : source.factories ?? {}));
1657
- for (const key of RESERVED_KEYS) {
1658
- if (Object.hasOwn(factories, key)) {
1659
- const reserving = sources.find((source) => !isBlueprint2(source) && Object.hasOwn(source.factories ?? {}, key));
1660
- throw new ReservedBlueprintKey2({
1661
- source: reserving.name,
1662
- details: { key }
1653
+ const methods = change.length > 0 ? [CHANGE_MEMBER] : [];
1654
+ for (const member of Object.keys(applied.descriptors)) {
1655
+ if (methods.includes(member)) {
1656
+ const plugin = applied.plugins.find((plugin) => plugin.members.includes(member));
1657
+ throw new RecordPluginConflict2({
1658
+ source: name,
1659
+ details: {
1660
+ plugin: plugin.name,
1661
+ key: member,
1662
+ owner: `record "${name}" (the \`change\` namespace)`
1663
+ }
1663
1664
  });
1664
1665
  }
1665
1666
  }
1666
- const available = Object.assign({}, ...sources.map((source) => source.properties));
1667
- for (const key of Object.keys(factories))
1668
- delete available[key];
1669
- const mirror = (fields, map) => Object.fromEntries(Object.entries(fields).map(([key, field]) => [key, map(field)]));
1670
- const list = mirror(available, (field) => listOf2(field));
1671
- const callable = (field) => isProperty2(field) ? callableOf(field) : field;
1672
- return Object.freeze({
1673
- ...mirror(available, callable),
1674
- ...factories,
1675
- ...HELPERS,
1676
- optional: Object.freeze(mirror(available, (field) => callable(optionalOf2(field)))),
1677
- list: Object.freeze({
1678
- ...list,
1679
- optional: Object.freeze(mirror(list, optionalOf2))
1680
- })
1667
+ for (const key of keys) {
1668
+ if (RESERVED_RECORD_KEYS2.includes(key) || reserved.includes(key) || methods.includes(key)) {
1669
+ throw new ReservedRecordKey2({ source: name, details: { key } });
1670
+ }
1671
+ }
1672
+ const options = Object.freeze({
1673
+ sensitive: Object.freeze(keys.filter((key) => sensitive.includes(key) || isMarked(blueprint.properties[key], "sensitive", { lists: true }))),
1674
+ unique: Object.freeze(keys.filter((key) => unique.includes(key) || isMarked(blueprint.properties[key], "unique"))),
1675
+ change: Object.freeze([...change])
1681
1676
  });
1682
- }
1683
- function schemaOfField(key, field) {
1684
- if (isBlueprintList2(field)) {
1685
- const { item, minItems, maxItems } = field;
1686
- const schema = Type3.Array(schemaOfField(key, item), {
1687
- ...minItems === undefined ? {} : { minItems },
1688
- ...maxItems === undefined ? {} : { maxItems }
1689
- });
1690
- return field.optional ? Type3.Optional(schema) : schema;
1677
+
1678
+ class DeclaredRecord extends Base {
1679
+ static [Kind2] = "record";
1680
+ static blueprint = blueprint;
1681
+ static options = options;
1682
+ static plugins = applied.plugins;
1683
+ static [RecordPlugins] = applied.plugins;
1684
+ static [RecordPluginHooks] = applied.hooks;
1691
1685
  }
1692
- if (isBlueprint2(field)) {
1693
- const schema = field.schema.type.Type();
1694
- return field.optional ? Type3.Optional(schema) : schema;
1686
+ if (declaredName !== undefined) {
1687
+ declareRecordName(DeclaredRecord, declaredName);
1695
1688
  }
1696
- const record = recordFieldOf2(field);
1697
- if (record) {
1698
- const schema = record.record.blueprint.schema.type.Type();
1699
- return record.optional ? Type3.Optional(schema) : schema;
1689
+ Object.defineProperty(DeclaredRecord, "recordName", {
1690
+ get() {
1691
+ return declaredName ?? this.name;
1692
+ },
1693
+ configurable: true
1694
+ });
1695
+ exposeRecordKeys(DeclaredRecord.prototype, keys);
1696
+ for (const [member, descriptor] of Object.entries(applied.descriptors)) {
1697
+ Object.defineProperty(DeclaredRecord.prototype, member, descriptor);
1700
1698
  }
1701
- if (isProperty2(field)) {
1702
- return field.type.schema.type.Type();
1699
+ if (change.length > 0) {
1700
+ Object.defineProperty(DeclaredRecord.prototype, CHANGE_MEMBER, {
1701
+ get() {
1702
+ let namespace = changeNamespaces.get(this);
1703
+ if (namespace === undefined) {
1704
+ namespace = Object.freeze(Object.fromEntries(change.map((key) => [
1705
+ key,
1706
+ (value) => this.set(key, value)
1707
+ ])));
1708
+ changeNamespaces.set(this, namespace);
1709
+ }
1710
+ return namespace;
1711
+ },
1712
+ configurable: true
1713
+ });
1703
1714
  }
1704
- throw new UndeclaredBlueprintProperty2({
1705
- source: "blueprint",
1706
- details: { key }
1707
- });
1715
+ Object.defineProperty(DeclaredRecord, "name", { value: name });
1716
+ return DeclaredRecord;
1708
1717
  }
1718
+ var CHANGE_MEMBER = "change";
1719
+ var changeNamespaces = new WeakMap;
1709
1720
 
1710
- export { __export, isProperty2, schemaOf2, Demo2, recordPropertyLineage, propertyLineageOf2, fieldOf, Kind2, isBlueprint2, isRecordClass2, isOptionalRecord2, recordFieldOf2, recordNameOf2, declaredRecordNameOf, listOf2, isBlueprintList2, ExceptionLayers2, UNEXPECTED_ERROR2, Exception2, isDeclaredException, declaredMessageOf, exceptionOf2, Meta2, InvalidRecordValue2, InvalidRecordDeclaration2, UndeclaredRecordProperty2, ReservedRecordKey2, InvalidRecordPlugin2, RecordPluginConflict2, UnknownRecordPlugin2, IncompatibleRecordPlugin2, exports_exceptions, MASK2, RESERVED_RECORD_KEYS2, recordDataOf, recordFieldDataOf, storeRecordValue, runRecordEffects, AbstractRecord2, IdentifiedRecord, isVersion, versionParts, pluginsOf2, isRecordPlugin2, hasPlugin2, recordPluginOf2, recordBuilderOf2, pluginContextOf, recordOf3, declareRecord, ReservedBlueprintKey2, UndeclaredBlueprintProperty2, exports_exceptions2, PropertyRequired2, InvalidPropertyValue2, exports_exceptions3, State2, AbstractProperty2, PropertyStates2, AbstractDefineProperty2, propertyOf2, optionalOf2, blueprintBuilderOf2, blueprintFromProperties };
1721
+ export { __export, isProperty2, schemaOf2, Demo2, recordPropertyLineage, propertyLineageOf2, fieldOf, Kind2, isBlueprint2, isRecordClass2, isOptionalRecord2, recordFieldOf2, recordNameOf2, declaredRecordNameOf, fieldsOf, isPlainObject, listOf2, isBlueprintList2, ExceptionLayers2, UNEXPECTED_ERROR2, Exception2, isDeclaredException, declaredMessageOf, exceptionOf2, Meta2, InvalidRecordValue2, InvalidRecordDeclaration2, UndeclaredRecordProperty2, ReservedRecordKey2, InvalidRecordPlugin2, RecordPluginConflict2, UnknownRecordPlugin2, IncompatibleRecordPlugin2, exports_exceptions, MASK2, RESERVED_RECORD_KEYS2, recordDataOf, recordFieldDataOf, storeRecordValue, runRecordEffects, AbstractRecord2, State2, AbstractProperty2, PropertyStates2, PropertyRequired2, InvalidPropertyValue2, exports_exceptions2, AbstractDefineProperty2, propertyOf2, ReservedBlueprintKey2, UndeclaredBlueprintProperty2, exports_exceptions3, optionalOf2, blueprintBuilderOf2, blueprintFromProperties, IdentifiedRecord, isVersion, satisfies, pluginsOf2, isRecordPlugin2, hasPlugin2, recordPluginOf2, recordBuilderOf2, pluginContextOf, recordOf3, declareRecord };