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