@endora-commerce/mod-custom-fields 0.0.0-stage → 0.100.0

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 (78) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +58 -2
  3. package/dist/admin/api/custom-fields-client.d.ts +10 -0
  4. package/dist/admin/api/custom-fields-client.d.ts.map +1 -0
  5. package/dist/admin/api/custom-fields-client.js +11 -0
  6. package/dist/admin/api/custom-fields-client.js.map +1 -0
  7. package/dist/admin/index.d.ts +44 -0
  8. package/dist/admin/index.d.ts.map +1 -0
  9. package/dist/admin/index.js +51 -0
  10. package/dist/admin/index.js.map +1 -0
  11. package/dist/admin/pages/CustomFieldsPage.d.ts +10 -0
  12. package/dist/admin/pages/CustomFieldsPage.d.ts.map +1 -0
  13. package/dist/admin/pages/CustomFieldsPage.js +118 -0
  14. package/dist/admin/pages/CustomFieldsPage.js.map +1 -0
  15. package/dist/backend/commands/definition-commands.d.ts +22 -0
  16. package/dist/backend/commands/definition-commands.d.ts.map +1 -0
  17. package/dist/backend/commands/definition-commands.js +115 -0
  18. package/dist/backend/commands/definition-commands.js.map +1 -0
  19. package/dist/backend/entities/custom-field-definition.entity.d.ts +37 -0
  20. package/dist/backend/entities/custom-field-definition.entity.d.ts.map +1 -0
  21. package/dist/backend/entities/custom-field-definition.entity.js +98 -0
  22. package/dist/backend/entities/custom-field-definition.entity.js.map +1 -0
  23. package/dist/backend/entities/custom-field-option.entity.d.ts +25 -0
  24. package/dist/backend/entities/custom-field-option.entity.d.ts.map +1 -0
  25. package/dist/backend/entities/custom-field-option.entity.js +79 -0
  26. package/dist/backend/entities/custom-field-option.entity.js.map +1 -0
  27. package/dist/backend/index.d.ts +79 -0
  28. package/dist/backend/index.d.ts.map +1 -0
  29. package/dist/backend/index.js +94 -0
  30. package/dist/backend/index.js.map +1 -0
  31. package/dist/backend/routes.admin.d.ts +14 -0
  32. package/dist/backend/routes.admin.d.ts.map +1 -0
  33. package/dist/backend/routes.admin.js +179 -0
  34. package/dist/backend/routes.admin.js.map +1 -0
  35. package/dist/backend/services/custom-field-definition-apply.d.ts +63 -0
  36. package/dist/backend/services/custom-field-definition-apply.d.ts.map +1 -0
  37. package/dist/backend/services/custom-field-definition-apply.js +221 -0
  38. package/dist/backend/services/custom-field-definition-apply.js.map +1 -0
  39. package/dist/backend/services/custom-field-definition.service.d.ts +68 -0
  40. package/dist/backend/services/custom-field-definition.service.d.ts.map +1 -0
  41. package/dist/backend/services/custom-field-definition.service.js +205 -0
  42. package/dist/backend/services/custom-field-definition.service.js.map +1 -0
  43. package/dist/backend/services/custom-field-definitions-cache.d.ts +46 -0
  44. package/dist/backend/services/custom-field-definitions-cache.d.ts.map +1 -0
  45. package/dist/backend/services/custom-field-definitions-cache.js +82 -0
  46. package/dist/backend/services/custom-field-definitions-cache.js.map +1 -0
  47. package/dist/backend/services/custom-field-read-port.d.ts +61 -0
  48. package/dist/backend/services/custom-field-read-port.d.ts.map +1 -0
  49. package/dist/backend/services/custom-field-read-port.js +82 -0
  50. package/dist/backend/services/custom-field-read-port.js.map +1 -0
  51. package/dist/backend/services/custom-field-registry.d.ts +41 -0
  52. package/dist/backend/services/custom-field-registry.d.ts.map +1 -0
  53. package/dist/backend/services/custom-field-registry.js +24 -0
  54. package/dist/backend/services/custom-field-registry.js.map +1 -0
  55. package/dist/backend/services/custom-field-value.service.d.ts +56 -0
  56. package/dist/backend/services/custom-field-value.service.d.ts.map +1 -0
  57. package/dist/backend/services/custom-field-value.service.js +175 -0
  58. package/dist/backend/services/custom-field-value.service.js.map +1 -0
  59. package/dist/manifest.d.ts +177 -0
  60. package/dist/manifest.d.ts.map +1 -0
  61. package/dist/manifest.js +130 -0
  62. package/dist/manifest.js.map +1 -0
  63. package/dist/migrations/20260718T200338_custom_fields_init.d.ts +20 -0
  64. package/dist/migrations/20260718T200338_custom_fields_init.d.ts.map +1 -0
  65. package/dist/migrations/20260718T200338_custom_fields_init.js +60 -0
  66. package/dist/migrations/20260718T200338_custom_fields_init.js.map +1 -0
  67. package/dist/migrations/index.d.ts +27 -0
  68. package/dist/migrations/index.d.ts.map +1 -0
  69. package/dist/migrations/index.js +29 -0
  70. package/dist/migrations/index.js.map +1 -0
  71. package/dist/ports/index.d.ts +123 -0
  72. package/dist/ports/index.d.ts.map +1 -0
  73. package/dist/ports/index.js +2 -0
  74. package/dist/ports/index.js.map +1 -0
  75. package/i18n/en.json +15 -0
  76. package/i18n/pl.json +15 -0
  77. package/package.json +99 -3
  78. package/tailwind.css +14 -0
