@proseql/core 0.1.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.
- package/LICENSE +21 -0
- package/dist/errors/crud-errors.d.ts +98 -0
- package/dist/errors/crud-errors.d.ts.map +1 -0
- package/dist/errors/crud-errors.js +23 -0
- package/dist/errors/crud-errors.js.map +1 -0
- package/dist/errors/index.d.ts +16 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +12 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/errors/migration-errors.d.ts +22 -0
- package/dist/errors/migration-errors.d.ts.map +1 -0
- package/dist/errors/migration-errors.js +14 -0
- package/dist/errors/migration-errors.js.map +1 -0
- package/dist/errors/plugin-errors.d.ts +15 -0
- package/dist/errors/plugin-errors.d.ts.map +1 -0
- package/dist/errors/plugin-errors.js +11 -0
- package/dist/errors/plugin-errors.js.map +1 -0
- package/dist/errors/query-errors.d.ts +31 -0
- package/dist/errors/query-errors.d.ts.map +1 -0
- package/dist/errors/query-errors.js +11 -0
- package/dist/errors/query-errors.js.map +1 -0
- package/dist/errors/storage-errors.d.ts +30 -0
- package/dist/errors/storage-errors.d.ts.map +1 -0
- package/dist/errors/storage-errors.js +11 -0
- package/dist/errors/storage-errors.js.map +1 -0
- package/dist/factories/crud-factory-with-relationships.d.ts +28 -0
- package/dist/factories/crud-factory-with-relationships.d.ts.map +1 -0
- package/dist/factories/crud-factory-with-relationships.js +8 -0
- package/dist/factories/crud-factory-with-relationships.js.map +1 -0
- package/dist/factories/crud-factory.d.ts +25 -0
- package/dist/factories/crud-factory.d.ts.map +1 -0
- package/dist/factories/crud-factory.js +8 -0
- package/dist/factories/crud-factory.js.map +1 -0
- package/dist/factories/database-effect.d.ts +241 -0
- package/dist/factories/database-effect.d.ts.map +1 -0
- package/dist/factories/database-effect.js +859 -0
- package/dist/factories/database-effect.js.map +1 -0
- package/dist/hooks/hook-runner.d.ts +60 -0
- package/dist/hooks/hook-runner.d.ts.map +1 -0
- package/dist/hooks/hook-runner.js +107 -0
- package/dist/hooks/hook-runner.js.map +1 -0
- package/dist/index.d.ts +84 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +110 -0
- package/dist/index.js.map +1 -0
- package/dist/indexes/index-lookup.d.ts +33 -0
- package/dist/indexes/index-lookup.d.ts.map +1 -0
- package/dist/indexes/index-lookup.js +180 -0
- package/dist/indexes/index-lookup.js.map +1 -0
- package/dist/indexes/index-manager.d.ts +118 -0
- package/dist/indexes/index-manager.d.ts.map +1 -0
- package/dist/indexes/index-manager.js +345 -0
- package/dist/indexes/index-manager.js.map +1 -0
- package/dist/indexes/search-index.d.ts +179 -0
- package/dist/indexes/search-index.d.ts.map +1 -0
- package/dist/indexes/search-index.js +405 -0
- package/dist/indexes/search-index.js.map +1 -0
- package/dist/migrations/migration-runner.d.ts +70 -0
- package/dist/migrations/migration-runner.d.ts.map +1 -0
- package/dist/migrations/migration-runner.js +271 -0
- package/dist/migrations/migration-runner.js.map +1 -0
- package/dist/migrations/migration-types.d.ts +63 -0
- package/dist/migrations/migration-types.d.ts.map +1 -0
- package/dist/migrations/migration-types.js +5 -0
- package/dist/migrations/migration-types.js.map +1 -0
- package/dist/operations/crud/create-with-relationships.d.ts +44 -0
- package/dist/operations/crud/create-with-relationships.d.ts.map +1 -0
- package/dist/operations/crud/create-with-relationships.js +483 -0
- package/dist/operations/crud/create-with-relationships.js.map +1 -0
- package/dist/operations/crud/create.d.ts +48 -0
- package/dist/operations/crud/create.d.ts.map +1 -0
- package/dist/operations/crud/create.js +333 -0
- package/dist/operations/crud/create.js.map +1 -0
- package/dist/operations/crud/delete-with-relationships.d.ts +63 -0
- package/dist/operations/crud/delete-with-relationships.d.ts.map +1 -0
- package/dist/operations/crud/delete-with-relationships.js +395 -0
- package/dist/operations/crud/delete-with-relationships.js.map +1 -0
- package/dist/operations/crud/delete.d.ts +58 -0
- package/dist/operations/crud/delete.d.ts.map +1 -0
- package/dist/operations/crud/delete.js +267 -0
- package/dist/operations/crud/delete.js.map +1 -0
- package/dist/operations/crud/unique-check.d.ts +114 -0
- package/dist/operations/crud/unique-check.d.ts.map +1 -0
- package/dist/operations/crud/unique-check.js +383 -0
- package/dist/operations/crud/unique-check.js.map +1 -0
- package/dist/operations/crud/update-with-relationships.d.ts +45 -0
- package/dist/operations/crud/update-with-relationships.d.ts.map +1 -0
- package/dist/operations/crud/update-with-relationships.js +516 -0
- package/dist/operations/crud/update-with-relationships.js.map +1 -0
- package/dist/operations/crud/update.d.ts +91 -0
- package/dist/operations/crud/update.d.ts.map +1 -0
- package/dist/operations/crud/update.js +505 -0
- package/dist/operations/crud/update.js.map +1 -0
- package/dist/operations/crud/upsert.d.ts +52 -0
- package/dist/operations/crud/upsert.d.ts.map +1 -0
- package/dist/operations/crud/upsert.js +386 -0
- package/dist/operations/crud/upsert.js.map +1 -0
- package/dist/operations/query/aggregate.d.ts +30 -0
- package/dist/operations/query/aggregate.d.ts.map +1 -0
- package/dist/operations/query/aggregate.js +227 -0
- package/dist/operations/query/aggregate.js.map +1 -0
- package/dist/operations/query/cursor-stream.d.ts +18 -0
- package/dist/operations/query/cursor-stream.d.ts.map +1 -0
- package/dist/operations/query/cursor-stream.js +199 -0
- package/dist/operations/query/cursor-stream.js.map +1 -0
- package/dist/operations/query/filter-stream.d.ts +12 -0
- package/dist/operations/query/filter-stream.d.ts.map +1 -0
- package/dist/operations/query/filter-stream.js +167 -0
- package/dist/operations/query/filter-stream.js.map +1 -0
- package/dist/operations/query/filter.d.ts +13 -0
- package/dist/operations/query/filter.d.ts.map +1 -0
- package/dist/operations/query/filter.js +267 -0
- package/dist/operations/query/filter.js.map +1 -0
- package/dist/operations/query/paginate-stream.d.ts +11 -0
- package/dist/operations/query/paginate-stream.d.ts.map +1 -0
- package/dist/operations/query/paginate-stream.js +22 -0
- package/dist/operations/query/paginate-stream.js.map +1 -0
- package/dist/operations/query/query-helpers.d.ts +14 -0
- package/dist/operations/query/query-helpers.d.ts.map +1 -0
- package/dist/operations/query/query-helpers.js +22 -0
- package/dist/operations/query/query-helpers.js.map +1 -0
- package/dist/operations/query/resolve-computed.d.ts +142 -0
- package/dist/operations/query/resolve-computed.d.ts.map +1 -0
- package/dist/operations/query/resolve-computed.js +197 -0
- package/dist/operations/query/resolve-computed.js.map +1 -0
- package/dist/operations/query/search.d.ts +110 -0
- package/dist/operations/query/search.d.ts.map +1 -0
- package/dist/operations/query/search.js +188 -0
- package/dist/operations/query/search.js.map +1 -0
- package/dist/operations/query/select-stream.d.ts +27 -0
- package/dist/operations/query/select-stream.d.ts.map +1 -0
- package/dist/operations/query/select-stream.js +88 -0
- package/dist/operations/query/select-stream.js.map +1 -0
- package/dist/operations/query/select.d.ts +54 -0
- package/dist/operations/query/select.d.ts.map +1 -0
- package/dist/operations/query/select.js +159 -0
- package/dist/operations/query/select.js.map +1 -0
- package/dist/operations/query/sort-stream.d.ts +46 -0
- package/dist/operations/query/sort-stream.d.ts.map +1 -0
- package/dist/operations/query/sort-stream.js +158 -0
- package/dist/operations/query/sort-stream.js.map +1 -0
- package/dist/operations/query/sort.d.ts +9 -0
- package/dist/operations/query/sort.d.ts.map +1 -0
- package/dist/operations/query/sort.js +58 -0
- package/dist/operations/query/sort.js.map +1 -0
- package/dist/operations/relationships/populate-stream.d.ts +29 -0
- package/dist/operations/relationships/populate-stream.d.ts.map +1 -0
- package/dist/operations/relationships/populate-stream.js +159 -0
- package/dist/operations/relationships/populate-stream.js.map +1 -0
- package/dist/operations/relationships/populate.d.ts +15 -0
- package/dist/operations/relationships/populate.d.ts.map +1 -0
- package/dist/operations/relationships/populate.js +228 -0
- package/dist/operations/relationships/populate.js.map +1 -0
- package/dist/plugins/plugin-hooks.d.ts +25 -0
- package/dist/plugins/plugin-hooks.d.ts.map +1 -0
- package/dist/plugins/plugin-hooks.js +64 -0
- package/dist/plugins/plugin-hooks.js.map +1 -0
- package/dist/plugins/plugin-registry.d.ts +26 -0
- package/dist/plugins/plugin-registry.d.ts.map +1 -0
- package/dist/plugins/plugin-registry.js +150 -0
- package/dist/plugins/plugin-registry.js.map +1 -0
- package/dist/plugins/plugin-types.d.ts +95 -0
- package/dist/plugins/plugin-types.d.ts.map +1 -0
- package/dist/plugins/plugin-types.js +6 -0
- package/dist/plugins/plugin-types.js.map +1 -0
- package/dist/plugins/plugin-validation.d.ts +49 -0
- package/dist/plugins/plugin-validation.d.ts.map +1 -0
- package/dist/plugins/plugin-validation.js +295 -0
- package/dist/plugins/plugin-validation.js.map +1 -0
- package/dist/reactive/change-event.d.ts +44 -0
- package/dist/reactive/change-event.d.ts.map +1 -0
- package/dist/reactive/change-event.js +49 -0
- package/dist/reactive/change-event.js.map +1 -0
- package/dist/reactive/change-pubsub.d.ts +32 -0
- package/dist/reactive/change-pubsub.d.ts.map +1 -0
- package/dist/reactive/change-pubsub.js +31 -0
- package/dist/reactive/change-pubsub.js.map +1 -0
- package/dist/reactive/evaluate-query.d.ts +62 -0
- package/dist/reactive/evaluate-query.d.ts.map +1 -0
- package/dist/reactive/evaluate-query.js +57 -0
- package/dist/reactive/evaluate-query.js.map +1 -0
- package/dist/reactive/watch-by-id.d.ts +53 -0
- package/dist/reactive/watch-by-id.d.ts.map +1 -0
- package/dist/reactive/watch-by-id.js +55 -0
- package/dist/reactive/watch-by-id.js.map +1 -0
- package/dist/reactive/watch.d.ts +78 -0
- package/dist/reactive/watch.d.ts.map +1 -0
- package/dist/reactive/watch.js +133 -0
- package/dist/reactive/watch.js.map +1 -0
- package/dist/serializers/codecs/hjson.d.ts +33 -0
- package/dist/serializers/codecs/hjson.d.ts.map +1 -0
- package/dist/serializers/codecs/hjson.js +40 -0
- package/dist/serializers/codecs/hjson.js.map +1 -0
- package/dist/serializers/codecs/json.d.ts +22 -0
- package/dist/serializers/codecs/json.d.ts.map +1 -0
- package/dist/serializers/codecs/json.js +28 -0
- package/dist/serializers/codecs/json.js.map +1 -0
- package/dist/serializers/codecs/json5.d.ts +26 -0
- package/dist/serializers/codecs/json5.d.ts.map +1 -0
- package/dist/serializers/codecs/json5.js +33 -0
- package/dist/serializers/codecs/json5.js.map +1 -0
- package/dist/serializers/codecs/jsonc.d.ts +29 -0
- package/dist/serializers/codecs/jsonc.d.ts.map +1 -0
- package/dist/serializers/codecs/jsonc.js +38 -0
- package/dist/serializers/codecs/jsonc.js.map +1 -0
- package/dist/serializers/codecs/jsonl.d.ts +17 -0
- package/dist/serializers/codecs/jsonl.d.ts.map +1 -0
- package/dist/serializers/codecs/jsonl.js +31 -0
- package/dist/serializers/codecs/jsonl.js.map +1 -0
- package/dist/serializers/codecs/prose.d.ts +419 -0
- package/dist/serializers/codecs/prose.d.ts.map +1 -0
- package/dist/serializers/codecs/prose.js +1060 -0
- package/dist/serializers/codecs/prose.js.map +1 -0
- package/dist/serializers/codecs/toml.d.ts +23 -0
- package/dist/serializers/codecs/toml.d.ts.map +1 -0
- package/dist/serializers/codecs/toml.js +66 -0
- package/dist/serializers/codecs/toml.js.map +1 -0
- package/dist/serializers/codecs/toon.d.ts +20 -0
- package/dist/serializers/codecs/toon.d.ts.map +1 -0
- package/dist/serializers/codecs/toon.js +33 -0
- package/dist/serializers/codecs/toon.js.map +1 -0
- package/dist/serializers/codecs/yaml.d.ts +24 -0
- package/dist/serializers/codecs/yaml.d.ts.map +1 -0
- package/dist/serializers/codecs/yaml.js +31 -0
- package/dist/serializers/codecs/yaml.js.map +1 -0
- package/dist/serializers/format-codec.d.ts +53 -0
- package/dist/serializers/format-codec.d.ts.map +1 -0
- package/dist/serializers/format-codec.js +148 -0
- package/dist/serializers/format-codec.js.map +1 -0
- package/dist/serializers/presets.d.ts +48 -0
- package/dist/serializers/presets.d.ts.map +1 -0
- package/dist/serializers/presets.js +72 -0
- package/dist/serializers/presets.js.map +1 -0
- package/dist/serializers/serializer-service.d.ts +11 -0
- package/dist/serializers/serializer-service.d.ts.map +1 -0
- package/dist/serializers/serializer-service.js +4 -0
- package/dist/serializers/serializer-service.js.map +1 -0
- package/dist/state/collection-state.d.ts +19 -0
- package/dist/state/collection-state.d.ts.map +1 -0
- package/dist/state/collection-state.js +15 -0
- package/dist/state/collection-state.js.map +1 -0
- package/dist/state/state-operations.d.ts +38 -0
- package/dist/state/state-operations.d.ts.map +1 -0
- package/dist/state/state-operations.js +65 -0
- package/dist/state/state-operations.js.map +1 -0
- package/dist/storage/in-memory-adapter-layer.d.ts +16 -0
- package/dist/storage/in-memory-adapter-layer.d.ts.map +1 -0
- package/dist/storage/in-memory-adapter-layer.js +81 -0
- package/dist/storage/in-memory-adapter-layer.js.map +1 -0
- package/dist/storage/persistence-effect.d.ts +244 -0
- package/dist/storage/persistence-effect.d.ts.map +1 -0
- package/dist/storage/persistence-effect.js +551 -0
- package/dist/storage/persistence-effect.js.map +1 -0
- package/dist/storage/storage-service.d.ts +22 -0
- package/dist/storage/storage-service.d.ts.map +1 -0
- package/dist/storage/storage-service.js +4 -0
- package/dist/storage/storage-service.js.map +1 -0
- package/dist/storage/transforms.d.ts +183 -0
- package/dist/storage/transforms.d.ts.map +1 -0
- package/dist/storage/transforms.js +263 -0
- package/dist/storage/transforms.js.map +1 -0
- package/dist/transactions/transaction.d.ts +87 -0
- package/dist/transactions/transaction.d.ts.map +1 -0
- package/dist/transactions/transaction.js +240 -0
- package/dist/transactions/transaction.js.map +1 -0
- package/dist/types/aggregate-types.d.ts +73 -0
- package/dist/types/aggregate-types.d.ts.map +1 -0
- package/dist/types/aggregate-types.js +14 -0
- package/dist/types/aggregate-types.js.map +1 -0
- package/dist/types/computed-types.d.ts +71 -0
- package/dist/types/computed-types.d.ts.map +1 -0
- package/dist/types/computed-types.js +8 -0
- package/dist/types/computed-types.js.map +1 -0
- package/dist/types/crud-relationship-types.d.ts +180 -0
- package/dist/types/crud-relationship-types.d.ts.map +1 -0
- package/dist/types/crud-relationship-types.js +17 -0
- package/dist/types/crud-relationship-types.js.map +1 -0
- package/dist/types/crud-types.d.ts +343 -0
- package/dist/types/crud-types.d.ts.map +1 -0
- package/dist/types/crud-types.js +43 -0
- package/dist/types/crud-types.js.map +1 -0
- package/dist/types/cursor-types.d.ts +52 -0
- package/dist/types/cursor-types.d.ts.map +1 -0
- package/dist/types/cursor-types.js +2 -0
- package/dist/types/cursor-types.js.map +1 -0
- package/dist/types/database-config-types.d.ts +196 -0
- package/dist/types/database-config-types.d.ts.map +1 -0
- package/dist/types/database-config-types.js +11 -0
- package/dist/types/database-config-types.js.map +1 -0
- package/dist/types/hook-types.d.ts +158 -0
- package/dist/types/hook-types.d.ts.map +1 -0
- package/dist/types/hook-types.js +6 -0
- package/dist/types/hook-types.js.map +1 -0
- package/dist/types/index-types.d.ts +42 -0
- package/dist/types/index-types.d.ts.map +1 -0
- package/dist/types/index-types.js +8 -0
- package/dist/types/index-types.js.map +1 -0
- package/dist/types/operators.d.ts +5 -0
- package/dist/types/operators.d.ts.map +1 -0
- package/dist/types/operators.js +297 -0
- package/dist/types/operators.js.map +1 -0
- package/dist/types/query-overloads.d.ts +54 -0
- package/dist/types/query-overloads.d.ts.map +1 -0
- package/dist/types/query-overloads.js +3 -0
- package/dist/types/query-overloads.js.map +1 -0
- package/dist/types/reactive-types.d.ts +75 -0
- package/dist/types/reactive-types.d.ts.map +1 -0
- package/dist/types/reactive-types.js +7 -0
- package/dist/types/reactive-types.js.map +1 -0
- package/dist/types/schema-types.d.ts +56 -0
- package/dist/types/schema-types.d.ts.map +1 -0
- package/dist/types/schema-types.js +8 -0
- package/dist/types/schema-types.js.map +1 -0
- package/dist/types/search-types.d.ts +82 -0
- package/dist/types/search-types.d.ts.map +1 -0
- package/dist/types/search-types.js +110 -0
- package/dist/types/search-types.js.map +1 -0
- package/dist/types/types.d.ts +286 -0
- package/dist/types/types.d.ts.map +1 -0
- package/dist/types/types.js +2 -0
- package/dist/types/types.js.map +1 -0
- package/dist/utils/id-generator.d.ts +97 -0
- package/dist/utils/id-generator.d.ts.map +1 -0
- package/dist/utils/id-generator.js +247 -0
- package/dist/utils/id-generator.js.map +1 -0
- package/dist/utils/nested-path.d.ts +56 -0
- package/dist/utils/nested-path.d.ts.map +1 -0
- package/dist/utils/nested-path.js +119 -0
- package/dist/utils/nested-path.js.map +1 -0
- package/dist/utils/path.d.ts +16 -0
- package/dist/utils/path.d.ts.map +1 -0
- package/dist/utils/path.js +24 -0
- package/dist/utils/path.js.map +1 -0
- package/dist/validators/foreign-key.d.ts +49 -0
- package/dist/validators/foreign-key.d.ts.map +1 -0
- package/dist/validators/foreign-key.js +153 -0
- package/dist/validators/foreign-key.js.map +1 -0
- package/dist/validators/schema-validator.d.ts +19 -0
- package/dist/validators/schema-validator.d.ts.map +1 -0
- package/dist/validators/schema-validator.js +34 -0
- package/dist/validators/schema-validator.js.map +1 -0
- package/package.json +57 -0
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unique constraint checking utilities for CRUD operations.
|
|
3
|
+
*
|
|
4
|
+
* Handles both single-field constraints (e.g., "email") and compound constraints
|
|
5
|
+
* (e.g., ["userId", "settingKey"]). All constraints are normalized to arrays internally.
|
|
6
|
+
*/
|
|
7
|
+
// ============================================================================
|
|
8
|
+
// Constraint Normalization
|
|
9
|
+
// ============================================================================
|
|
10
|
+
/**
|
|
11
|
+
* Normalize unique fields configuration to a consistent array-of-arrays format.
|
|
12
|
+
*
|
|
13
|
+
* Converts:
|
|
14
|
+
* ["email", ["userId", "settingKey"]]
|
|
15
|
+
* To:
|
|
16
|
+
* [["email"], ["userId", "settingKey"]]
|
|
17
|
+
*
|
|
18
|
+
* This allows a single code path to handle both single and compound constraints.
|
|
19
|
+
*
|
|
20
|
+
* @param uniqueFields - Configuration from CollectionConfig.uniqueFields
|
|
21
|
+
* @returns Normalized constraints where each constraint is an array of field names
|
|
22
|
+
*/
|
|
23
|
+
export const normalizeConstraints = (uniqueFields) => {
|
|
24
|
+
if (!uniqueFields || uniqueFields.length === 0) {
|
|
25
|
+
return [];
|
|
26
|
+
}
|
|
27
|
+
return uniqueFields.map((constraint) => typeof constraint === "string" ? [constraint] : constraint);
|
|
28
|
+
};
|
|
29
|
+
// ============================================================================
|
|
30
|
+
// Unique Constraint Checking
|
|
31
|
+
// ============================================================================
|
|
32
|
+
import { Effect } from "effect";
|
|
33
|
+
import { UniqueConstraintError, ValidationError, } from "../../errors/crud-errors.js";
|
|
34
|
+
/**
|
|
35
|
+
* Check for unique constraint violations when creating or updating an entity.
|
|
36
|
+
*
|
|
37
|
+
* For each normalized constraint (array of field names):
|
|
38
|
+
* 1. Extract the values for all fields in the constraint from the entity
|
|
39
|
+
* 2. Skip if any field value is null or undefined (nulls are not unique-checked)
|
|
40
|
+
* 3. Check if any existing entity (excluding the same ID) has matching values for ALL fields
|
|
41
|
+
* 4. Fail-fast on first violation with UniqueConstraintError
|
|
42
|
+
*
|
|
43
|
+
* @param entity - The entity being created or updated
|
|
44
|
+
* @param existingMap - Current state of the collection
|
|
45
|
+
* @param constraints - Normalized constraints (array of field name arrays)
|
|
46
|
+
* @param collectionName - Name of the collection for error messages
|
|
47
|
+
* @returns Effect that succeeds with void or fails with UniqueConstraintError
|
|
48
|
+
*/
|
|
49
|
+
export const checkUniqueConstraints = (entity, existingMap, constraints, collectionName) => {
|
|
50
|
+
// No constraints configured — nothing to check
|
|
51
|
+
if (constraints.length === 0) {
|
|
52
|
+
return Effect.void;
|
|
53
|
+
}
|
|
54
|
+
const entityRecord = entity;
|
|
55
|
+
for (const constraintFields of constraints) {
|
|
56
|
+
// Extract values for this constraint
|
|
57
|
+
const constraintValues = {};
|
|
58
|
+
let hasNullOrUndefined = false;
|
|
59
|
+
for (const field of constraintFields) {
|
|
60
|
+
const value = entityRecord[field];
|
|
61
|
+
if (value === null || value === undefined) {
|
|
62
|
+
hasNullOrUndefined = true;
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
constraintValues[field] = value;
|
|
66
|
+
}
|
|
67
|
+
// Skip constraint if any field is null/undefined
|
|
68
|
+
if (hasNullOrUndefined) {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
// Check against existing entities
|
|
72
|
+
for (const [existingId, existing] of existingMap) {
|
|
73
|
+
// Exclude the entity itself (for updates)
|
|
74
|
+
if (existingId === entity.id) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const existingRecord = existing;
|
|
78
|
+
// Check if ALL fields in the constraint match
|
|
79
|
+
let allFieldsMatch = true;
|
|
80
|
+
for (const field of constraintFields) {
|
|
81
|
+
if (existingRecord[field] !== constraintValues[field]) {
|
|
82
|
+
allFieldsMatch = false;
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (allFieldsMatch) {
|
|
87
|
+
// Generate constraint name: "unique_" + fields joined by "_"
|
|
88
|
+
const constraintName = `unique_${constraintFields.join("_")}`;
|
|
89
|
+
return Effect.fail(new UniqueConstraintError({
|
|
90
|
+
collection: collectionName,
|
|
91
|
+
constraint: constraintName,
|
|
92
|
+
fields: constraintFields,
|
|
93
|
+
values: constraintValues,
|
|
94
|
+
existingId,
|
|
95
|
+
message: `Unique constraint violation on ${collectionName}: ${constraintName} (${constraintFields.join(", ")}) = ${JSON.stringify(constraintValues)} already exists (id: ${existingId})`,
|
|
96
|
+
}));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return Effect.void;
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* Check for unique constraint violations when creating multiple entities in a batch.
|
|
104
|
+
*
|
|
105
|
+
* This function performs two types of checks:
|
|
106
|
+
* 1. Each entity against the existing map (same as checkUniqueConstraints)
|
|
107
|
+
* 2. Each entity against other entities in the batch (inter-batch check)
|
|
108
|
+
*
|
|
109
|
+
* The inter-batch check ensures that if entities at index 3 and 7 both have
|
|
110
|
+
* email: "alice@example.com", entity 7 fails (the later one in the batch).
|
|
111
|
+
*
|
|
112
|
+
* @param entities - The entities being created in the batch
|
|
113
|
+
* @param existingMap - Current state of the collection
|
|
114
|
+
* @param constraints - Normalized constraints (array of field name arrays)
|
|
115
|
+
* @param collectionName - Name of the collection for error messages
|
|
116
|
+
* @returns Effect that succeeds with void or fails with UniqueConstraintError
|
|
117
|
+
*/
|
|
118
|
+
export const checkBatchUniqueConstraints = (entities, existingMap, constraints, collectionName) => {
|
|
119
|
+
// No constraints configured — nothing to check
|
|
120
|
+
if (constraints.length === 0) {
|
|
121
|
+
return Effect.void;
|
|
122
|
+
}
|
|
123
|
+
// No entities to check
|
|
124
|
+
if (entities.length === 0) {
|
|
125
|
+
return Effect.void;
|
|
126
|
+
}
|
|
127
|
+
// Build a lookup index from entities processed so far in the batch
|
|
128
|
+
// Key: constraintName + ":" + serialized values, Value: entity id
|
|
129
|
+
const batchIndex = new Map();
|
|
130
|
+
for (const entity of entities) {
|
|
131
|
+
const entityRecord = entity;
|
|
132
|
+
for (const constraintFields of constraints) {
|
|
133
|
+
// Extract values for this constraint
|
|
134
|
+
const constraintValues = {};
|
|
135
|
+
let hasNullOrUndefined = false;
|
|
136
|
+
for (const field of constraintFields) {
|
|
137
|
+
const value = entityRecord[field];
|
|
138
|
+
if (value === null || value === undefined) {
|
|
139
|
+
hasNullOrUndefined = true;
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
constraintValues[field] = value;
|
|
143
|
+
}
|
|
144
|
+
// Skip constraint if any field is null/undefined
|
|
145
|
+
if (hasNullOrUndefined) {
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
const constraintName = `unique_${constraintFields.join("_")}`;
|
|
149
|
+
// Create a stable key for this constraint+values combination
|
|
150
|
+
const valuesKey = constraintFields
|
|
151
|
+
.map((f) => constraintValues[f])
|
|
152
|
+
.join("\0");
|
|
153
|
+
const indexKey = `${constraintName}:${valuesKey}`;
|
|
154
|
+
// Check against existing entities in the collection
|
|
155
|
+
for (const [existingId, existing] of existingMap) {
|
|
156
|
+
// Exclude the entity itself (for updates)
|
|
157
|
+
if (existingId === entity.id) {
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
const existingRecord = existing;
|
|
161
|
+
// Check if ALL fields in the constraint match
|
|
162
|
+
let allFieldsMatch = true;
|
|
163
|
+
for (const field of constraintFields) {
|
|
164
|
+
if (existingRecord[field] !== constraintValues[field]) {
|
|
165
|
+
allFieldsMatch = false;
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (allFieldsMatch) {
|
|
170
|
+
return Effect.fail(new UniqueConstraintError({
|
|
171
|
+
collection: collectionName,
|
|
172
|
+
constraint: constraintName,
|
|
173
|
+
fields: constraintFields,
|
|
174
|
+
values: constraintValues,
|
|
175
|
+
existingId,
|
|
176
|
+
message: `Unique constraint violation on ${collectionName}: ${constraintName} (${constraintFields.join(", ")}) = ${JSON.stringify(constraintValues)} already exists (id: ${existingId})`,
|
|
177
|
+
}));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// Check against entities already processed in this batch
|
|
181
|
+
const conflictingId = batchIndex.get(indexKey);
|
|
182
|
+
if (conflictingId !== undefined && conflictingId !== entity.id) {
|
|
183
|
+
return Effect.fail(new UniqueConstraintError({
|
|
184
|
+
collection: collectionName,
|
|
185
|
+
constraint: constraintName,
|
|
186
|
+
fields: constraintFields,
|
|
187
|
+
values: constraintValues,
|
|
188
|
+
existingId: conflictingId,
|
|
189
|
+
message: `Unique constraint violation on ${collectionName}: ${constraintName} (${constraintFields.join(", ")}) = ${JSON.stringify(constraintValues)} already exists in batch (id: ${conflictingId})`,
|
|
190
|
+
}));
|
|
191
|
+
}
|
|
192
|
+
// Add this entity to the batch index
|
|
193
|
+
batchIndex.set(indexKey, entity.id);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return Effect.void;
|
|
197
|
+
};
|
|
198
|
+
// ============================================================================
|
|
199
|
+
// Per-Entity Unique Constraint Checking (for skipDuplicates support)
|
|
200
|
+
// ============================================================================
|
|
201
|
+
/**
|
|
202
|
+
* Check a single entity against unique constraints, including a batch index of
|
|
203
|
+
* previously-checked entities. Used by createMany with skipDuplicates.
|
|
204
|
+
*
|
|
205
|
+
* @param entity - The entity being checked
|
|
206
|
+
* @param entityRecord - The entity as a record for field access
|
|
207
|
+
* @param existingMap - Current state of the collection
|
|
208
|
+
* @param constraints - Normalized constraints
|
|
209
|
+
* @param collectionName - Name of the collection for error messages
|
|
210
|
+
* @param batchIndex - Map of constraint keys to entity IDs from prior entities in the batch
|
|
211
|
+
* @returns Effect that succeeds with void or fails with UniqueConstraintError
|
|
212
|
+
*/
|
|
213
|
+
export const checkEntityUniqueConstraints = (entity, entityRecord, existingMap, constraints, collectionName, batchIndex) => {
|
|
214
|
+
// No constraints configured — nothing to check
|
|
215
|
+
if (constraints.length === 0) {
|
|
216
|
+
return Effect.void;
|
|
217
|
+
}
|
|
218
|
+
for (const constraintFields of constraints) {
|
|
219
|
+
// Extract values for this constraint
|
|
220
|
+
const constraintValues = {};
|
|
221
|
+
let hasNullOrUndefined = false;
|
|
222
|
+
for (const field of constraintFields) {
|
|
223
|
+
const value = entityRecord[field];
|
|
224
|
+
if (value === null || value === undefined) {
|
|
225
|
+
hasNullOrUndefined = true;
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
constraintValues[field] = value;
|
|
229
|
+
}
|
|
230
|
+
// Skip constraint if any field is null/undefined
|
|
231
|
+
if (hasNullOrUndefined) {
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
const constraintName = `unique_${constraintFields.join("_")}`;
|
|
235
|
+
// Create a stable key for this constraint+values combination
|
|
236
|
+
const valuesKey = constraintFields
|
|
237
|
+
.map((f) => constraintValues[f])
|
|
238
|
+
.join("\0");
|
|
239
|
+
const indexKey = `${constraintName}:${valuesKey}`;
|
|
240
|
+
// Check against existing entities in the collection
|
|
241
|
+
for (const [existingId, existing] of existingMap) {
|
|
242
|
+
// Exclude the entity itself (for updates)
|
|
243
|
+
if (existingId === entity.id) {
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
const existingRecord = existing;
|
|
247
|
+
// Check if ALL fields in the constraint match
|
|
248
|
+
let allFieldsMatch = true;
|
|
249
|
+
for (const field of constraintFields) {
|
|
250
|
+
if (existingRecord[field] !== constraintValues[field]) {
|
|
251
|
+
allFieldsMatch = false;
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
if (allFieldsMatch) {
|
|
256
|
+
return Effect.fail(new UniqueConstraintError({
|
|
257
|
+
collection: collectionName,
|
|
258
|
+
constraint: constraintName,
|
|
259
|
+
fields: constraintFields,
|
|
260
|
+
values: constraintValues,
|
|
261
|
+
existingId,
|
|
262
|
+
message: `Unique constraint violation on ${collectionName}: ${constraintName} (${constraintFields.join(", ")}) = ${JSON.stringify(constraintValues)} already exists (id: ${existingId})`,
|
|
263
|
+
}));
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
// Check against entities already processed in this batch
|
|
267
|
+
const conflictingId = batchIndex.get(indexKey);
|
|
268
|
+
if (conflictingId !== undefined && conflictingId !== entity.id) {
|
|
269
|
+
return Effect.fail(new UniqueConstraintError({
|
|
270
|
+
collection: collectionName,
|
|
271
|
+
constraint: constraintName,
|
|
272
|
+
fields: constraintFields,
|
|
273
|
+
values: constraintValues,
|
|
274
|
+
existingId: conflictingId,
|
|
275
|
+
message: `Unique constraint violation on ${collectionName}: ${constraintName} (${constraintFields.join(", ")}) = ${JSON.stringify(constraintValues)} already exists in batch (id: ${conflictingId})`,
|
|
276
|
+
}));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return Effect.void;
|
|
280
|
+
};
|
|
281
|
+
/**
|
|
282
|
+
* Add an entity's unique constraint values to the batch index.
|
|
283
|
+
* Called after an entity passes unique constraint checks.
|
|
284
|
+
*
|
|
285
|
+
* @param entity - The entity to add
|
|
286
|
+
* @param entityRecord - The entity as a record for field access
|
|
287
|
+
* @param constraints - Normalized constraints
|
|
288
|
+
* @param batchIndex - Map to populate with constraint keys -> entity IDs
|
|
289
|
+
*/
|
|
290
|
+
export const addEntityToBatchIndex = (entity, entityRecord, constraints, batchIndex) => {
|
|
291
|
+
for (const constraintFields of constraints) {
|
|
292
|
+
// Extract values for this constraint
|
|
293
|
+
const constraintValues = {};
|
|
294
|
+
let hasNullOrUndefined = false;
|
|
295
|
+
for (const field of constraintFields) {
|
|
296
|
+
const value = entityRecord[field];
|
|
297
|
+
if (value === null || value === undefined) {
|
|
298
|
+
hasNullOrUndefined = true;
|
|
299
|
+
break;
|
|
300
|
+
}
|
|
301
|
+
constraintValues[field] = value;
|
|
302
|
+
}
|
|
303
|
+
// Skip constraint if any field is null/undefined
|
|
304
|
+
if (hasNullOrUndefined) {
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
const constraintName = `unique_${constraintFields.join("_")}`;
|
|
308
|
+
const valuesKey = constraintFields
|
|
309
|
+
.map((f) => constraintValues[f])
|
|
310
|
+
.join("\0");
|
|
311
|
+
const indexKey = `${constraintName}:${valuesKey}`;
|
|
312
|
+
// Add this entity to the batch index
|
|
313
|
+
batchIndex.set(indexKey, entity.id);
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
// ============================================================================
|
|
317
|
+
// Upsert Where Clause Validation
|
|
318
|
+
// ============================================================================
|
|
319
|
+
/**
|
|
320
|
+
* Validate that an upsert where clause targets a declared unique field or id.
|
|
321
|
+
*
|
|
322
|
+
* The where clause must fully cover at least one declared constraint:
|
|
323
|
+
* - `{ id: "..." }` — always valid (id is implicitly unique)
|
|
324
|
+
* - `{ email: "..." }` — valid if `[["email"]]` is in constraints
|
|
325
|
+
* - `{ userId: "u1", settingKey: "theme" }` — valid if `[["userId", "settingKey"]]` is in constraints
|
|
326
|
+
* - Extra fields beyond the constraint are allowed (for additional filtering)
|
|
327
|
+
*
|
|
328
|
+
* If no constraint is fully covered by the where clause, fail with ValidationError
|
|
329
|
+
* listing the valid unique fields.
|
|
330
|
+
*
|
|
331
|
+
* @param where - The where clause object from upsert
|
|
332
|
+
* @param constraints - Normalized constraints (array of field name arrays)
|
|
333
|
+
* @param collectionName - Name of the collection for error messages
|
|
334
|
+
* @returns Effect that succeeds with void or fails with ValidationError
|
|
335
|
+
*/
|
|
336
|
+
export const validateUpsertWhere = (where, constraints, collectionName) => {
|
|
337
|
+
const whereKeys = Object.keys(where);
|
|
338
|
+
// `id` is always a valid constraint (implicitly unique)
|
|
339
|
+
if (whereKeys.includes("id")) {
|
|
340
|
+
return Effect.void;
|
|
341
|
+
}
|
|
342
|
+
// Check if where keys cover at least one declared constraint
|
|
343
|
+
for (const constraintFields of constraints) {
|
|
344
|
+
// All fields in the constraint must be present in where keys
|
|
345
|
+
const coversConstraint = constraintFields.every((field) => whereKeys.includes(field));
|
|
346
|
+
if (coversConstraint) {
|
|
347
|
+
return Effect.void;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
// No constraint is covered — build error message
|
|
351
|
+
const validFields = buildValidFieldsDescription(constraints);
|
|
352
|
+
return Effect.fail(new ValidationError({
|
|
353
|
+
message: `Upsert where clause must target a unique field or id`,
|
|
354
|
+
issues: [
|
|
355
|
+
{
|
|
356
|
+
field: "where",
|
|
357
|
+
message: `Where clause does not match any declared unique field in collection '${collectionName}'. Valid unique fields: ${validFields}`,
|
|
358
|
+
value: where,
|
|
359
|
+
},
|
|
360
|
+
],
|
|
361
|
+
}));
|
|
362
|
+
};
|
|
363
|
+
/**
|
|
364
|
+
* Build a human-readable description of valid unique fields for error messages.
|
|
365
|
+
*
|
|
366
|
+
* @param constraints - Normalized constraints
|
|
367
|
+
* @returns String like "email, username" or "(userId, settingKey)" for compounds
|
|
368
|
+
*/
|
|
369
|
+
const buildValidFieldsDescription = (constraints) => {
|
|
370
|
+
if (constraints.length === 0) {
|
|
371
|
+
return "id";
|
|
372
|
+
}
|
|
373
|
+
const descriptions = constraints.map((constraintFields) => {
|
|
374
|
+
if (constraintFields.length === 1) {
|
|
375
|
+
return constraintFields[0];
|
|
376
|
+
}
|
|
377
|
+
// Compound constraint: show as tuple notation
|
|
378
|
+
return `(${constraintFields.join(", ")})`;
|
|
379
|
+
});
|
|
380
|
+
// Always include id as valid
|
|
381
|
+
return [...descriptions, "id"].join(", ");
|
|
382
|
+
};
|
|
383
|
+
//# sourceMappingURL=unique-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unique-check.js","sourceRoot":"","sources":["../../../src/operations/crud/unique-check.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Effect-based update operations with relationship support.
|
|
3
|
+
*
|
|
4
|
+
* Uses Ref<ReadonlyMap> for atomic state mutation, Effect Schema for validation,
|
|
5
|
+
* and typed errors. Supports $disconnect, $connect, $update, $delete, and $set
|
|
6
|
+
* operations for both ref (single) and inverse (many) relationship types.
|
|
7
|
+
*/
|
|
8
|
+
import { Effect, PubSub, Ref, type Schema } from "effect";
|
|
9
|
+
import { ForeignKeyError, NotFoundError, type OperationError, ValidationError } from "../../errors/crud-errors.js";
|
|
10
|
+
import type { ComputedFieldsConfig } from "../../types/computed-types.js";
|
|
11
|
+
import type { UpdateWithRelationshipsInput } from "../../types/crud-relationship-types.js";
|
|
12
|
+
import type { ChangeEvent } from "../../types/reactive-types.js";
|
|
13
|
+
import type { RelationshipDef } from "../../types/types.js";
|
|
14
|
+
type HasId = {
|
|
15
|
+
readonly id: string;
|
|
16
|
+
};
|
|
17
|
+
type RelationshipConfig = {
|
|
18
|
+
readonly type: "ref" | "inverse";
|
|
19
|
+
readonly target?: string;
|
|
20
|
+
readonly __targetCollection?: string;
|
|
21
|
+
readonly foreignKey?: string;
|
|
22
|
+
};
|
|
23
|
+
type CollectionConfig = {
|
|
24
|
+
readonly schema: Schema.Schema<HasId, unknown>;
|
|
25
|
+
readonly relationships: Record<string, RelationshipConfig>;
|
|
26
|
+
};
|
|
27
|
+
type DatabaseConfig = Record<string, CollectionConfig>;
|
|
28
|
+
/**
|
|
29
|
+
* Update a single entity with relationship support.
|
|
30
|
+
*
|
|
31
|
+
* Steps:
|
|
32
|
+
* 1. Strip computed field keys from input (they are derived, not stored)
|
|
33
|
+
* 2. Look up existing entity by ID
|
|
34
|
+
* 3. Parse relationship operations from input
|
|
35
|
+
* 4. Extract base entity updates (non-relationship fields)
|
|
36
|
+
* 5. Process $disconnect: set FK to null (ref) or update inverse entities
|
|
37
|
+
* 6. Process $connect: set FK (ref) or update inverse entity FKs
|
|
38
|
+
* 7. Process $update: update related entities in target collections
|
|
39
|
+
* 8. Process $delete: disconnect specific inverse entities
|
|
40
|
+
* 9. Process $set: replace all inverse relationships
|
|
41
|
+
* 10. Merge base updates, validate, and update the entity
|
|
42
|
+
*/
|
|
43
|
+
export declare const updateWithRelationships: <T extends HasId, I = T>(collectionName: string, schema: Schema.Schema<T, I>, relationships: Record<string, RelationshipConfig>, ref: Ref.Ref<ReadonlyMap<string, T>>, stateRefs: Record<string, Ref.Ref<ReadonlyMap<string, HasId>>>, dbConfig: DatabaseConfig, computed?: ComputedFieldsConfig<unknown>, changePubSub?: PubSub.PubSub<ChangeEvent>) => (id: string, input: UpdateWithRelationshipsInput<T, Record<string, RelationshipDef>>) => Effect.Effect<T, ValidationError | NotFoundError | ForeignKeyError | OperationError>;
|
|
44
|
+
export {};
|
|
45
|
+
//# sourceMappingURL=update-with-relationships.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-with-relationships.d.ts","sourceRoot":"","sources":["../../../src/operations/crud/update-with-relationships.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC1D,OAAO,EACN,eAAe,EACf,aAAa,EACb,KAAK,cAAc,EACnB,eAAe,EACf,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAC1E,OAAO,KAAK,EAIX,4BAA4B,EAC5B,MAAM,wCAAwC,CAAC;AAGhD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAQ5D,KAAK,KAAK,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAErC,KAAK,kBAAkB,GAAG;IACzB,QAAQ,CAAC,IAAI,EAAE,KAAK,GAAG,SAAS,CAAC;IACjC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B,CAAC;AAEF,KAAK,gBAAgB,GAAG;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAC/C,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;CAC3D,CAAC;AAEF,KAAK,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;AA0avD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,uBAAuB,GAClC,CAAC,SAAS,KAAK,EAAE,CAAC,GAAG,CAAC,EACtB,gBAAgB,MAAM,EACtB,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,EAC3B,eAAe,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,EACjD,KAAK,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EACpC,WAAW,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,EAC9D,UAAU,cAAc,EACxB,WAAW,oBAAoB,CAAC,OAAO,CAAC,EACxC,eAAe,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,MAGzC,IAAI,MAAM,EACV,OAAO,4BAA4B,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,KACrE,MAAM,CAAC,MAAM,CACf,CAAC,EACD,eAAe,GAAG,aAAa,GAAG,eAAe,GAAG,cAAc,CAqThE,CAAC"}
|