@@ -0,0 +1,63 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql';
2
+ import { type CreateCustomFieldDefinitionRequest, type CustomFieldOptionDto, type SupportedEntityType, type UpdateCustomFieldDefinitionRequest } from '@endora-commerce/contracts';
3
+ import { CustomFieldDefinition } from '../entities/custom-field-definition.entity.js';
4
+ import { CustomFieldOption } from '../entities/custom-field-option.entity.js';
5
+ /**
6
+ * Transactional apply functions for custom-field definition/option writes
7
+ * (feature 061, contracts/custom-fields-product-host.md §3).
8
+ *
9
+ * Every function runs entirely on the CALLER-provided EntityManager and
10
+ * enforces the same invariants as the public CRUD (duplicate key, options
11
+ * rule, entity type known, value-type lock via the probe binding,
12
+ * option-in-use). They dispatch NO command, write NO audit entry, and publish
13
+ * NO cache invalidation — a host command audits its composite operation and
14
+ * calls `publishInvalidate` post-commit; the module's own definition commands
15
+ * share these bodies so there is a single write path (Principle XIII).
16
+ */
17
+ /** Typed failures the routes map to HTTP status codes. */
18
+ export declare class CustomFieldDefinitionError extends Error {
19
+ readonly code: 'not_found' | 'duplicate_key' | 'options_required' | 'options_forbidden' | 'entity_type_unknown' | 'value_type_locked' | 'option_in_use' | 'key_changed' | 'invalid_key';
20
+ constructor(code: 'not_found' | 'duplicate_key' | 'options_required' | 'options_forbidden' | 'entity_type_unknown' | 'value_type_locked' | 'option_in_use' | 'key_changed' | 'invalid_key', message: string);
21
+ }
22
+ /**
23
+ * Change-guard probes over the host's value storage (structurally satisfied by
24
+ * `CustomFieldValueService`). Optional: when absent, the corresponding guard
25
+ * is the caller's responsibility (e.g. a host running its own authoritative
26
+ * in-use checks first — research §R9).
27
+ */
28
+ export interface DefinitionChangeProbes {
29
+ hasStoredValues(em: EntityManager, entityType: SupportedEntityType, key: string): Promise<boolean>;
30
+ isOptionInUse(em: EntityManager, entityType: SupportedEntityType, key: string, optionValue: string): Promise<boolean>;
31
+ }
32
+ /** Select fields require ≥ 1 option; non-select fields must not carry any. */
33
+ export declare function assertOptionsRule(valueType: string, options: CustomFieldOptionDto[]): void;
34
+ /** Create a definition (+ its options for select types) on the caller's EM. */
35
+ export declare function applyCreateDefinition(em: EntityManager, input: CreateCustomFieldDefinitionRequest): Promise<CustomFieldDefinition>;
36
+ /** Patch a definition's scalar/label/config fields on the caller's EM. */
37
+ export declare function applyUpdateDefinition(em: EntityManager, id: string, patch: UpdateCustomFieldDefinitionRequest, probes?: DefinitionChangeProbes): Promise<CustomFieldDefinition>;
38
+ /**
39
+ * Rename a definition's key on the caller's EM — the one path by which a key
40
+ * changes after create (`specs/134-paid-module-extraction/research.md` D12).
41
+ *
42
+ * Not an edit: `UpdateCustomFieldDefinitionRequest` omits `key` on purpose,
43
+ * because every host stores its values **under** the key and an ordinary rename
44
+ * would orphan them. The caller of this function is therefore one that renames
45
+ * the host's values in the same transaction — today a connector repairing keys
46
+ * it derived itself, with `catalog` moving the product values beside it.
47
+ *
48
+ * `expectedKey` is the key the caller planned from. A definition whose key has
49
+ * moved since is refused with `key_changed` rather than renamed from a state the
50
+ * caller never saw. Flushes before it returns, so a caller sequencing a
51
+ * two-phase rename (park, then move) gets its statements in the order it asked
52
+ * for them and a unique-index violation surfaces at the call that caused it.
53
+ */
54
+ export declare function applyRenameDefinitionKey(em: EntityManager, id: string, expectedKey: string, newKey: string): Promise<CustomFieldDefinition>;
55
+ /** Delete a definition on the caller's EM. Options cascade; host values stay dormant (FR-010). */
56
+ export declare function applyDeleteDefinition(em: EntityManager, id: string): Promise<CustomFieldDefinition>;
57
+ /** Add one option to a select/multiselect definition on the caller's EM. */
58
+ export declare function applyCreateOption(em: EntityManager, definitionId: string, input: CustomFieldOptionDto): Promise<CustomFieldOption>;
59
+ /** Patch one option's label/default/ordering on the caller's EM. */
60
+ export declare function applyUpdateOption(em: EntityManager, definitionId: string, optionId: string, patch: Partial<Pick<CustomFieldOptionDto, 'label' | 'labelDefault' | 'isDefault' | 'sortOrder'>>): Promise<CustomFieldOption>;
61
+ /** Delete one option on the caller's EM (in-use guard via the probe binding). */
62
+ export declare function applyDeleteOption(em: EntityManager, definitionId: string, optionId: string, probes?: DefinitionChangeProbes): Promise<CustomFieldOption>;
63
+ //# sourceMappingURL=custom-field-definition-apply.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-field-definition-apply.d.ts","sourceRoot":"","sources":["../../../src/backend/services/custom-field-definition-apply.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAEL,KAAK,kCAAkC,EACvC,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,kCAAkC,EACxC,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,qBAAqB,EAAE,MAAM,+CAA+C,CAAC;AACtF,OAAO,EAAE,iBAAiB,EAAE,MAAM,2CAA2C,CAAC;AAG9E;;;;;;;;;;;GAWG;AAEH,0DAA0D;AAC1D,qBAAa,0BAA2B,SAAQ,KAAK;IAEjD,QAAQ,CAAC,IAAI,EACT,WAAW,GACX,eAAe,GACf,kBAAkB,GAClB,mBAAmB,GACnB,qBAAqB,GACrB,mBAAmB,GACnB,eAAe,GACf,aAAa,GACb,aAAa;gBATR,IAAI,EACT,WAAW,GACX,eAAe,GACf,kBAAkB,GAClB,mBAAmB,GACnB,qBAAqB,GACrB,mBAAmB,GACnB,eAAe,GACf,aAAa,GACb,aAAa,EACjB,OAAO,EAAE,MAAM;CAKlB;AAID;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACrC,eAAe,CAAC,EAAE,EAAE,aAAa,EAAE,UAAU,EAAE,mBAAmB,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnG,aAAa,CACX,EAAE,EAAE,aAAa,EACjB,UAAU,EAAE,mBAAmB,EAC/B,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,OAAO,CAAC,CAAC;CACrB;AAED,8EAA8E;AAC9E,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,oBAAoB,EAAE,GAAG,IAAI,CAQ1F;AAED,+EAA+E;AAC/E,wBAAsB,qBAAqB,CACzC,EAAE,EAAE,aAAa,EACjB,KAAK,EAAE,kCAAkC,GACxC,OAAO,CAAC,qBAAqB,CAAC,CAoChC;AAED,0EAA0E;AAC1E,wBAAsB,qBAAqB,CACzC,EAAE,EAAE,aAAa,EACjB,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,kCAAkC,EACzC,MAAM,CAAC,EAAE,sBAAsB,GAC9B,OAAO,CAAC,qBAAqB,CAAC,CAoBhC;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,wBAAwB,CAC5C,EAAE,EAAE,aAAa,EACjB,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,qBAAqB,CAAC,CA6BhC;AAED,kGAAkG;AAClG,wBAAsB,qBAAqB,CAAC,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAWzG;AAED,4EAA4E;AAC5E,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,aAAa,EACjB,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,oBAAoB,GAC1B,OAAO,CAAC,iBAAiB,CAAC,CAO5B;AAED,oEAAoE;AACpE,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,aAAa,EACjB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,oBAAoB,EAAE,OAAO,GAAG,cAAc,GAAG,WAAW,GAAG,WAAW,CAAC,CAAC,GAC/F,OAAO,CAAC,iBAAiB,CAAC,CAU5B;AAED,iFAAiF;AACjF,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,aAAa,EACjB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,sBAAsB,GAC9B,OAAO,CAAC,iBAAiB,CAAC,CAuB5B"}
@@ -0,0 +1,221 @@
1
+ import { customFieldDefinitionSchema, } from '@endora-commerce/contracts';
2
+ import { CustomFieldDefinition } from '../entities/custom-field-definition.entity.js';
3
+ import { CustomFieldOption } from '../entities/custom-field-option.entity.js';
4
+ import { isSupportedEntityType } from './custom-field-registry.js';
5
+ /**
6
+ * Transactional apply functions for custom-field definition/option writes
7
+ * (feature 061, contracts/custom-fields-product-host.md §3).
8
+ *
9
+ * Every function runs entirely on the CALLER-provided EntityManager and
10
+ * enforces the same invariants as the public CRUD (duplicate key, options
11
+ * rule, entity type known, value-type lock via the probe binding,
12
+ * option-in-use). They dispatch NO command, write NO audit entry, and publish
13
+ * NO cache invalidation — a host command audits its composite operation and
14
+ * calls `publishInvalidate` post-commit; the module's own definition commands
15
+ * share these bodies so there is a single write path (Principle XIII).
16
+ */
17
+ /** Typed failures the routes map to HTTP status codes. */
18
+ export class CustomFieldDefinitionError extends Error {
19
+ code;
20
+ constructor(code, message) {
21
+ super(message);
22
+ this.code = code;
23
+ this.name = 'CustomFieldDefinitionError';
24
+ }
25
+ }
26
+ const SELECT_TYPES = new Set(['select', 'multiselect']);
27
+ /** Select fields require ≥ 1 option; non-select fields must not carry any. */
28
+ export function assertOptionsRule(valueType, options) {
29
+ const isSelect = SELECT_TYPES.has(valueType);
30
+ if (isSelect && options.length === 0) {
31
+ throw new CustomFieldDefinitionError('options_required', 'Select fields require at least one option.');
32
+ }
33
+ if (!isSelect && options.length > 0) {
34
+ throw new CustomFieldDefinitionError('options_forbidden', 'Only select fields may carry options.');
35
+ }
36
+ }
37
+ /** Create a definition (+ its options for select types) on the caller's EM. */
38
+ export async function applyCreateDefinition(em, input) {
39
+ // command-coverage-ignore: apply-seam primitive — mutates on the invoking Command's
40
+ // transactional EM; the caller (custom_fields definition command or a host command,
41
+ // e.g. catalog.attribute.*) records the single audit row co-transactionally.
42
+ // Contract: specs/061-attributes-on-custom-fields/contracts/custom-fields-product-host.md.
43
+ if (!isSupportedEntityType(input.entityType)) {
44
+ throw new CustomFieldDefinitionError('entity_type_unknown', `Unknown entity type "${input.entityType}".`);
45
+ }
46
+ assertOptionsRule(input.valueType, input.options);
47
+ const existing = await em.findOne(CustomFieldDefinition, {
48
+ entityType: input.entityType,
49
+ key: input.key,
50
+ });
51
+ if (existing) {
52
+ throw new CustomFieldDefinitionError('duplicate_key', `A field "${input.key}" already exists on ${input.entityType}.`);
53
+ }
54
+ const def = em.create(CustomFieldDefinition, {
55
+ entityType: input.entityType,
56
+ key: input.key,
57
+ label: input.label,
58
+ labelDefault: input.labelDefault,
59
+ valueType: input.valueType,
60
+ required: input.required,
61
+ sortOrder: input.sortOrder,
62
+ config: input.config,
63
+ });
64
+ if (SELECT_TYPES.has(input.valueType)) {
65
+ // Flush the definition first: options reference it by id (no ORM relation),
66
+ // so MikroORM cannot infer the insert order and would violate the FK.
67
+ await em.flush();
68
+ for (const opt of input.options)
69
+ createOptionEntity(em, def.id, opt);
70
+ }
71
+ return def;
72
+ }
73
+ /** Patch a definition's scalar/label/config fields on the caller's EM. */
74
+ export async function applyUpdateDefinition(em, id, patch, probes) {
75
+ const def = await em.findOne(CustomFieldDefinition, { id });
76
+ if (!def)
77
+ throw new CustomFieldDefinitionError('not_found', `Custom field ${id} not found.`);
78
+ // FR-010: changing valueType while values exist is rejected (probe binding).
79
+ if (patch.valueType !== undefined && patch.valueType !== def.valueType && probes) {
80
+ const inUse = await probes.hasStoredValues(em, def.entityType, def.key);
81
+ if (inUse) {
82
+ throw new CustomFieldDefinitionError('value_type_locked', `Cannot change the value type of "${def.key}" while records hold values. Clear them first.`);
83
+ }
84
+ }
85
+ if (patch.label !== undefined)
86
+ def.label = patch.label;
87
+ if (patch.labelDefault !== undefined)
88
+ def.labelDefault = patch.labelDefault;
89
+ if (patch.valueType !== undefined)
90
+ def.valueType = patch.valueType;
91
+ if (patch.required !== undefined)
92
+ def.required = patch.required;
93
+ if (patch.sortOrder !== undefined)
94
+ def.sortOrder = patch.sortOrder;
95
+ if (patch.config !== undefined)
96
+ def.config = patch.config;
97
+ return def;
98
+ }
99
+ /**
100
+ * Rename a definition's key on the caller's EM — the one path by which a key
101
+ * changes after create (`specs/134-paid-module-extraction/research.md` D12).
102
+ *
103
+ * Not an edit: `UpdateCustomFieldDefinitionRequest` omits `key` on purpose,
104
+ * because every host stores its values **under** the key and an ordinary rename
105
+ * would orphan them. The caller of this function is therefore one that renames
106
+ * the host's values in the same transaction — today a connector repairing keys
107
+ * it derived itself, with `catalog` moving the product values beside it.
108
+ *
109
+ * `expectedKey` is the key the caller planned from. A definition whose key has
110
+ * moved since is refused with `key_changed` rather than renamed from a state the
111
+ * caller never saw. Flushes before it returns, so a caller sequencing a
112
+ * two-phase rename (park, then move) gets its statements in the order it asked
113
+ * for them and a unique-index violation surfaces at the call that caused it.
114
+ */
115
+ export async function applyRenameDefinitionKey(em, id, expectedKey, newKey) {
116
+ // command-coverage-ignore: apply-seam primitive — mutates on the caller's
117
+ // transactional EM. Its one caller is a system-invariant repair with no
118
+ // operator and no actor (a connector re-keying the attributes it derived,
119
+ // committed atomically with the host's value rename and the connector's own
120
+ // repair checkpoint); an audit entry for it would have nobody to attribute.
121
+ // Contract: specs/134-paid-module-extraction/research.md D12.
122
+ const def = await em.findOne(CustomFieldDefinition, { id });
123
+ if (!def)
124
+ throw new CustomFieldDefinitionError('not_found', `Custom field ${id} not found.`);
125
+ if (def.key !== expectedKey) {
126
+ throw new CustomFieldDefinitionError('key_changed', `Custom field ${id} is keyed "${def.key}", not "${expectedKey}"; nothing was renamed.`);
127
+ }
128
+ if (!customFieldDefinitionSchema.shape.key.safeParse(newKey).success) {
129
+ throw new CustomFieldDefinitionError('invalid_key', `"${newKey}" is not a valid field key.`);
130
+ }
131
+ if (newKey === def.key)
132
+ return def;
133
+ const holder = await em.findOne(CustomFieldDefinition, { entityType: def.entityType, key: newKey });
134
+ if (holder) {
135
+ throw new CustomFieldDefinitionError('duplicate_key', `A field "${newKey}" already exists on ${def.entityType}.`);
136
+ }
137
+ def.key = newKey;
138
+ await em.flush();
139
+ return def;
140
+ }
141
+ /** Delete a definition on the caller's EM. Options cascade; host values stay dormant (FR-010). */
142
+ export async function applyDeleteDefinition(em, id) {
143
+ // command-coverage-ignore: apply-seam primitive — mutates on the invoking Command's
144
+ // transactional EM; the caller (custom_fields definition command or a host command,
145
+ // e.g. catalog.attribute.*) records the single audit row co-transactionally.
146
+ // Contract: specs/061-attributes-on-custom-fields/contracts/custom-fields-product-host.md.
147
+ const def = await em.findOne(CustomFieldDefinition, { id });
148
+ if (!def)
149
+ throw new CustomFieldDefinitionError('not_found', `Custom field ${id} not found.`);
150
+ const options = await em.find(CustomFieldOption, { definitionId: id });
151
+ for (const o of options)
152
+ em.remove(o);
153
+ em.remove(def);
154
+ return def;
155
+ }
156
+ /** Add one option to a select/multiselect definition on the caller's EM. */
157
+ export async function applyCreateOption(em, definitionId, input) {
158
+ const def = await em.findOne(CustomFieldDefinition, { id: definitionId });
159
+ if (!def)
160
+ throw new CustomFieldDefinitionError('not_found', `Custom field ${definitionId} not found.`);
161
+ if (!SELECT_TYPES.has(def.valueType)) {
162
+ throw new CustomFieldDefinitionError('options_forbidden', `Field "${def.key}" does not take options.`);
163
+ }
164
+ return createOptionEntity(em, definitionId, input);
165
+ }
166
+ /** Patch one option's label/default/ordering on the caller's EM. */
167
+ export async function applyUpdateOption(em, definitionId, optionId, patch) {
168
+ const option = await em.findOne(CustomFieldOption, { id: optionId });
169
+ if (!option || option.definitionId !== definitionId) {
170
+ throw new CustomFieldDefinitionError('not_found', `Option ${optionId} not found.`);
171
+ }
172
+ if (patch.label !== undefined)
173
+ option.label = patch.label;
174
+ if (patch.labelDefault !== undefined)
175
+ option.labelDefault = patch.labelDefault;
176
+ if (patch.isDefault !== undefined)
177
+ option.isDefault = patch.isDefault;
178
+ if (patch.sortOrder !== undefined)
179
+ option.sortOrder = patch.sortOrder;
180
+ return option;
181
+ }
182
+ /** Delete one option on the caller's EM (in-use guard via the probe binding). */
183
+ export async function applyDeleteOption(em, definitionId, optionId, probes) {
184
+ // command-coverage-ignore: apply-seam primitive — mutates on the invoking Command's
185
+ // transactional EM; the caller (custom_fields definition command or a host command,
186
+ // e.g. catalog.attribute.*) records the single audit row co-transactionally.
187
+ // Contract: specs/061-attributes-on-custom-fields/contracts/custom-fields-product-host.md.
188
+ const def = await em.findOne(CustomFieldDefinition, { id: definitionId });
189
+ if (!def)
190
+ throw new CustomFieldDefinitionError('not_found', `Custom field ${definitionId} not found.`);
191
+ const option = await em.findOne(CustomFieldOption, { id: optionId });
192
+ if (!option || option.definitionId !== definitionId) {
193
+ throw new CustomFieldDefinitionError('not_found', `Option ${optionId} not found.`);
194
+ }
195
+ // FR-010: an in-use option cannot be removed while records reference it.
196
+ if (probes) {
197
+ const inUse = await probes.isOptionInUse(em, def.entityType, def.key, option.value);
198
+ if (inUse) {
199
+ throw new CustomFieldDefinitionError('option_in_use', `Option "${option.value}" is in use and cannot be removed.`);
200
+ }
201
+ }
202
+ em.remove(option);
203
+ return option;
204
+ }
205
+ function createOptionEntity(em, definitionId, input) {
206
+ // command-coverage-ignore: apply-seam primitive, exactly like the two exported
207
+ // functions that call it — the `em.create` queues the row on the invoking
208
+ // Command's transactional EM and is flushed by that Command, which records the
209
+ // single audit row co-transactionally. The marker belongs here rather than only
210
+ // on the callers because this is the function that writes (D-89).
211
+ // Contract: specs/061-attributes-on-custom-fields/contracts/custom-fields-product-host.md.
212
+ return em.create(CustomFieldOption, {
213
+ definitionId,
214
+ value: input.value,
215
+ label: input.label,
216
+ labelDefault: input.labelDefault,
217
+ isDefault: input.isDefault,
218
+ sortOrder: input.sortOrder,
219
+ });
220
+ }
221
+ //# sourceMappingURL=custom-field-definition-apply.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-field-definition-apply.js","sourceRoot":"","sources":["../../../src/backend/services/custom-field-definition-apply.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql';
2
+ import type { CreateCustomFieldDefinitionRequest, CustomFieldDefinitionRecord, CustomFieldOptionDto, CustomFieldOptionRecord, SupportedEntityType, UpdateCustomFieldDefinitionRequest } from '@endora-commerce/contracts';
3
+ import type { CommandBus } from '@endora-commerce/platform/commands';
4
+ import { CustomFieldDefinition } from '../entities/custom-field-definition.entity.js';
5
+ import { CustomFieldOption } from '../entities/custom-field-option.entity.js';
6
+ import type { CustomFieldDefinitionsCache, CachedDefinition } from './custom-field-definitions-cache.js';
7
+ import { CustomFieldDefinitionError } from './custom-field-definition-apply.js';
8
+ import type { CustomFieldValueService } from './custom-field-value.service.js';
9
+ import type { DefinitionSource } from './custom-field-value.service.js';
10
+ import type { CustomFieldDefinitionApplyApi } from '../../ports/index.js';
11
+ export { CustomFieldDefinitionError };
12
+ /**
13
+ * CustomFieldDefinitionService (feature 055) — CRUD over definitions and options.
14
+ *
15
+ * All mutations run through the Command Bus (Principle XIII); the bus writes the
16
+ * audit entry, so this service never calls the audit writer by hand. Reads are
17
+ * served from the per-`entityType` cache and every committed mutation publishes a
18
+ * cross-process invalidation. Implements {@link DefinitionSource} so the value
19
+ * service can validate host writes against the live definitions, and
20
+ * {@link CustomFieldDefinitionApplyApi} so host modules can mutate definitions
21
+ * co-transactionally with their own data (feature 061).
22
+ */
23
+ export declare class CustomFieldDefinitionService implements DefinitionSource, CustomFieldDefinitionApplyApi {
24
+ private readonly emFactory;
25
+ private readonly cache;
26
+ private readonly commandBus;
27
+ /** Set post-construction to break the definition⇄value service cycle (used by change guards). */
28
+ private valueService?;
29
+ constructor(emFactory: () => EntityManager, cache: CustomFieldDefinitionsCache, commandBus: CommandBus);
30
+ setValueService(v: CustomFieldValueService): void;
31
+ /** Live definitions (with options) for one entity type, cached per type. */
32
+ listForEntity(entityType: SupportedEntityType): Promise<CachedDefinition[]>;
33
+ /**
34
+ * The same list, past the cache, and every other process's cache with it
35
+ * (D-97.1).
36
+ *
37
+ * A consumer that composes its own rows against these definitions is the only
38
+ * party that can *detect* a stale entry — it knows what its rows say — and
39
+ * this module is the only one that may *resolve* it, because the cache is its
40
+ * own. The invalidation is the same fan-out a committed definition Command
41
+ * publishes, so the reload converges every process rather than only this one.
42
+ *
43
+ * This is what `catalog` used to do by calling `publishInvalidate` through an
44
+ * optional widening of the read port, which the `lazyPort` proxy made
45
+ * always-truthy and therefore always wrong when the real provider lacked it.
46
+ */
47
+ listForEntityFresh(entityType: SupportedEntityType): Promise<CachedDefinition[]>;
48
+ private loadForEntity;
49
+ /** All definitions across every (or one) entity type, options embedded. */
50
+ listAll(entityType?: SupportedEntityType): Promise<CachedDefinition[]>;
51
+ getById(id: string): Promise<CachedDefinition | null>;
52
+ create(input: CreateCustomFieldDefinitionRequest): Promise<CustomFieldDefinition>;
53
+ update(id: string, patch: UpdateCustomFieldDefinitionRequest): Promise<CustomFieldDefinition>;
54
+ delete(id: string): Promise<void>;
55
+ createOption(definitionId: string, input: CustomFieldOptionDto): Promise<CustomFieldOption>;
56
+ updateOption(definitionId: string, optionId: string, patch: Partial<Pick<CustomFieldOptionDto, 'label' | 'labelDefault' | 'isDefault' | 'sortOrder'>>): Promise<CustomFieldOption>;
57
+ deleteOption(definitionId: string, optionId: string): Promise<void>;
58
+ applyCreate(em: EntityManager, input: CreateCustomFieldDefinitionRequest): Promise<CustomFieldDefinitionRecord>;
59
+ applyUpdate(em: EntityManager, id: string, patch: UpdateCustomFieldDefinitionRequest): Promise<CustomFieldDefinitionRecord>;
60
+ applyRenameKey(em: EntityManager, id: string, expectedKey: string, newKey: string): Promise<CustomFieldDefinitionRecord>;
61
+ applyDelete(em: EntityManager, id: string): Promise<void>;
62
+ applyCreateOption(em: EntityManager, definitionId: string, input: CustomFieldOptionDto): Promise<CustomFieldOptionRecord>;
63
+ applyUpdateOption(em: EntityManager, definitionId: string, optionId: string, patch: Partial<Pick<CustomFieldOptionDto, 'label' | 'labelDefault' | 'isDefault' | 'sortOrder'>>): Promise<CustomFieldOptionRecord>;
64
+ applyDeleteOption(em: EntityManager, definitionId: string, optionId: string): Promise<void>;
65
+ /** Post-commit cache fan-out for apply-seam callers (also used internally after every command). */
66
+ publishInvalidate(entityType: SupportedEntityType): Promise<void>;
67
+ }
68
+ //# sourceMappingURL=custom-field-definition.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-field-definition.service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/custom-field-definition.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,205 @@
1
+ import { CustomFieldDefinition } from '../entities/custom-field-definition.entity.js';
2
+ import { CustomFieldOption } from '../entities/custom-field-option.entity.js';
3
+ import { isSupportedEntityType } from './custom-field-registry.js';
4
+ import { createDefinitionCommand, createOptionCommand, deleteDefinitionCommand, deleteOptionCommand, updateDefinitionCommand, updateOptionCommand, } from '../commands/definition-commands.js';
5
+ import { applyCreateDefinition, applyCreateOption, applyDeleteDefinition, applyDeleteOption, applyRenameDefinitionKey, applyUpdateDefinition, applyUpdateOption, assertOptionsRule, CustomFieldDefinitionError, } from './custom-field-definition-apply.js';
6
+ import { toCustomFieldDefinitionRecord, toCustomFieldOptionRecord, } from './custom-field-read-port.js';
7
+ // Feature 061 — the error class moved next to the shared apply functions;
8
+ // re-exported here so existing imports keep working.
9
+ export { CustomFieldDefinitionError };
10
+ const SELECT_TYPES = new Set(['select', 'multiselect']);
11
+ /**
12
+ * CustomFieldDefinitionService (feature 055) — CRUD over definitions and options.
13
+ *
14
+ * All mutations run through the Command Bus (Principle XIII); the bus writes the
15
+ * audit entry, so this service never calls the audit writer by hand. Reads are
16
+ * served from the per-`entityType` cache and every committed mutation publishes a
17
+ * cross-process invalidation. Implements {@link DefinitionSource} so the value
18
+ * service can validate host writes against the live definitions, and
19
+ * {@link CustomFieldDefinitionApplyApi} so host modules can mutate definitions
20
+ * co-transactionally with their own data (feature 061).
21
+ */
22
+ export class CustomFieldDefinitionService {
23
+ emFactory;
24
+ cache;
25
+ commandBus;
26
+ /** Set post-construction to break the definition⇄value service cycle (used by change guards). */
27
+ valueService;
28
+ constructor(emFactory, cache, commandBus) {
29
+ this.emFactory = emFactory;
30
+ this.cache = cache;
31
+ this.commandBus = commandBus;
32
+ }
33
+ setValueService(v) {
34
+ this.valueService = v;
35
+ }
36
+ // ---- Reads --------------------------------------------------------------
37
+ /** Live definitions (with options) for one entity type, cached per type. */
38
+ async listForEntity(entityType) {
39
+ if (!isSupportedEntityType(entityType))
40
+ return [];
41
+ return this.cache.getForEntity(entityType, () => this.loadForEntity(entityType));
42
+ }
43
+ /**
44
+ * The same list, past the cache, and every other process's cache with it
45
+ * (D-97.1).
46
+ *
47
+ * A consumer that composes its own rows against these definitions is the only
48
+ * party that can *detect* a stale entry — it knows what its rows say — and
49
+ * this module is the only one that may *resolve* it, because the cache is its
50
+ * own. The invalidation is the same fan-out a committed definition Command
51
+ * publishes, so the reload converges every process rather than only this one.
52
+ *
53
+ * This is what `catalog` used to do by calling `publishInvalidate` through an
54
+ * optional widening of the read port, which the `lazyPort` proxy made
55
+ * always-truthy and therefore always wrong when the real provider lacked it.
56
+ */
57
+ async listForEntityFresh(entityType) {
58
+ if (!isSupportedEntityType(entityType))
59
+ return [];
60
+ await this.cache.publishInvalidate(entityType);
61
+ return this.listForEntity(entityType);
62
+ }
63
+ async loadForEntity(entityType) {
64
+ const em = this.emFactory();
65
+ const definitions = await em.find(CustomFieldDefinition, { entityType }, { orderBy: { sortOrder: 'asc', key: 'asc' } });
66
+ if (definitions.length === 0)
67
+ return [];
68
+ const options = await em.find(CustomFieldOption, { definitionId: { $in: definitions.map((d) => d.id) } }, { orderBy: { sortOrder: 'asc' } });
69
+ const byDef = new Map();
70
+ for (const o of options) {
71
+ const list = byDef.get(o.definitionId) ?? [];
72
+ list.push(o);
73
+ byDef.set(o.definitionId, list);
74
+ }
75
+ return definitions.map((definition) => ({
76
+ definition,
77
+ options: byDef.get(definition.id) ?? [],
78
+ }));
79
+ }
80
+ /** All definitions across every (or one) entity type, options embedded. */
81
+ async listAll(entityType) {
82
+ if (entityType)
83
+ return this.listForEntity(entityType);
84
+ const em = this.emFactory();
85
+ const definitions = await em.find(CustomFieldDefinition, {}, { orderBy: { entityType: 'asc', sortOrder: 'asc', key: 'asc' } });
86
+ const options = await em.find(CustomFieldOption, {}, { orderBy: { sortOrder: 'asc' } });
87
+ const byDef = new Map();
88
+ for (const o of options) {
89
+ const list = byDef.get(o.definitionId) ?? [];
90
+ list.push(o);
91
+ byDef.set(o.definitionId, list);
92
+ }
93
+ return definitions.map((definition) => ({ definition, options: byDef.get(definition.id) ?? [] }));
94
+ }
95
+ async getById(id) {
96
+ const em = this.emFactory();
97
+ const definition = await em.findOne(CustomFieldDefinition, { id });
98
+ if (!definition)
99
+ return null;
100
+ const options = await em.find(CustomFieldOption, { definitionId: id }, { orderBy: { sortOrder: 'asc' } });
101
+ return { definition, options };
102
+ }
103
+ // ---- Mutations (via Command Bus) ----------------------------------------
104
+ async create(input) {
105
+ if (!isSupportedEntityType(input.entityType)) {
106
+ throw new CustomFieldDefinitionError('entity_type_unknown', `Unknown entity type "${input.entityType}".`);
107
+ }
108
+ assertOptionsRule(input.valueType, input.options);
109
+ const existing = await this.emFactory().findOne(CustomFieldDefinition, {
110
+ entityType: input.entityType,
111
+ key: input.key,
112
+ });
113
+ if (existing) {
114
+ throw new CustomFieldDefinitionError('duplicate_key', `A field "${input.key}" already exists on ${input.entityType}.`);
115
+ }
116
+ const def = await this.commandBus.run(createDefinitionCommand(input));
117
+ await this.cache.publishInvalidate(input.entityType);
118
+ return def;
119
+ }
120
+ async update(id, patch) {
121
+ const current = await this.emFactory().findOne(CustomFieldDefinition, { id });
122
+ if (!current)
123
+ throw new CustomFieldDefinitionError('not_found', `Custom field ${id} not found.`);
124
+ // FR-010: changing valueType while values exist is rejected.
125
+ if (patch.valueType !== undefined && patch.valueType !== current.valueType && this.valueService) {
126
+ const inUse = await this.valueService.hasStoredValues(this.emFactory(), current.entityType, current.key);
127
+ if (inUse) {
128
+ throw new CustomFieldDefinitionError('value_type_locked', `Cannot change the value type of "${current.key}" while records hold values. Clear them first.`);
129
+ }
130
+ }
131
+ const def = await this.commandBus.run(updateDefinitionCommand(id, patch, this.valueService));
132
+ await this.cache.publishInvalidate(current.entityType);
133
+ return def;
134
+ }
135
+ async delete(id) {
136
+ const current = await this.emFactory().findOne(CustomFieldDefinition, { id });
137
+ if (!current)
138
+ throw new CustomFieldDefinitionError('not_found', `Custom field ${id} not found.`);
139
+ await this.commandBus.run(deleteDefinitionCommand(id));
140
+ await this.cache.publishInvalidate(current.entityType);
141
+ }
142
+ async createOption(definitionId, input) {
143
+ const def = await this.emFactory().findOne(CustomFieldDefinition, { id: definitionId });
144
+ if (!def)
145
+ throw new CustomFieldDefinitionError('not_found', `Custom field ${definitionId} not found.`);
146
+ if (!SELECT_TYPES.has(def.valueType)) {
147
+ throw new CustomFieldDefinitionError('options_forbidden', `Field "${def.key}" does not take options.`);
148
+ }
149
+ const opt = await this.commandBus.run(createOptionCommand(definitionId, input));
150
+ await this.cache.publishInvalidate(def.entityType);
151
+ return opt;
152
+ }
153
+ async updateOption(definitionId, optionId, patch) {
154
+ const def = await this.emFactory().findOne(CustomFieldDefinition, { id: definitionId });
155
+ if (!def)
156
+ throw new CustomFieldDefinitionError('not_found', `Custom field ${definitionId} not found.`);
157
+ const opt = await this.commandBus.run(updateOptionCommand(definitionId, optionId, patch));
158
+ await this.cache.publishInvalidate(def.entityType);
159
+ return opt;
160
+ }
161
+ async deleteOption(definitionId, optionId) {
162
+ const def = await this.emFactory().findOne(CustomFieldDefinition, { id: definitionId });
163
+ if (!def)
164
+ throw new CustomFieldDefinitionError('not_found', `Custom field ${definitionId} not found.`);
165
+ const option = await this.emFactory().findOne(CustomFieldOption, { id: optionId });
166
+ if (!option)
167
+ throw new CustomFieldDefinitionError('not_found', `Option ${optionId} not found.`);
168
+ // FR-010: an in-use option cannot be removed while records reference it.
169
+ if (this.valueService) {
170
+ const inUse = await this.valueService.isOptionInUse(this.emFactory(), def.entityType, def.key, option.value);
171
+ if (inUse) {
172
+ throw new CustomFieldDefinitionError('option_in_use', `Option "${option.value}" is in use and cannot be removed.`);
173
+ }
174
+ }
175
+ await this.commandBus.run(deleteOptionCommand(definitionId, optionId, this.valueService));
176
+ await this.cache.publishInvalidate(def.entityType);
177
+ }
178
+ // ---- Transactional apply seam (feature 061, CustomFieldDefinitionApplyApi)
179
+ async applyCreate(em, input) {
180
+ return toCustomFieldDefinitionRecord(await applyCreateDefinition(em, input));
181
+ }
182
+ async applyUpdate(em, id, patch) {
183
+ return toCustomFieldDefinitionRecord(await applyUpdateDefinition(em, id, patch, this.valueService));
184
+ }
185
+ async applyRenameKey(em, id, expectedKey, newKey) {
186
+ return toCustomFieldDefinitionRecord(await applyRenameDefinitionKey(em, id, expectedKey, newKey));
187
+ }
188
+ async applyDelete(em, id) {
189
+ await applyDeleteDefinition(em, id);
190
+ }
191
+ async applyCreateOption(em, definitionId, input) {
192
+ return toCustomFieldOptionRecord(await applyCreateOption(em, definitionId, input));
193
+ }
194
+ async applyUpdateOption(em, definitionId, optionId, patch) {
195
+ return toCustomFieldOptionRecord(await applyUpdateOption(em, definitionId, optionId, patch));
196
+ }
197
+ async applyDeleteOption(em, definitionId, optionId) {
198
+ await applyDeleteOption(em, definitionId, optionId, this.valueService);
199
+ }
200
+ /** Post-commit cache fan-out for apply-seam callers (also used internally after every command). */
201
+ async publishInvalidate(entityType) {
202
+ await this.cache.publishInvalidate(entityType);
203
+ }
204
+ }
205
+ //# sourceMappingURL=custom-field-definition.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-field-definition.service.js","sourceRoot":"","sources":["../../../src/backend/services/custom-field-definition.service.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,qBAAqB,EAAE,MAAM,+CAA+C,CAAC;AACtF,OAAO,EAAE,iBAAiB,EAAE,MAAM,2CAA2C,CAAC;AAC9E,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAKnE,OAAO,EACL,uBAAuB,EACvB,mBAAmB,EACnB,uBAAuB,EACvB,mBAAmB,EACnB,uBAAuB,EACvB,mBAAmB,GACpB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EACjB,qBAAqB,EACrB,iBAAiB,EACjB,wBAAwB,EACxB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EACjB,0BAA0B,GAC3B,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EACL,6BAA6B,EAC7B,yBAAyB,GAC1B,MAAM,6BAA6B,CAAC;AAWrC,0EAA0E;AAC1E,qDAAqD;AACrD,OAAO,EAAE,0BAA0B,EAAE,CAAC;AAEtC,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC;AAExD;;;;;;;;;;GAUG;AACH,MAAM,OAAO,4BAA4B;IAKpB;IACA;IACA;IANnB,iGAAiG;IACzF,YAAY,CAA2B;IAE/C,YACmB,SAA8B,EAC9B,KAAkC,EAClC,UAAsB;QAFtB,cAAS,GAAT,SAAS,CAAqB;QAC9B,UAAK,GAAL,KAAK,CAA6B;QAClC,eAAU,GAAV,UAAU,CAAY;IACtC,CAAC;IAEJ,eAAe,CAAC,CAA0B;QACxC,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC;IACxB,CAAC;IAED,4EAA4E;IAE5E,4EAA4E;IAC5E,KAAK,CAAC,aAAa,CAAC,UAA+B;QACjD,IAAI,CAAC,qBAAqB,CAAC,UAAU,CAAC;YAAE,OAAO,EAAE,CAAC;QAClD,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,UAAU,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC;IACnF,CAAC;IAED;;;;;;;;;;;;;OAaG;IACH,KAAK,CAAC,kBAAkB,CAAC,UAA+B;QACtD,IAAI,CAAC,qBAAqB,CAAC,UAAU,CAAC;YAAE,OAAO,EAAE,CAAC;QAClD,MAAM,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;QAC/C,OAAO,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;IACxC,CAAC;IAEO,KAAK,CAAC,aAAa,CAAC,UAA+B;QACzD,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC5B,MAAM,WAAW,GAAG,MAAM,EAAE,CAAC,IAAI,CAC/B,qBAAqB,EACrB,EAAE,UAAU,EAAE,EACd,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,CAC9C,CAAC;QACF,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QACxC,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,IAAI,CAC3B,iBAAiB,EACjB,EAAE,YAAY,EAAE,EAAE,GAAG,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EACvD,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,CAClC,CAAC;QACF,MAAM,KAAK,GAAG,IAAI,GAAG,EAA+B,CAAC;QACrD,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACxB,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC;YAC7C,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACb,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;QAClC,CAAC;QACD,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;YACtC,UAAU;YACV,OAAO,EAAE,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE;SACxC,CAAC,CAAC,CAAC;IACN,CAAC;IAED,2EAA2E;IAC3E,KAAK,CAAC,OAAO,CAAC,UAAgC;QAC5C,IAAI,UAAU;YAAE,OAAO,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;QACtD,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC5B,MAAM,WAAW,GAAG,MAAM,EAAE,CAAC,IAAI,CAC/B,qBAAqB,EACrB,EAAE,EACF,EAAE,OAAO,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,CACjE,CAAC;QACF,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QACxF,MAAM,KAAK,GAAG,IAAI,GAAG,EAA+B,CAAC;QACrD,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACxB,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC;YAC7C,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACb,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;QAClC,CAAC;QACD,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,OAAO,EAAE,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;IACpG,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,EAAU;QACtB,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC5B,MAAM,UAAU,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,qBAAqB,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;QACnE,IAAI,CAAC,UAAU;YAAE,OAAO,IAAI,CAAC;QAC7B,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,IAAI,CAC3B,iBAAiB,EACjB,EAAE,YAAY,EAAE,EAAE,EAAE,EACpB,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,CAClC,CAAC;QACF,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC;IACjC,CAAC;IAED,4EAA4E;IAE5E,KAAK,CAAC,MAAM,CAAC,KAAyC;QACpD,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;YAC7C,MAAM,IAAI,0BAA0B,CAAC,qBAAqB,EAAE,wBAAwB,KAAK,CAAC,UAAU,IAAI,CAAC,CAAC;QAC5G,CAAC;QACD,iBAAiB,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;QAClD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,qBAAqB,EAAE;YACrE,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,GAAG,EAAE,KAAK,CAAC,GAAG;SACf,CAAC,CAAC;QACH,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,IAAI,0BAA0B,CAAC,eAAe,EAAE,YAAY,KAAK,CAAC,GAAG,uBAAuB,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC;QACzH,CAAC;QACD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,uBAAuB,CAAC,KAAK,CAAC,CAAC,CAAC;QACtE,MAAM,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QACrD,OAAO,GAAG,CAAC;IACb,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAU,EAAE,KAAyC;QAChE,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,qBAAqB,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;QAC9E,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,0BAA0B,CAAC,WAAW,EAAE,gBAAgB,EAAE,aAAa,CAAC,CAAC;QAEjG,6DAA6D;QAC7D,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS,IAAI,KAAK,CAAC,SAAS,KAAK,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YAChG,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,eAAe,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;YACzG,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,IAAI,0BAA0B,CAClC,mBAAmB,EACnB,oCAAoC,OAAO,CAAC,GAAG,gDAAgD,CAChG,CAAC;YACJ,CAAC;QACH,CAAC;QACD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,uBAAuB,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC;QAC7F,MAAM,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QACvD,OAAO,GAAG,CAAC;IACb,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAU;QACrB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,qBAAqB,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;QAC9E,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,0BAA0B,CAAC,WAAW,EAAE,gBAAgB,EAAE,aAAa,CAAC,CAAC;QACjG,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,uBAAuB,CAAC,EAAE,CAAC,CAAC,CAAC;QACvD,MAAM,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IACzD,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,YAAoB,EAAE,KAA2B;QAClE,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,qBAAqB,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;QACxF,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,0BAA0B,CAAC,WAAW,EAAE,gBAAgB,YAAY,aAAa,CAAC,CAAC;QACvG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YACrC,MAAM,IAAI,0BAA0B,CAAC,mBAAmB,EAAE,UAAU,GAAG,CAAC,GAAG,0BAA0B,CAAC,CAAC;QACzG,CAAC;QACD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,mBAAmB,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC,CAAC;QAChF,MAAM,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACnD,OAAO,GAAG,CAAC;IACb,CAAC;IAED,KAAK,CAAC,YAAY,CAChB,YAAoB,EACpB,QAAgB,EAChB,KAAgG;QAEhG,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,qBAAqB,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;QACxF,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,0BAA0B,CAAC,WAAW,EAAE,gBAAgB,YAAY,aAAa,CAAC,CAAC;QACvG,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,mBAAmB,CAAC,YAAY,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC;QAC1F,MAAM,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACnD,OAAO,GAAG,CAAC;IACb,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,YAAoB,EAAE,QAAgB;QACvD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,qBAAqB,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;QACxF,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,0BAA0B,CAAC,WAAW,EAAE,gBAAgB,YAAY,aAAa,CAAC,CAAC;QACvG,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,iBAAiB,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;QACnF,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,0BAA0B,CAAC,WAAW,EAAE,UAAU,QAAQ,aAAa,CAAC,CAAC;QAChG,yEAAyE;QACzE,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,aAAa,CACjD,IAAI,CAAC,SAAS,EAAE,EAChB,GAAG,CAAC,UAAU,EACd,GAAG,CAAC,GAAG,EACP,MAAM,CAAC,KAAK,CACb,CAAC;YACF,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,IAAI,0BAA0B,CAClC,eAAe,EACf,WAAW,MAAM,CAAC,KAAK,oCAAoC,CAC5D,CAAC;YACJ,CAAC;QACH,CAAC;QACD,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,mBAAmB,CAAC,YAAY,EAAE,QAAQ,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC;QAC1F,MAAM,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACrD,CAAC;IAED,6EAA6E;IAE7E,KAAK,CAAC,WAAW,CACf,EAAiB,EACjB,KAAyC;QAEzC,OAAO,6BAA6B,CAAC,MAAM,qBAAqB,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC;IAC/E,CAAC;IAED,KAAK,CAAC,WAAW,CACf,EAAiB,EACjB,EAAU,EACV,KAAyC;QAEzC,OAAO,6BAA6B,CAClC,MAAM,qBAAqB,CAAC,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,YAAY,CAAC,CAC9D,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,cAAc,CAClB,EAAiB,EACjB,EAAU,EACV,WAAmB,EACnB,MAAc;QAEd,OAAO,6BAA6B,CAClC,MAAM,wBAAwB,CAAC,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE,MAAM,CAAC,CAC5D,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,EAAiB,EAAE,EAAU;QAC7C,MAAM,qBAAqB,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IACtC,CAAC;IAED,KAAK,CAAC,iBAAiB,CACrB,EAAiB,EACjB,YAAoB,EACpB,KAA2B;QAE3B,OAAO,yBAAyB,CAAC,MAAM,iBAAiB,CAAC,EAAE,EAAE,YAAY,EAAE,KAAK,CAAC,CAAC,CAAC;IACrF,CAAC;IAED,KAAK,CAAC,iBAAiB,CACrB,EAAiB,EACjB,YAAoB,EACpB,QAAgB,EAChB,KAAgG;QAEhG,OAAO,yBAAyB,CAAC,MAAM,iBAAiB,CAAC,EAAE,EAAE,YAAY,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC;IAC/F,CAAC;IAED,KAAK,CAAC,iBAAiB,CAAC,EAAiB,EAAE,YAAoB,EAAE,QAAgB;QAC/E,MAAM,iBAAiB,CAAC,EAAE,EAAE,YAAY,EAAE,QAAQ,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;IACzE,CAAC;IAED,mGAAmG;IACnG,KAAK,CAAC,iBAAiB,CAAC,UAA+B;QACrD,MAAM,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;IACjD,CAAC;CACF"}