@entifix/core 0.7.2 → 0.9.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/README.md +3 -3
- package/dist/envelope/make-envelope.d.ts +7 -1
- package/dist/envelope/make-envelope.d.ts.map +1 -1
- package/dist/envelope/make-envelope.js +12 -0
- package/dist/envelope/make-envelope.js.map +1 -1
- package/dist/envelope/read-envelope.d.ts +7 -0
- package/dist/envelope/read-envelope.d.ts.map +1 -1
- package/dist/envelope/read-envelope.js +20 -0
- package/dist/envelope/read-envelope.js.map +1 -1
- package/dist/envelope/types.d.ts +3 -1
- package/dist/envelope/types.d.ts.map +1 -1
- package/dist/envelope/types.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/query/fold.d.ts +40 -0
- package/dist/query/fold.d.ts.map +1 -0
- package/dist/query/fold.js +49 -0
- package/dist/query/fold.js.map +1 -0
- package/dist/query/in-memory-query.d.ts +12 -1
- package/dist/query/in-memory-query.d.ts.map +1 -1
- package/dist/query/in-memory-query.js +94 -19
- package/dist/query/in-memory-query.js.map +1 -1
- package/dist/rsql/facet-query-url.d.ts +25 -0
- package/dist/rsql/facet-query-url.d.ts.map +1 -0
- package/dist/rsql/facet-query-url.js +63 -0
- package/dist/rsql/facet-query-url.js.map +1 -0
- package/dist/rsql/index.d.ts +1 -0
- package/dist/rsql/index.d.ts.map +1 -1
- package/dist/rsql/index.js +1 -0
- package/dist/rsql/index.js.map +1 -1
- package/dist/types/EntityFacets.d.ts +30 -0
- package/dist/types/EntityFacets.d.ts.map +1 -0
- package/dist/types/EntityFacets.js +10 -0
- package/dist/types/EntityFacets.js.map +1 -0
- package/dist/types/EntityFiltering.d.ts +46 -1
- package/dist/types/EntityFiltering.d.ts.map +1 -1
- package/dist/types/EntityFiltering.js.map +1 -1
- package/dist/types/EntitySelection.d.ts +17 -0
- package/dist/types/EntitySelection.d.ts.map +1 -1
- package/dist/types/EntitySelection.js +19 -0
- package/dist/types/EntitySelection.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,9 +12,9 @@ pnpm add @entifix/core effect
|
|
|
12
12
|
|
|
13
13
|
## Entry points
|
|
14
14
|
|
|
15
|
-
| Import | What it holds
|
|
16
|
-
| --------------- |
|
|
17
|
-
| `@entifix/core` | Decorators (`entity`, `accessor`, `method`, `useCase`), metadata readers, serialization, envelopes, the RSQL parser and serializer, locales, wizard state, selection |
|
|
15
|
+
| Import | What it holds |
|
|
16
|
+
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
17
|
+
| `@entifix/core` | Decorators (`entity`, `accessor`, `method`, `useCase`), metadata readers, serialization, envelopes, the RSQL parser and serializer and the facet-count query protocol, the in-memory query (`like` ignores accents and case, by the rule in `foldText`) and `countFacets`, locales, wizard state, selection |
|
|
18
18
|
|
|
19
19
|
---
|
|
20
20
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { EntityMetadataDocument } from '../entity-definition/metadata/index.js';
|
|
2
2
|
import type { Entity, EntityConstructor } from '../types/Entity.js';
|
|
3
|
+
import type { EntityFacetCounts } from '../types/EntityFacets.js';
|
|
3
4
|
import type { EntityPage } from '../types/EntityPage.js';
|
|
4
|
-
import type { DomainEvent, EntifixEnvelope, EntifixEnvelopeLink, EntifixEnvelopeType, EntityCollectionEnvelope, EntityEnvelope, EntityMetadataEnvelope, EntityPageEnvelope, EventEnvelope } from './types.js';
|
|
5
|
+
import type { DomainEvent, EntifixEnvelope, EntifixEnvelopeLink, EntifixEnvelopeType, EntityCollectionEnvelope, EntityEnvelope, EntityFacetsEnvelope, EntityMetadataEnvelope, EntityPageEnvelope, EventEnvelope } from './types.js';
|
|
5
6
|
/**
|
|
6
7
|
* The transport-free envelope builder for payloads that are not serialized
|
|
7
8
|
* entities — `command`/`transactionRecord` messages carry their own `data`
|
|
@@ -28,6 +29,11 @@ export declare function makeEntityPageEnvelope<TEntity extends Entity>(entityCon
|
|
|
28
29
|
* makes the document per entity rather than per service.
|
|
29
30
|
*/
|
|
30
31
|
export declare function makeEntityMetadataEnvelope<TEntity extends Entity>(entityConstructor: EntityConstructor<TEntity>, document: EntityMetadataDocument, links?: EntifixEnvelopeLink[]): EntityMetadataEnvelope;
|
|
32
|
+
/**
|
|
33
|
+
* The response to `GET /api/<entity>/$facets`: the counts as they are, since a
|
|
34
|
+
* count is a plain shape of values and numbers with nothing to serialize.
|
|
35
|
+
*/
|
|
36
|
+
export declare function makeEntityFacetsEnvelope<TEntity extends Entity>(entityConstructor: EntityConstructor<TEntity>, counts: EntityFacetCounts): EntityFacetsEnvelope;
|
|
31
37
|
/**
|
|
32
38
|
* Frames a domain event for the bus.
|
|
33
39
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"make-envelope.d.ts","sourceRoot":"","sources":["../../src/envelope/make-envelope.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AAKrF,OAAO,KAAK,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,KAAK,EACV,WAAW,EACX,eAAe,EACf,mBAAmB,EACnB,mBAAmB,EACnB,wBAAwB,EACxB,cAAc,EACd,sBAAsB,EACtB,kBAAkB,EAClB,aAAa,EACd,MAAM,YAAY,CAAC;AAEpB;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAChC,IAAI,EAAE,mBAAmB,EACzB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,KAAK,EACX,KAAK,CAAC,EAAE,mBAAmB,EAAE,GAC5B,eAAe,CAAC,KAAK,CAAC,CASxB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,SAAS,MAAM,EACvD,iBAAiB,EAAE,iBAAiB,CAAC,OAAO,CAAC,GAC5C,MAAM,CAGR;AAED,wBAAgB,kBAAkB,CAAC,OAAO,SAAS,MAAM,EACvD,iBAAiB,EAAE,iBAAiB,CAAC,OAAO,CAAC,EAC7C,QAAQ,EAAE,OAAO,EACjB,KAAK,CAAC,EAAE,mBAAmB,EAAE,GAC5B,cAAc,CAShB;AAED,wBAAgB,4BAA4B,CAAC,OAAO,SAAS,MAAM,EACjE,iBAAiB,EAAE,iBAAiB,CAAC,OAAO,CAAC,EAC7C,SAAS,EAAE,SAAS,OAAO,EAAE,EAC7B,KAAK,CAAC,EAAE,mBAAmB,EAAE,GAC5B,wBAAwB,CAS1B;AAED,wBAAgB,sBAAsB,CAAC,OAAO,SAAS,MAAM,EAC3D,iBAAiB,EAAE,iBAAiB,CAAC,OAAO,CAAC,EAC7C,IAAI,EAAE,UAAU,CAAC,OAAO,CAAC,EACzB,KAAK,CAAC,EAAE,mBAAmB,EAAE,GAC5B,kBAAkB,CAAC,OAAO,CAAC,CAa7B;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,SAAS,MAAM,EAC/D,iBAAiB,EAAE,iBAAiB,CAAC,OAAO,CAAC,EAC7C,QAAQ,EAAE,sBAAsB,EAChC,KAAK,CAAC,EAAE,mBAAmB,EAAE,GAC5B,sBAAsB,CASxB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EACrC,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC,GACxB,aAAa,CAAC,KAAK,CAAC,CAMtB"}
|
|
1
|
+
{"version":3,"file":"make-envelope.d.ts","sourceRoot":"","sources":["../../src/envelope/make-envelope.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AAKrF,OAAO,KAAK,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,KAAK,EACV,WAAW,EACX,eAAe,EACf,mBAAmB,EACnB,mBAAmB,EACnB,wBAAwB,EACxB,cAAc,EACd,oBAAoB,EACpB,sBAAsB,EACtB,kBAAkB,EAClB,aAAa,EACd,MAAM,YAAY,CAAC;AAEpB;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAChC,IAAI,EAAE,mBAAmB,EACzB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,KAAK,EACX,KAAK,CAAC,EAAE,mBAAmB,EAAE,GAC5B,eAAe,CAAC,KAAK,CAAC,CASxB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,SAAS,MAAM,EACvD,iBAAiB,EAAE,iBAAiB,CAAC,OAAO,CAAC,GAC5C,MAAM,CAGR;AAED,wBAAgB,kBAAkB,CAAC,OAAO,SAAS,MAAM,EACvD,iBAAiB,EAAE,iBAAiB,CAAC,OAAO,CAAC,EAC7C,QAAQ,EAAE,OAAO,EACjB,KAAK,CAAC,EAAE,mBAAmB,EAAE,GAC5B,cAAc,CAShB;AAED,wBAAgB,4BAA4B,CAAC,OAAO,SAAS,MAAM,EACjE,iBAAiB,EAAE,iBAAiB,CAAC,OAAO,CAAC,EAC7C,SAAS,EAAE,SAAS,OAAO,EAAE,EAC7B,KAAK,CAAC,EAAE,mBAAmB,EAAE,GAC5B,wBAAwB,CAS1B;AAED,wBAAgB,sBAAsB,CAAC,OAAO,SAAS,MAAM,EAC3D,iBAAiB,EAAE,iBAAiB,CAAC,OAAO,CAAC,EAC7C,IAAI,EAAE,UAAU,CAAC,OAAO,CAAC,EACzB,KAAK,CAAC,EAAE,mBAAmB,EAAE,GAC5B,kBAAkB,CAAC,OAAO,CAAC,CAa7B;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,SAAS,MAAM,EAC/D,iBAAiB,EAAE,iBAAiB,CAAC,OAAO,CAAC,EAC7C,QAAQ,EAAE,sBAAsB,EAChC,KAAK,CAAC,EAAE,mBAAmB,EAAE,GAC5B,sBAAsB,CASxB;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,SAAS,MAAM,EAC7D,iBAAiB,EAAE,iBAAiB,CAAC,OAAO,CAAC,EAC7C,MAAM,EAAE,iBAAiB,GACxB,oBAAoB,CAQtB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EACrC,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC,GACxB,aAAa,CAAC,KAAK,CAAC,CAMtB"}
|
|
@@ -83,6 +83,18 @@ export function makeEntityPageEnvelope(entityConstructor, page, links) {
|
|
|
83
83
|
data: document
|
|
84
84
|
};
|
|
85
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* The response to `GET /api/<entity>/$facets`: the counts as they are, since a
|
|
88
|
+
* count is a plain shape of values and numbers with nothing to serialize.
|
|
89
|
+
*/ export function makeEntityFacetsEnvelope(entityConstructor, counts) {
|
|
90
|
+
return {
|
|
91
|
+
meta: {
|
|
92
|
+
type: 'entityFacets',
|
|
93
|
+
entity: envelopeEntityName(entityConstructor)
|
|
94
|
+
},
|
|
95
|
+
data: counts
|
|
96
|
+
};
|
|
97
|
+
}
|
|
86
98
|
/**
|
|
87
99
|
* Frames a domain event for the bus.
|
|
88
100
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/envelope/make-envelope.ts"],"sourcesContent":["import { extractMetaEntity } from '../entity-definition/helpers/index.js';\nimport type { EntityMetadataDocument } from '../entity-definition/metadata/index.js';\nimport {\n serializeEntity,\n serializeEntityCollection,\n} from '../entity-definition/serializer/index.js';\nimport type { Entity, EntityConstructor } from '../types/Entity.js';\nimport type { EntityPage } from '../types/EntityPage.js';\nimport type {\n DomainEvent,\n EntifixEnvelope,\n EntifixEnvelopeLink,\n EntifixEnvelopeType,\n EntityCollectionEnvelope,\n EntityEnvelope,\n EntityMetadataEnvelope,\n EntityPageEnvelope,\n EventEnvelope,\n} from './types.js';\n\n/**\n * The transport-free envelope builder for payloads that are not serialized\n * entities — `command`/`transactionRecord` messages carry their own `data`\n * shapes (defined by the transactions layer), so they cannot go through the\n * entity builders. `entity` is still the routing label (`meta.entity`): the\n * target entity's `key` for a command, or the entity/transaction subject for an\n * event.\n */\nexport function makeEnvelope<TData>(\n type: EntifixEnvelopeType,\n entity: string,\n data: TData,\n links?: EntifixEnvelopeLink[],\n): EntifixEnvelope<TData> {\n return {\n meta: {\n type,\n entity,\n ...(links ? { links } : {}),\n },\n data,\n };\n}\n\n/**\n * The entity name carried in `meta.entity` — the same `key ?? name` resolution\n * every adapter uses for collections and endpoints.\n */\nexport function envelopeEntityName<TEntity extends Entity>(\n entityConstructor: EntityConstructor<TEntity>,\n): string {\n const metaEntity = extractMetaEntity(entityConstructor);\n return metaEntity.key ?? metaEntity.name;\n}\n\nexport function makeEntityEnvelope<TEntity extends Entity>(\n entityConstructor: EntityConstructor<TEntity>,\n instance: TEntity,\n links?: EntifixEnvelopeLink[],\n): EntityEnvelope {\n return {\n meta: {\n type: 'entity',\n entity: envelopeEntityName(entityConstructor),\n ...(links ? { links } : {}),\n },\n data: serializeEntity(entityConstructor, instance),\n };\n}\n\nexport function makeEntityCollectionEnvelope<TEntity extends Entity>(\n entityConstructor: EntityConstructor<TEntity>,\n instances: readonly TEntity[],\n links?: EntifixEnvelopeLink[],\n): EntityCollectionEnvelope {\n return {\n meta: {\n type: 'entityCollection',\n entity: envelopeEntityName(entityConstructor),\n ...(links ? { links } : {}),\n },\n data: serializeEntityCollection(entityConstructor, instances),\n };\n}\n\nexport function makeEntityPageEnvelope<TEntity extends Entity>(\n entityConstructor: EntityConstructor<TEntity>,\n page: EntityPage<TEntity>,\n links?: EntifixEnvelopeLink[],\n): EntityPageEnvelope<TEntity> {\n return {\n meta: {\n type: 'entityPage',\n entity: envelopeEntityName(entityConstructor),\n ...(links ? { links } : {}),\n },\n data: {\n items: serializeEntityCollection(entityConstructor, page.items),\n total: page.total,\n request: page.request,\n },\n };\n}\n\n/**\n * The response to `GET /api/<entity>/$metadata`.\n *\n * Unlike its neighbours this serializes nothing: the document is already a plain\n * shape of catalog keys and verbs, and it describes the class rather than any\n * instance of it. `meta.entity` is still the target entity's key, which is what\n * makes the document per entity rather than per service.\n */\nexport function makeEntityMetadataEnvelope<TEntity extends Entity>(\n entityConstructor: EntityConstructor<TEntity>,\n document: EntityMetadataDocument,\n links?: EntifixEnvelopeLink[],\n): EntityMetadataEnvelope {\n return {\n meta: {\n type: 'entityMetadata',\n entity: envelopeEntityName(entityConstructor),\n ...(links ? { links } : {}),\n },\n data: document,\n };\n}\n\n/**\n * Frames a domain event for the bus.\n *\n * The split is mechanical and worth stating: everything about the *message*\n * (name, id, source, time, correlation) goes to `meta.event`; the payload — what\n * actually happened — is `data`. A consumer therefore never has to reach into\n * metadata to read its own domain object, and never has to reach into the\n * payload to find the routing key.\n *\n * `meta.entity` is deliberately left unset. A bus message says what it *is* in\n * `meta.event.name`; only the HTTP arm needs an entity label, and inventing one\n * here would put `settlement` on a message about a settlement *run*.\n */\nexport function makeEventEnvelope<TData>(\n event: DomainEvent<TData>,\n): EventEnvelope<TData> {\n const { data, ...eventMeta } = event;\n return {\n meta: { type: 'event', event: eventMeta },\n data,\n };\n}\n"],"names":["extractMetaEntity","serializeEntity","serializeEntityCollection","makeEnvelope","type","entity","data","links","meta","envelopeEntityName","entityConstructor","metaEntity","key","name","makeEntityEnvelope","instance","makeEntityCollectionEnvelope","instances","makeEntityPageEnvelope","page","items","total","request","makeEntityMetadataEnvelope","document","makeEventEnvelope","event","eventMeta"],"mappings":";;AAAA,SAASA,iBAAiB,QAAQ,wCAAwC;AAE1E,SACEC,eAAe,EACfC,yBAAyB,QACpB,2CAA2C;
|
|
1
|
+
{"version":3,"sources":["../../src/envelope/make-envelope.ts"],"sourcesContent":["import { extractMetaEntity } from '../entity-definition/helpers/index.js';\nimport type { EntityMetadataDocument } from '../entity-definition/metadata/index.js';\nimport {\n serializeEntity,\n serializeEntityCollection,\n} from '../entity-definition/serializer/index.js';\nimport type { Entity, EntityConstructor } from '../types/Entity.js';\nimport type { EntityFacetCounts } from '../types/EntityFacets.js';\nimport type { EntityPage } from '../types/EntityPage.js';\nimport type {\n DomainEvent,\n EntifixEnvelope,\n EntifixEnvelopeLink,\n EntifixEnvelopeType,\n EntityCollectionEnvelope,\n EntityEnvelope,\n EntityFacetsEnvelope,\n EntityMetadataEnvelope,\n EntityPageEnvelope,\n EventEnvelope,\n} from './types.js';\n\n/**\n * The transport-free envelope builder for payloads that are not serialized\n * entities — `command`/`transactionRecord` messages carry their own `data`\n * shapes (defined by the transactions layer), so they cannot go through the\n * entity builders. `entity` is still the routing label (`meta.entity`): the\n * target entity's `key` for a command, or the entity/transaction subject for an\n * event.\n */\nexport function makeEnvelope<TData>(\n type: EntifixEnvelopeType,\n entity: string,\n data: TData,\n links?: EntifixEnvelopeLink[],\n): EntifixEnvelope<TData> {\n return {\n meta: {\n type,\n entity,\n ...(links ? { links } : {}),\n },\n data,\n };\n}\n\n/**\n * The entity name carried in `meta.entity` — the same `key ?? name` resolution\n * every adapter uses for collections and endpoints.\n */\nexport function envelopeEntityName<TEntity extends Entity>(\n entityConstructor: EntityConstructor<TEntity>,\n): string {\n const metaEntity = extractMetaEntity(entityConstructor);\n return metaEntity.key ?? metaEntity.name;\n}\n\nexport function makeEntityEnvelope<TEntity extends Entity>(\n entityConstructor: EntityConstructor<TEntity>,\n instance: TEntity,\n links?: EntifixEnvelopeLink[],\n): EntityEnvelope {\n return {\n meta: {\n type: 'entity',\n entity: envelopeEntityName(entityConstructor),\n ...(links ? { links } : {}),\n },\n data: serializeEntity(entityConstructor, instance),\n };\n}\n\nexport function makeEntityCollectionEnvelope<TEntity extends Entity>(\n entityConstructor: EntityConstructor<TEntity>,\n instances: readonly TEntity[],\n links?: EntifixEnvelopeLink[],\n): EntityCollectionEnvelope {\n return {\n meta: {\n type: 'entityCollection',\n entity: envelopeEntityName(entityConstructor),\n ...(links ? { links } : {}),\n },\n data: serializeEntityCollection(entityConstructor, instances),\n };\n}\n\nexport function makeEntityPageEnvelope<TEntity extends Entity>(\n entityConstructor: EntityConstructor<TEntity>,\n page: EntityPage<TEntity>,\n links?: EntifixEnvelopeLink[],\n): EntityPageEnvelope<TEntity> {\n return {\n meta: {\n type: 'entityPage',\n entity: envelopeEntityName(entityConstructor),\n ...(links ? { links } : {}),\n },\n data: {\n items: serializeEntityCollection(entityConstructor, page.items),\n total: page.total,\n request: page.request,\n },\n };\n}\n\n/**\n * The response to `GET /api/<entity>/$metadata`.\n *\n * Unlike its neighbours this serializes nothing: the document is already a plain\n * shape of catalog keys and verbs, and it describes the class rather than any\n * instance of it. `meta.entity` is still the target entity's key, which is what\n * makes the document per entity rather than per service.\n */\nexport function makeEntityMetadataEnvelope<TEntity extends Entity>(\n entityConstructor: EntityConstructor<TEntity>,\n document: EntityMetadataDocument,\n links?: EntifixEnvelopeLink[],\n): EntityMetadataEnvelope {\n return {\n meta: {\n type: 'entityMetadata',\n entity: envelopeEntityName(entityConstructor),\n ...(links ? { links } : {}),\n },\n data: document,\n };\n}\n\n/**\n * The response to `GET /api/<entity>/$facets`: the counts as they are, since a\n * count is a plain shape of values and numbers with nothing to serialize.\n */\nexport function makeEntityFacetsEnvelope<TEntity extends Entity>(\n entityConstructor: EntityConstructor<TEntity>,\n counts: EntityFacetCounts,\n): EntityFacetsEnvelope {\n return {\n meta: {\n type: 'entityFacets',\n entity: envelopeEntityName(entityConstructor),\n },\n data: counts,\n };\n}\n\n/**\n * Frames a domain event for the bus.\n *\n * The split is mechanical and worth stating: everything about the *message*\n * (name, id, source, time, correlation) goes to `meta.event`; the payload — what\n * actually happened — is `data`. A consumer therefore never has to reach into\n * metadata to read its own domain object, and never has to reach into the\n * payload to find the routing key.\n *\n * `meta.entity` is deliberately left unset. A bus message says what it *is* in\n * `meta.event.name`; only the HTTP arm needs an entity label, and inventing one\n * here would put `settlement` on a message about a settlement *run*.\n */\nexport function makeEventEnvelope<TData>(\n event: DomainEvent<TData>,\n): EventEnvelope<TData> {\n const { data, ...eventMeta } = event;\n return {\n meta: { type: 'event', event: eventMeta },\n data,\n };\n}\n"],"names":["extractMetaEntity","serializeEntity","serializeEntityCollection","makeEnvelope","type","entity","data","links","meta","envelopeEntityName","entityConstructor","metaEntity","key","name","makeEntityEnvelope","instance","makeEntityCollectionEnvelope","instances","makeEntityPageEnvelope","page","items","total","request","makeEntityMetadataEnvelope","document","makeEntityFacetsEnvelope","counts","makeEventEnvelope","event","eventMeta"],"mappings":";;AAAA,SAASA,iBAAiB,QAAQ,wCAAwC;AAE1E,SACEC,eAAe,EACfC,yBAAyB,QACpB,2CAA2C;AAiBlD;;;;;;;CAOC,GACD,OAAO,SAASC,aACdC,IAAyB,EACzBC,MAAc,EACdC,IAAW,EACXC,KAA6B;IAE7B,OAAO;QACLC,MAAM;YACJJ;YACAC;WACIE,QAAQ;YAAEA;QAAM,IAAI,CAAC;QAE3BD;IACF;AACF;AAEA;;;CAGC,GACD,OAAO,SAASG,mBACdC,iBAA6C;QAGtCC;IADP,MAAMA,aAAaX,kBAAkBU;IACrC,QAAOC,kBAAAA,WAAWC,GAAG,YAAdD,kBAAkBA,WAAWE,IAAI;AAC1C;AAEA,OAAO,SAASC,mBACdJ,iBAA6C,EAC7CK,QAAiB,EACjBR,KAA6B;IAE7B,OAAO;QACLC,MAAM;YACJJ,MAAM;YACNC,QAAQI,mBAAmBC;WACvBH,QAAQ;YAAEA;QAAM,IAAI,CAAC;QAE3BD,MAAML,gBAAgBS,mBAAmBK;IAC3C;AACF;AAEA,OAAO,SAASC,6BACdN,iBAA6C,EAC7CO,SAA6B,EAC7BV,KAA6B;IAE7B,OAAO;QACLC,MAAM;YACJJ,MAAM;YACNC,QAAQI,mBAAmBC;WACvBH,QAAQ;YAAEA;QAAM,IAAI,CAAC;QAE3BD,MAAMJ,0BAA0BQ,mBAAmBO;IACrD;AACF;AAEA,OAAO,SAASC,uBACdR,iBAA6C,EAC7CS,IAAyB,EACzBZ,KAA6B;IAE7B,OAAO;QACLC,MAAM;YACJJ,MAAM;YACNC,QAAQI,mBAAmBC;WACvBH,QAAQ;YAAEA;QAAM,IAAI,CAAC;QAE3BD,MAAM;YACJc,OAAOlB,0BAA0BQ,mBAAmBS,KAAKC,KAAK;YAC9DC,OAAOF,KAAKE,KAAK;YACjBC,SAASH,KAAKG,OAAO;QACvB;IACF;AACF;AAEA;;;;;;;CAOC,GACD,OAAO,SAASC,2BACdb,iBAA6C,EAC7Cc,QAAgC,EAChCjB,KAA6B;IAE7B,OAAO;QACLC,MAAM;YACJJ,MAAM;YACNC,QAAQI,mBAAmBC;WACvBH,QAAQ;YAAEA;QAAM,IAAI,CAAC;QAE3BD,MAAMkB;IACR;AACF;AAEA;;;CAGC,GACD,OAAO,SAASC,yBACdf,iBAA6C,EAC7CgB,MAAyB;IAEzB,OAAO;QACLlB,MAAM;YACJJ,MAAM;YACNC,QAAQI,mBAAmBC;QAC7B;QACAJ,MAAMoB;IACR;AACF;AAEA;;;;;;;;;;;;CAYC,GACD,OAAO,SAASC,kBACdC,KAAyB;IAEzB,MAAM,EAAEtB,IAAI,EAAgB,GAAGsB,OAAdC,6CAAcD;;;IAC/B,OAAO;QACLpB,MAAM;YAAEJ,MAAM;YAASwB,OAAOC;QAAU;QACxCvB;IACF;AACF"}
|
|
@@ -2,6 +2,7 @@ import { Effect } from 'effect';
|
|
|
2
2
|
import { EntifixBuildError } from '../base-entities/entifix-error/index.js';
|
|
3
3
|
import type { EntityMetadataDocument } from '../entity-definition/metadata/index.js';
|
|
4
4
|
import type { Entity, EntityConstructor } from '../types/Entity.js';
|
|
5
|
+
import type { EntityFacetValue } from '../types/EntityFacets.js';
|
|
5
6
|
import type { DomainEvent, EntifixEnvelope, EntifixEnvelopeType } from './types.js';
|
|
6
7
|
/**
|
|
7
8
|
* Structural check only — `meta.type` is validated separately so a mismatch
|
|
@@ -30,6 +31,12 @@ export declare const readEntityPageEnvelope: <TEntity extends Entity>(entityCons
|
|
|
30
31
|
* the way a half-populated entity does.
|
|
31
32
|
*/
|
|
32
33
|
export declare const readEntityMetadataEnvelope: <TEntity extends Entity>(entityConstructor: EntityConstructor<TEntity>, body: unknown) => Effect.Effect<EntityMetadataDocument, EntifixBuildError, never>;
|
|
34
|
+
/**
|
|
35
|
+
* Reads a `$facets` response back into its counts, refusing a body whose lists
|
|
36
|
+
* are not `{ value, count }` rows — a panel that rendered a malformed count
|
|
37
|
+
* would show a number nobody computed.
|
|
38
|
+
*/
|
|
39
|
+
export declare const readEntityFacetsEnvelope: <TEntity extends Entity>(entityConstructor: EntityConstructor<TEntity>, body: unknown) => Effect.Effect<Readonly<Record<string, readonly EntityFacetValue[]>>, EntifixBuildError, never>;
|
|
33
40
|
/**
|
|
34
41
|
* Reads a bus message back into a {@link DomainEvent}.
|
|
35
42
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"read-envelope.d.ts","sourceRoot":"","sources":["../../src/envelope/read-envelope.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,OAAO,EAAE,iBAAiB,EAAE,MAAM,yCAAyC,CAAC;AAC5E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AAKrF,OAAO,KAAK,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"read-envelope.d.ts","sourceRoot":"","sources":["../../src/envelope/read-envelope.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,OAAO,EAAE,iBAAiB,EAAE,MAAM,yCAAyC,CAAC;AAC5E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AAKrF,OAAO,KAAK,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,KAAK,EAEV,gBAAgB,EACjB,MAAM,0BAA0B,CAAC;AAGlC,OAAO,KAAK,EACV,WAAW,EACX,eAAe,EACf,mBAAmB,EAGpB,MAAM,YAAY,CAAC;AAEpB;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,eAAe,CAQxE;AAmCD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAChC,IAAI,EAAE,OAAO,EACb,QAAQ,EAAE,mBAAmB,EAC7B,KAAK,SAAY,GAChB,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,EAAE,iBAAiB,CAAC,CAoB1D;AAED,eAAO,MAAM,kBAAkB,GAAI,OAAO,SAAS,MAAM,EACvD,mBAAmB,iBAAiB,CAAC,OAAO,CAAC,EAC7C,MAAM,OAAO,qDAwBX,CAAC;AAEL,eAAO,MAAM,4BAA4B,GAAI,OAAO,SAAS,MAAM,EACjE,mBAAmB,iBAAiB,CAAC,OAAO,CAAC,EAC7C,MAAM,OAAO,uDAaX,CAAC;AAEL,eAAO,MAAM,sBAAsB,GAAI,OAAO,SAAS,MAAM,EAC3D,mBAAmB,iBAAiB,CAAC,OAAO,CAAC,EAC7C,MAAM,OAAO;WAYO,OAAO,EAAE;;;4BAI3B,CAAC;AAEL;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,GAAI,OAAO,SAAS,MAAM,EAC/D,mBAAmB,iBAAiB,CAAC,OAAO,CAAC,EAC7C,MAAM,OAAO,oEAwBX,CAAC;AAUL;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,GAAI,OAAO,SAAS,MAAM,EAC7D,mBAAmB,iBAAiB,CAAC,OAAO,CAAC,EAC7C,MAAM,OAAO,mGA2BX,CAAC;AAKL;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,GAAG,OAAO,EAC/C,IAAI,EAAE,OAAO,GACZ,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,iBAAiB,CAAC,CAoBtD"}
|
|
@@ -99,6 +99,26 @@ export const readEntityPageEnvelope = (entityConstructor, body)=>Effect.gen(func
|
|
|
99
99
|
}
|
|
100
100
|
return document;
|
|
101
101
|
});
|
|
102
|
+
const isFacetValueRow = (row)=>row != null && typeof row === 'object' && [
|
|
103
|
+
'string',
|
|
104
|
+
'number',
|
|
105
|
+
'boolean'
|
|
106
|
+
].includes(typeof row.value) && typeof row.count === 'number';
|
|
107
|
+
/**
|
|
108
|
+
* Reads a `$facets` response back into its counts, refusing a body whose lists
|
|
109
|
+
* are not `{ value, count }` rows — a panel that rendered a malformed count
|
|
110
|
+
* would show a number nobody computed.
|
|
111
|
+
*/ export const readEntityFacetsEnvelope = (entityConstructor, body)=>Effect.gen(function*() {
|
|
112
|
+
const envelope = yield* assertEnvelope(entityConstructor, body, 'entityFacets');
|
|
113
|
+
const counts = envelope.data;
|
|
114
|
+
const wellFormed = counts != null && typeof counts === 'object' && Object.values(counts).every((rows)=>Array.isArray(rows) && rows.every(isFacetValueRow));
|
|
115
|
+
if (!wellFormed) {
|
|
116
|
+
return yield* Effect.fail(new EntifixBuildError(`EntifixEnvelope for "${envelopeEntityName(entityConstructor)}" carried no facet counts`, undefined, {
|
|
117
|
+
entity: envelopeEntityName(entityConstructor)
|
|
118
|
+
}));
|
|
119
|
+
}
|
|
120
|
+
return counts;
|
|
121
|
+
});
|
|
102
122
|
/** Every member `meta.event` must carry for a message to be routable at all. */ const REQUIRED_EVENT_META = [
|
|
103
123
|
'name',
|
|
104
124
|
'id',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/envelope/read-envelope.ts"],"sourcesContent":["import { Effect } from 'effect';\n\nimport { EntifixBuildError } from '../base-entities/entifix-error/index.js';\nimport type { EntityMetadataDocument } from '../entity-definition/metadata/index.js';\nimport {\n deserializeEntityCollection,\n deserializeSingleEntity,\n} from '../entity-definition/serializer/index.js';\nimport type { Entity, EntityConstructor } from '../types/Entity.js';\nimport type { EntityPage } from '../types/EntityPage.js';\nimport { envelopeEntityName } from './make-envelope.js';\nimport type {\n DomainEvent,\n EntifixEnvelope,\n EntifixEnvelopeType,\n EntifixEventMeta,\n SerializedEntityPage,\n} from './types.js';\n\n/**\n * Structural check only — `meta.type` is validated separately so a mismatch\n * reports the expected/actual types rather than a generic \"not an envelope\".\n */\nexport function isEntifixEnvelope(body: unknown): body is EntifixEnvelope {\n if (body == null || typeof body !== 'object') {\n return false;\n }\n const meta = (body as EntifixEnvelope).meta;\n return (\n meta != null && typeof meta === 'object' && typeof meta.type === 'string'\n );\n}\n\n/**\n * Narrows an arbitrary body to an envelope of the expected `meta.type`, failing\n * with an {@link EntifixBuildError} otherwise. A wrong shape must never\n * silently deserialize into a half-populated entity.\n */\nfunction assertEnvelope<TEntity extends Entity, TData>(\n entityConstructor: EntityConstructor<TEntity>,\n body: unknown,\n expected: EntifixEnvelopeType,\n): Effect.Effect<EntifixEnvelope<TData>, EntifixBuildError> {\n const entity = envelopeEntityName(entityConstructor);\n\n if (!isEntifixEnvelope(body)) {\n return Effect.fail(\n new EntifixBuildError(\n `Expected an EntifixEnvelope for \"${entity}\" but the payload carried no meta.type`,\n undefined,\n { entity, expected, body },\n ),\n );\n }\n if (body.meta.type !== expected) {\n return Effect.fail(\n new EntifixBuildError(\n `Expected an EntifixEnvelope of type \"${expected}\" for \"${entity}\" but got \"${body.meta.type}\"`,\n undefined,\n { entity, expected, actual: body.meta.type },\n ),\n );\n }\n return Effect.succeed(body as EntifixEnvelope<TData>);\n}\n\n/**\n * Narrows an arbitrary body to an envelope of the expected `meta.type` without\n * requiring an entity constructor — for `command`/`transactionRecord` messages\n * whose `data` is not a serialized entity. `label` only sharpens error text.\n */\nexport function readEnvelope<TData>(\n body: unknown,\n expected: EntifixEnvelopeType,\n label = 'message',\n): Effect.Effect<EntifixEnvelope<TData>, EntifixBuildError> {\n if (!isEntifixEnvelope(body)) {\n return Effect.fail(\n new EntifixBuildError(\n `Expected an EntifixEnvelope for \"${label}\" but the payload carried no meta.type`,\n undefined,\n { label, expected, body },\n ),\n );\n }\n if (body.meta.type !== expected) {\n return Effect.fail(\n new EntifixBuildError(\n `Expected an EntifixEnvelope of type \"${expected}\" for \"${label}\" but got \"${body.meta.type}\"`,\n undefined,\n { label, expected, actual: body.meta.type },\n ),\n );\n }\n return Effect.succeed(body as EntifixEnvelope<TData>);\n}\n\nexport const readEntityEnvelope = <TEntity extends Entity>(\n entityConstructor: EntityConstructor<TEntity>,\n body: unknown,\n) =>\n Effect.gen(function* () {\n const envelope = yield* assertEnvelope<TEntity, unknown>(\n entityConstructor,\n body,\n 'entity',\n );\n const entity = yield* deserializeSingleEntity(\n entityConstructor,\n envelope.data,\n );\n if (entity === undefined) {\n return yield* Effect.fail(\n new EntifixBuildError(\n `EntifixEnvelope for \"${envelopeEntityName(\n entityConstructor,\n )}\" carried no data`,\n undefined,\n { entity: envelopeEntityName(entityConstructor) },\n ),\n );\n }\n return entity;\n });\n\nexport const readEntityCollectionEnvelope = <TEntity extends Entity>(\n entityConstructor: EntityConstructor<TEntity>,\n body: unknown,\n) =>\n Effect.gen(function* () {\n const envelope = yield* assertEnvelope<TEntity, unknown>(\n entityConstructor,\n body,\n 'entityCollection',\n );\n const items = yield* deserializeEntityCollection(\n entityConstructor,\n envelope.data,\n );\n return items as TEntity[];\n });\n\nexport const readEntityPageEnvelope = <TEntity extends Entity>(\n entityConstructor: EntityConstructor<TEntity>,\n body: unknown,\n) =>\n Effect.gen(function* () {\n const envelope = yield* assertEnvelope<\n TEntity,\n SerializedEntityPage<TEntity>\n >(entityConstructor, body, 'entityPage');\n const items = yield* deserializeEntityCollection(\n entityConstructor,\n envelope.data?.items,\n );\n return {\n items: items as TEntity[],\n total: envelope.data?.total ?? 0,\n request: envelope.data?.request ?? {},\n } satisfies EntityPage<TEntity>;\n });\n\n/**\n * Reads a `$metadata` response back into its document.\n *\n * No deserialization step, and no defaulting of the members either: an envelope\n * whose `data` is missing a list would leave a form guessing whether \"no\n * actions\" meant \"denied\" or \"malformed\", so a broken payload fails loudly here\n * the way a half-populated entity does.\n */\nexport const readEntityMetadataEnvelope = <TEntity extends Entity>(\n entityConstructor: EntityConstructor<TEntity>,\n body: unknown,\n) =>\n Effect.gen(function* () {\n const envelope = yield* assertEnvelope<TEntity, EntityMetadataDocument>(\n entityConstructor,\n body,\n 'entityMetadata',\n );\n const document = envelope.data;\n if (\n !Array.isArray(document?.actions) ||\n !Array.isArray(document?.useCases)\n ) {\n return yield* Effect.fail(\n new EntifixBuildError(\n `EntifixEnvelope for \"${envelopeEntityName(\n entityConstructor,\n )}\" carried no metadata document`,\n undefined,\n { entity: envelopeEntityName(entityConstructor) },\n ),\n );\n }\n return document;\n });\n\n/** Every member `meta.event` must carry for a message to be routable at all. */\nconst REQUIRED_EVENT_META = ['name', 'id', 'source', 'at'] as const;\n\n/**\n * Reads a bus message back into a {@link DomainEvent}.\n *\n * The metadata is validated rather than defaulted, and that is the point: a\n * message missing `id` cannot be deduplicated and a message missing `name`\n * cannot be routed, so both are failures a consumer must see rather than\n * absences it silently works around. It is the same call\n * {@link readEntityMetadataEnvelope} makes — a broken payload fails loudly\n * instead of arriving half-populated.\n *\n * Note this does not validate `data`. The transport has no idea what a\n * `catalog.published` payload should look like, and inventing an opinion here\n * is how the bus would start knowing about domains.\n */\nexport function readEventEnvelope<TData = unknown>(\n body: unknown,\n): Effect.Effect<DomainEvent<TData>, EntifixBuildError> {\n return Effect.gen(function* () {\n const envelope = yield* readEnvelope<TData>(body, 'event', 'event');\n const event = envelope.meta.event;\n const missing = REQUIRED_EVENT_META.filter(\n member => typeof event?.[member] !== 'string' || event[member] === '',\n );\n if (missing.length > 0) {\n return yield* Effect.fail(\n new EntifixBuildError(\n `event envelope carried incomplete meta.event (missing ${missing.join(\n ', ',\n )})`,\n undefined,\n { missing, body },\n ),\n );\n }\n return { ...(event as EntifixEventMeta), data: envelope.data };\n });\n}\n"],"names":["Effect","EntifixBuildError","deserializeEntityCollection","deserializeSingleEntity","envelopeEntityName","isEntifixEnvelope","body","meta","type","assertEnvelope","entityConstructor","expected","entity","fail","undefined","actual","succeed","readEnvelope","label","readEntityEnvelope","gen","envelope","data","readEntityCollectionEnvelope","items","readEntityPageEnvelope","total","request","readEntityMetadataEnvelope","document","Array","isArray","actions","useCases","REQUIRED_EVENT_META","readEventEnvelope","event","missing","filter","member","length","join"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/envelope/read-envelope.ts"],"sourcesContent":["import { Effect } from 'effect';\n\nimport { EntifixBuildError } from '../base-entities/entifix-error/index.js';\nimport type { EntityMetadataDocument } from '../entity-definition/metadata/index.js';\nimport {\n deserializeEntityCollection,\n deserializeSingleEntity,\n} from '../entity-definition/serializer/index.js';\nimport type { Entity, EntityConstructor } from '../types/Entity.js';\nimport type {\n EntityFacetCounts,\n EntityFacetValue,\n} from '../types/EntityFacets.js';\nimport type { EntityPage } from '../types/EntityPage.js';\nimport { envelopeEntityName } from './make-envelope.js';\nimport type {\n DomainEvent,\n EntifixEnvelope,\n EntifixEnvelopeType,\n EntifixEventMeta,\n SerializedEntityPage,\n} from './types.js';\n\n/**\n * Structural check only — `meta.type` is validated separately so a mismatch\n * reports the expected/actual types rather than a generic \"not an envelope\".\n */\nexport function isEntifixEnvelope(body: unknown): body is EntifixEnvelope {\n if (body == null || typeof body !== 'object') {\n return false;\n }\n const meta = (body as EntifixEnvelope).meta;\n return (\n meta != null && typeof meta === 'object' && typeof meta.type === 'string'\n );\n}\n\n/**\n * Narrows an arbitrary body to an envelope of the expected `meta.type`, failing\n * with an {@link EntifixBuildError} otherwise. A wrong shape must never\n * silently deserialize into a half-populated entity.\n */\nfunction assertEnvelope<TEntity extends Entity, TData>(\n entityConstructor: EntityConstructor<TEntity>,\n body: unknown,\n expected: EntifixEnvelopeType,\n): Effect.Effect<EntifixEnvelope<TData>, EntifixBuildError> {\n const entity = envelopeEntityName(entityConstructor);\n\n if (!isEntifixEnvelope(body)) {\n return Effect.fail(\n new EntifixBuildError(\n `Expected an EntifixEnvelope for \"${entity}\" but the payload carried no meta.type`,\n undefined,\n { entity, expected, body },\n ),\n );\n }\n if (body.meta.type !== expected) {\n return Effect.fail(\n new EntifixBuildError(\n `Expected an EntifixEnvelope of type \"${expected}\" for \"${entity}\" but got \"${body.meta.type}\"`,\n undefined,\n { entity, expected, actual: body.meta.type },\n ),\n );\n }\n return Effect.succeed(body as EntifixEnvelope<TData>);\n}\n\n/**\n * Narrows an arbitrary body to an envelope of the expected `meta.type` without\n * requiring an entity constructor — for `command`/`transactionRecord` messages\n * whose `data` is not a serialized entity. `label` only sharpens error text.\n */\nexport function readEnvelope<TData>(\n body: unknown,\n expected: EntifixEnvelopeType,\n label = 'message',\n): Effect.Effect<EntifixEnvelope<TData>, EntifixBuildError> {\n if (!isEntifixEnvelope(body)) {\n return Effect.fail(\n new EntifixBuildError(\n `Expected an EntifixEnvelope for \"${label}\" but the payload carried no meta.type`,\n undefined,\n { label, expected, body },\n ),\n );\n }\n if (body.meta.type !== expected) {\n return Effect.fail(\n new EntifixBuildError(\n `Expected an EntifixEnvelope of type \"${expected}\" for \"${label}\" but got \"${body.meta.type}\"`,\n undefined,\n { label, expected, actual: body.meta.type },\n ),\n );\n }\n return Effect.succeed(body as EntifixEnvelope<TData>);\n}\n\nexport const readEntityEnvelope = <TEntity extends Entity>(\n entityConstructor: EntityConstructor<TEntity>,\n body: unknown,\n) =>\n Effect.gen(function* () {\n const envelope = yield* assertEnvelope<TEntity, unknown>(\n entityConstructor,\n body,\n 'entity',\n );\n const entity = yield* deserializeSingleEntity(\n entityConstructor,\n envelope.data,\n );\n if (entity === undefined) {\n return yield* Effect.fail(\n new EntifixBuildError(\n `EntifixEnvelope for \"${envelopeEntityName(\n entityConstructor,\n )}\" carried no data`,\n undefined,\n { entity: envelopeEntityName(entityConstructor) },\n ),\n );\n }\n return entity;\n });\n\nexport const readEntityCollectionEnvelope = <TEntity extends Entity>(\n entityConstructor: EntityConstructor<TEntity>,\n body: unknown,\n) =>\n Effect.gen(function* () {\n const envelope = yield* assertEnvelope<TEntity, unknown>(\n entityConstructor,\n body,\n 'entityCollection',\n );\n const items = yield* deserializeEntityCollection(\n entityConstructor,\n envelope.data,\n );\n return items as TEntity[];\n });\n\nexport const readEntityPageEnvelope = <TEntity extends Entity>(\n entityConstructor: EntityConstructor<TEntity>,\n body: unknown,\n) =>\n Effect.gen(function* () {\n const envelope = yield* assertEnvelope<\n TEntity,\n SerializedEntityPage<TEntity>\n >(entityConstructor, body, 'entityPage');\n const items = yield* deserializeEntityCollection(\n entityConstructor,\n envelope.data?.items,\n );\n return {\n items: items as TEntity[],\n total: envelope.data?.total ?? 0,\n request: envelope.data?.request ?? {},\n } satisfies EntityPage<TEntity>;\n });\n\n/**\n * Reads a `$metadata` response back into its document.\n *\n * No deserialization step, and no defaulting of the members either: an envelope\n * whose `data` is missing a list would leave a form guessing whether \"no\n * actions\" meant \"denied\" or \"malformed\", so a broken payload fails loudly here\n * the way a half-populated entity does.\n */\nexport const readEntityMetadataEnvelope = <TEntity extends Entity>(\n entityConstructor: EntityConstructor<TEntity>,\n body: unknown,\n) =>\n Effect.gen(function* () {\n const envelope = yield* assertEnvelope<TEntity, EntityMetadataDocument>(\n entityConstructor,\n body,\n 'entityMetadata',\n );\n const document = envelope.data;\n if (\n !Array.isArray(document?.actions) ||\n !Array.isArray(document?.useCases)\n ) {\n return yield* Effect.fail(\n new EntifixBuildError(\n `EntifixEnvelope for \"${envelopeEntityName(\n entityConstructor,\n )}\" carried no metadata document`,\n undefined,\n { entity: envelopeEntityName(entityConstructor) },\n ),\n );\n }\n return document;\n });\n\nconst isFacetValueRow = (row: unknown): row is EntityFacetValue =>\n row != null &&\n typeof row === 'object' &&\n ['string', 'number', 'boolean'].includes(\n typeof (row as EntityFacetValue).value,\n ) &&\n typeof (row as EntityFacetValue).count === 'number';\n\n/**\n * Reads a `$facets` response back into its counts, refusing a body whose lists\n * are not `{ value, count }` rows — a panel that rendered a malformed count\n * would show a number nobody computed.\n */\nexport const readEntityFacetsEnvelope = <TEntity extends Entity>(\n entityConstructor: EntityConstructor<TEntity>,\n body: unknown,\n) =>\n Effect.gen(function* () {\n const envelope = yield* assertEnvelope<TEntity, EntityFacetCounts>(\n entityConstructor,\n body,\n 'entityFacets',\n );\n const counts = envelope.data;\n const wellFormed =\n counts != null &&\n typeof counts === 'object' &&\n Object.values(counts).every(\n rows => Array.isArray(rows) && rows.every(isFacetValueRow),\n );\n if (!wellFormed) {\n return yield* Effect.fail(\n new EntifixBuildError(\n `EntifixEnvelope for \"${envelopeEntityName(\n entityConstructor,\n )}\" carried no facet counts`,\n undefined,\n { entity: envelopeEntityName(entityConstructor) },\n ),\n );\n }\n return counts;\n });\n\n/** Every member `meta.event` must carry for a message to be routable at all. */\nconst REQUIRED_EVENT_META = ['name', 'id', 'source', 'at'] as const;\n\n/**\n * Reads a bus message back into a {@link DomainEvent}.\n *\n * The metadata is validated rather than defaulted, and that is the point: a\n * message missing `id` cannot be deduplicated and a message missing `name`\n * cannot be routed, so both are failures a consumer must see rather than\n * absences it silently works around. It is the same call\n * {@link readEntityMetadataEnvelope} makes — a broken payload fails loudly\n * instead of arriving half-populated.\n *\n * Note this does not validate `data`. The transport has no idea what a\n * `catalog.published` payload should look like, and inventing an opinion here\n * is how the bus would start knowing about domains.\n */\nexport function readEventEnvelope<TData = unknown>(\n body: unknown,\n): Effect.Effect<DomainEvent<TData>, EntifixBuildError> {\n return Effect.gen(function* () {\n const envelope = yield* readEnvelope<TData>(body, 'event', 'event');\n const event = envelope.meta.event;\n const missing = REQUIRED_EVENT_META.filter(\n member => typeof event?.[member] !== 'string' || event[member] === '',\n );\n if (missing.length > 0) {\n return yield* Effect.fail(\n new EntifixBuildError(\n `event envelope carried incomplete meta.event (missing ${missing.join(\n ', ',\n )})`,\n undefined,\n { missing, body },\n ),\n );\n }\n return { ...(event as EntifixEventMeta), data: envelope.data };\n });\n}\n"],"names":["Effect","EntifixBuildError","deserializeEntityCollection","deserializeSingleEntity","envelopeEntityName","isEntifixEnvelope","body","meta","type","assertEnvelope","entityConstructor","expected","entity","fail","undefined","actual","succeed","readEnvelope","label","readEntityEnvelope","gen","envelope","data","readEntityCollectionEnvelope","items","readEntityPageEnvelope","total","request","readEntityMetadataEnvelope","document","Array","isArray","actions","useCases","isFacetValueRow","row","includes","value","count","readEntityFacetsEnvelope","counts","wellFormed","Object","values","every","rows","REQUIRED_EVENT_META","readEventEnvelope","event","missing","filter","member","length","join"],"mappings":"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"}
|
package/dist/envelope/types.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { EntityMetadataDocument } from '../entity-definition/metadata/index.js';
|
|
2
2
|
import type { SerializedEntity } from '../entity-definition/serializer/index.js';
|
|
3
3
|
import type { Entity, EntityId } from '../types/Entity.js';
|
|
4
|
+
import type { EntityFacetCounts } from '../types/EntityFacets.js';
|
|
4
5
|
import type { EntityLoadRequest } from '../types/EntityLoadRequest.js';
|
|
5
6
|
/**
|
|
6
7
|
* Discriminates what {@link EntifixEnvelope.data} carries.
|
|
@@ -35,7 +36,7 @@ import type { EntityLoadRequest } from '../types/EntityLoadRequest.js';
|
|
|
35
36
|
* both the service that computes it and the controls that render it are already
|
|
36
37
|
* below the layer that would otherwise host it.
|
|
37
38
|
*/
|
|
38
|
-
export type EntifixEnvelopeType = 'entity' | 'entityCollection' | 'entityPage' | 'command' | 'event' | 'transactionAccepted' | 'transactionRecord' | 'sagaResult' | 'sagaInstance' | 'entityMetadata';
|
|
39
|
+
export type EntifixEnvelopeType = 'entity' | 'entityCollection' | 'entityPage' | 'command' | 'event' | 'transactionAccepted' | 'transactionRecord' | 'sagaResult' | 'sagaInstance' | 'entityMetadata' | 'entityFacets';
|
|
39
40
|
export type EntifixEnvelopeMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
40
41
|
/**
|
|
41
42
|
* A HATEOAS affordance: where the peer can go next from this message. Optional —
|
|
@@ -159,6 +160,7 @@ export type EntityEnvelope = EntifixEnvelope<SerializedEntity>;
|
|
|
159
160
|
export type EntityCollectionEnvelope = EntifixEnvelope<SerializedEntity[]>;
|
|
160
161
|
export type EntityPageEnvelope<TEntity extends Entity = Entity> = EntifixEnvelope<SerializedEntityPage<TEntity>>;
|
|
161
162
|
export type EntityMetadataEnvelope = EntifixEnvelope<EntityMetadataDocument>;
|
|
163
|
+
export type EntityFacetsEnvelope = EntifixEnvelope<EntityFacetCounts>;
|
|
162
164
|
/**
|
|
163
165
|
* A bus message on the wire. `meta.event` is always populated, and
|
|
164
166
|
* `readEventEnvelope` is what proves it before a consumer sees the payload.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/envelope/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AACrF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AACjF,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAEvE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,MAAM,mBAAmB,GAC3B,QAAQ,GACR,kBAAkB,GAClB,YAAY,GACZ,SAAS,GACT,OAAO,GACP,qBAAqB,GACrB,mBAAmB,GACnB,YAAY,GACZ,cAAc,GACd,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/envelope/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/envelope/types.ts"],"sourcesContent":["import type { EntityMetadataDocument } from '../entity-definition/metadata/index.js';\nimport type { SerializedEntity } from '../entity-definition/serializer/index.js';\nimport type { Entity, EntityId } from '../types/Entity.js';\nimport type { EntityLoadRequest } from '../types/EntityLoadRequest.js';\n\n/**\n * Discriminates what {@link EntifixEnvelope.data} carries.\n *\n * `command` extends the contract for the transactions layer: a write is issued\n * as a `command`, whose `data` shape lives in `@entifix/transactions` —\n * core only owns the discriminant so every artifact agrees on it.\n *\n * `event` is what actually rides the bus, and it is deliberately not named\n * after one publisher's flow: once a message can be `catalog.published` as\n * easily as `transaction.completed`, `transactionEvent` would be the wrong\n * name for it.\n *\n * ⚠️ **The four transaction-shaped discriminants below are four because they\n * carry four shapes.** `transactionEvent` used to mean all of them, and a\n * discriminant that means several things asserts nothing when it is read —\n * which is what `readTransactionEventEnvelope` was doing on the `202`: checking\n * a name that the record route and the event type both also answered to.\n *\n * - `transactionAccepted` — the `202` accept body, `{ transactionId, state }`.\n * Not a record: it has no `createdAt` and names no entity id yet.\n * - `transactionRecord` — the fold a client polls, a `TransactionRecord`.\n * - `sagaResult` — how a settled multi-step walk ended: which steps ran and\n * what each returned.\n * - `sagaInstance` — the durable instance itself, which is what a resumable\n * coordinator reads and what `GET /api/saga/:id` serves. It differs from\n * `sagaResult` in carrying the inputs and the resume count, because those are\n * what an operator needs to answer *why is this one still here*.\n *\n * `entityMetadata` is the same extension made once more, for the action model:\n * its `data` is an {@link EntityMetadataDocument}, which core does own because\n * both the service that computes it and the controls that render it are already\n * below the layer that would otherwise host it.\n */\nexport type EntifixEnvelopeType =\n | 'entity'\n | 'entityCollection'\n | 'entityPage'\n | 'command'\n | 'event'\n | 'transactionAccepted'\n | 'transactionRecord'\n | 'sagaResult'\n | 'sagaInstance'\n | 'entityMetadata';\n\nexport type EntifixEnvelopeMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';\n\n/**\n * A HATEOAS affordance: where the peer can go next from this message. Optional —\n * the envelope stands on its own without links, and only the artifact that owns\n * a route surface (today the REST service) is able to fill them in.\n */\nexport interface EntifixEnvelopeLink {\n rel: string;\n href: string;\n method?: EntifixEnvelopeMethod;\n}\n\n/**\n * The facts a message carries about *itself*, as opposed to about what\n * happened. Present only when the envelope is a bus message.\n *\n * The split rule: `meta` describes the message, `data` describes the\n * occurrence. So `correlationId` belongs here and an outcome's `code` does not.\n *\n * **`source` is never a consumer branch.** It is there for routing,\n * observability and audit. A handler that behaves differently depending on who\n * published re-couples the two services the bus decoupled — which is why .NET's\n * canonical `(sender, eventArgs)` types its sender as bare `object`, making the\n * dependency awkward on purpose. TypeScript cannot reproduce that friction, so\n * here it is a rule rather than a type.\n */\nexport interface EntifixEventMeta {\n /**\n * What happened, in the register's vocabulary: `catalog.published`,\n * `transaction.completed`. Declared in `tools/slices/*.slice.ts` as\n * `publishedEvents`/`subscriptions`, and used verbatim as the AMQP routing\n * key — which is what makes a subscriber's declared interest and its actual\n * queue binding the same string.\n */\n name: string;\n /**\n * Unique per message, and **the** deduplication key. Delivery is\n * at-least-once, so a consumer that must not fold twice keys on this.\n *\n * Not `correlationId`: one flow emits several messages, so correlating and\n * deduplicating are different questions. Keying dedup on the correlation id\n * makes a transaction's `completed` look like a duplicate of its `accepted`.\n */\n id: string;\n /** The emitting **slice** — the unit that owns a store — e.g. `catalog`. */\n source: string;\n /** ISO-8601 emission time. */\n at: string;\n /** Ties every message of one flow together — a transaction id, a saga id. */\n correlationId?: string;\n}\n\nexport interface EntifixEnvelopeMeta {\n type: EntifixEnvelopeType;\n /**\n * The target entity's `key` (falling back to its class name).\n *\n * Optional, because a bus message need not be about an entity at all —\n * `settlement.run.completed` is about a run. What a message *is* lives in\n * {@link EntifixEventMeta.name}; this stays the entity label the HTTP arm\n * routes on.\n */\n entity?: string;\n links?: EntifixEnvelopeLink[];\n /** Present only on a bus message. */\n event?: EntifixEventMeta;\n}\n\n/**\n * One message on the bus: its own metadata, plus the payload describing what\n * happened.\n *\n * Generic in the payload so a publisher keeps its own shape —\n * `DomainEvent<TransactionEvent>` today, `DomainEvent<PublishedOffering>` when\n * a projection's event lands — without the transport learning either.\n */\nexport interface DomainEvent<TData = unknown> extends EntifixEventMeta {\n data: TData;\n}\n\n/**\n * A change to an entity that happened on the server, delivered out-of-band —\n * the `data` of a bus/stream `event` envelope rather than a framing of its own\n *. `entity` is the `key ?? name` an adapter routes on, the same\n * string `entityQueryScope` uses, so a subscriber can invalidate exactly the\n * affected query keys.\n *\n * It lives in core rather than beside the React `ReactiveChannel` that consumes\n * it because both ends need it: the service maps a `TransactionEvent` onto it,\n * and the React packages sit above every tier that does the mapping.\n *\n * Deliberately **no** `transactionId`, timestamp or sequence: all three already\n * exist one level up as `meta.event.correlationId`, `.at` and `.id`, and\n * duplicating them onto the payload is how two sources of the same fact drift.\n */\nexport interface EntityChangeEvent {\n entity: string;\n change: 'created' | 'updated' | 'deleted';\n id: EntityId;\n}\n\n/**\n * The standard message exchanged between entifix artifacts.\n *\n * Every request and response body is an envelope: `meta` describes the payload,\n * `data` is the payload. It is deliberately transport-free — it lives in `core`\n * and knows nothing about HTTP — so the same contract can carry entities over\n * REST today and amqp or websockets later.\n */\nexport interface EntifixEnvelope<TData = unknown> {\n meta: EntifixEnvelopeMeta;\n data: TData;\n}\n\n/**\n * `data` shape when `meta.type` is `entityPage`. Generic in the entity so the\n * echoed `request` keeps its `keyof TEntity` filter/sort types — `EntityLoadRequest`\n * is invariant in its entity, so a non-generic `EntityLoadRequest<Entity>` here\n * would not accept a caller's `EntityLoadRequest<Product>`.\n */\nexport interface SerializedEntityPage<TEntity extends Entity = Entity> {\n items: SerializedEntity[];\n total: number;\n request?: EntityLoadRequest<TEntity>;\n}\n\nexport type EntityEnvelope = EntifixEnvelope<SerializedEntity>;\nexport type EntityCollectionEnvelope = EntifixEnvelope<SerializedEntity[]>;\nexport type EntityPageEnvelope<TEntity extends Entity = Entity> =\n EntifixEnvelope<SerializedEntityPage<TEntity>>;\nexport type EntityMetadataEnvelope = EntifixEnvelope<EntityMetadataDocument>;\n\n/**\n * A bus message on the wire. `meta.event` is always populated, and\n * `readEventEnvelope` is what proves it before a consumer sees the payload.\n */\nexport type EventEnvelope<TData = unknown> = EntifixEnvelope<TData> & {\n meta: EntifixEnvelopeMeta & { event: EntifixEventMeta };\n};\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../src/envelope/types.ts"],"sourcesContent":["import type { EntityMetadataDocument } from '../entity-definition/metadata/index.js';\nimport type { SerializedEntity } from '../entity-definition/serializer/index.js';\nimport type { Entity, EntityId } from '../types/Entity.js';\nimport type { EntityFacetCounts } from '../types/EntityFacets.js';\nimport type { EntityLoadRequest } from '../types/EntityLoadRequest.js';\n\n/**\n * Discriminates what {@link EntifixEnvelope.data} carries.\n *\n * `command` extends the contract for the transactions layer: a write is issued\n * as a `command`, whose `data` shape lives in `@entifix/transactions` —\n * core only owns the discriminant so every artifact agrees on it.\n *\n * `event` is what actually rides the bus, and it is deliberately not named\n * after one publisher's flow: once a message can be `catalog.published` as\n * easily as `transaction.completed`, `transactionEvent` would be the wrong\n * name for it.\n *\n * ⚠️ **The four transaction-shaped discriminants below are four because they\n * carry four shapes.** `transactionEvent` used to mean all of them, and a\n * discriminant that means several things asserts nothing when it is read —\n * which is what `readTransactionEventEnvelope` was doing on the `202`: checking\n * a name that the record route and the event type both also answered to.\n *\n * - `transactionAccepted` — the `202` accept body, `{ transactionId, state }`.\n * Not a record: it has no `createdAt` and names no entity id yet.\n * - `transactionRecord` — the fold a client polls, a `TransactionRecord`.\n * - `sagaResult` — how a settled multi-step walk ended: which steps ran and\n * what each returned.\n * - `sagaInstance` — the durable instance itself, which is what a resumable\n * coordinator reads and what `GET /api/saga/:id` serves. It differs from\n * `sagaResult` in carrying the inputs and the resume count, because those are\n * what an operator needs to answer *why is this one still here*.\n *\n * `entityMetadata` is the same extension made once more, for the action model:\n * its `data` is an {@link EntityMetadataDocument}, which core does own because\n * both the service that computes it and the controls that render it are already\n * below the layer that would otherwise host it.\n */\nexport type EntifixEnvelopeType =\n | 'entity'\n | 'entityCollection'\n | 'entityPage'\n | 'command'\n | 'event'\n | 'transactionAccepted'\n | 'transactionRecord'\n | 'sagaResult'\n | 'sagaInstance'\n | 'entityMetadata'\n | 'entityFacets';\n\nexport type EntifixEnvelopeMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';\n\n/**\n * A HATEOAS affordance: where the peer can go next from this message. Optional —\n * the envelope stands on its own without links, and only the artifact that owns\n * a route surface (today the REST service) is able to fill them in.\n */\nexport interface EntifixEnvelopeLink {\n rel: string;\n href: string;\n method?: EntifixEnvelopeMethod;\n}\n\n/**\n * The facts a message carries about *itself*, as opposed to about what\n * happened. Present only when the envelope is a bus message.\n *\n * The split rule: `meta` describes the message, `data` describes the\n * occurrence. So `correlationId` belongs here and an outcome's `code` does not.\n *\n * **`source` is never a consumer branch.** It is there for routing,\n * observability and audit. A handler that behaves differently depending on who\n * published re-couples the two services the bus decoupled — which is why .NET's\n * canonical `(sender, eventArgs)` types its sender as bare `object`, making the\n * dependency awkward on purpose. TypeScript cannot reproduce that friction, so\n * here it is a rule rather than a type.\n */\nexport interface EntifixEventMeta {\n /**\n * What happened, in the register's vocabulary: `catalog.published`,\n * `transaction.completed`. Declared in `tools/slices/*.slice.ts` as\n * `publishedEvents`/`subscriptions`, and used verbatim as the AMQP routing\n * key — which is what makes a subscriber's declared interest and its actual\n * queue binding the same string.\n */\n name: string;\n /**\n * Unique per message, and **the** deduplication key. Delivery is\n * at-least-once, so a consumer that must not fold twice keys on this.\n *\n * Not `correlationId`: one flow emits several messages, so correlating and\n * deduplicating are different questions. Keying dedup on the correlation id\n * makes a transaction's `completed` look like a duplicate of its `accepted`.\n */\n id: string;\n /** The emitting **slice** — the unit that owns a store — e.g. `catalog`. */\n source: string;\n /** ISO-8601 emission time. */\n at: string;\n /** Ties every message of one flow together — a transaction id, a saga id. */\n correlationId?: string;\n}\n\nexport interface EntifixEnvelopeMeta {\n type: EntifixEnvelopeType;\n /**\n * The target entity's `key` (falling back to its class name).\n *\n * Optional, because a bus message need not be about an entity at all —\n * `settlement.run.completed` is about a run. What a message *is* lives in\n * {@link EntifixEventMeta.name}; this stays the entity label the HTTP arm\n * routes on.\n */\n entity?: string;\n links?: EntifixEnvelopeLink[];\n /** Present only on a bus message. */\n event?: EntifixEventMeta;\n}\n\n/**\n * One message on the bus: its own metadata, plus the payload describing what\n * happened.\n *\n * Generic in the payload so a publisher keeps its own shape —\n * `DomainEvent<TransactionEvent>` today, `DomainEvent<PublishedOffering>` when\n * a projection's event lands — without the transport learning either.\n */\nexport interface DomainEvent<TData = unknown> extends EntifixEventMeta {\n data: TData;\n}\n\n/**\n * A change to an entity that happened on the server, delivered out-of-band —\n * the `data` of a bus/stream `event` envelope rather than a framing of its own\n *. `entity` is the `key ?? name` an adapter routes on, the same\n * string `entityQueryScope` uses, so a subscriber can invalidate exactly the\n * affected query keys.\n *\n * It lives in core rather than beside the React `ReactiveChannel` that consumes\n * it because both ends need it: the service maps a `TransactionEvent` onto it,\n * and the React packages sit above every tier that does the mapping.\n *\n * Deliberately **no** `transactionId`, timestamp or sequence: all three already\n * exist one level up as `meta.event.correlationId`, `.at` and `.id`, and\n * duplicating them onto the payload is how two sources of the same fact drift.\n */\nexport interface EntityChangeEvent {\n entity: string;\n change: 'created' | 'updated' | 'deleted';\n id: EntityId;\n}\n\n/**\n * The standard message exchanged between entifix artifacts.\n *\n * Every request and response body is an envelope: `meta` describes the payload,\n * `data` is the payload. It is deliberately transport-free — it lives in `core`\n * and knows nothing about HTTP — so the same contract can carry entities over\n * REST today and amqp or websockets later.\n */\nexport interface EntifixEnvelope<TData = unknown> {\n meta: EntifixEnvelopeMeta;\n data: TData;\n}\n\n/**\n * `data` shape when `meta.type` is `entityPage`. Generic in the entity so the\n * echoed `request` keeps its `keyof TEntity` filter/sort types — `EntityLoadRequest`\n * is invariant in its entity, so a non-generic `EntityLoadRequest<Entity>` here\n * would not accept a caller's `EntityLoadRequest<Product>`.\n */\nexport interface SerializedEntityPage<TEntity extends Entity = Entity> {\n items: SerializedEntity[];\n total: number;\n request?: EntityLoadRequest<TEntity>;\n}\n\nexport type EntityEnvelope = EntifixEnvelope<SerializedEntity>;\nexport type EntityCollectionEnvelope = EntifixEnvelope<SerializedEntity[]>;\nexport type EntityPageEnvelope<TEntity extends Entity = Entity> =\n EntifixEnvelope<SerializedEntityPage<TEntity>>;\nexport type EntityMetadataEnvelope = EntifixEnvelope<EntityMetadataDocument>;\nexport type EntityFacetsEnvelope = EntifixEnvelope<EntityFacetCounts>;\n\n/**\n * A bus message on the wire. `meta.event` is always populated, and\n * `readEventEnvelope` is what proves it before a consumer sees the payload.\n */\nexport type EventEnvelope<TData = unknown> = EntifixEnvelope<TData> & {\n meta: EntifixEnvelopeMeta & { event: EntifixEventMeta };\n};\n"],"names":[],"mappings":"AA0LA;;;CAGC,GACD,WAEE"}
|
package/dist/index.d.ts
CHANGED
|
@@ -28,12 +28,14 @@ export * from './i18n/format.js';
|
|
|
28
28
|
export * from './i18n/locales.js';
|
|
29
29
|
export * from './i18n/localized-text.js';
|
|
30
30
|
export * from './i18n/negotiate.js';
|
|
31
|
+
export * from './query/fold.js';
|
|
31
32
|
export * from './query/in-memory-query.js';
|
|
32
33
|
export * from './rsql/index.js';
|
|
33
34
|
export * from './types/BulkOutcome.js';
|
|
34
35
|
export * from './types/Entity.js';
|
|
35
36
|
export * from './types/EntityAction.js';
|
|
36
37
|
export * from './types/EntityDraft.js';
|
|
38
|
+
export * from './types/EntityFacets.js';
|
|
37
39
|
export * from './types/EntityFiltering.js';
|
|
38
40
|
export * from './types/EntityLoadRequest.js';
|
|
39
41
|
export * from './types/EntityPage.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,+CAA+C,CAAC;AAC9D,cAAc,wCAAwC,CAAC;AACvD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,kDAAkD,CAAC;AACjE,cAAc,gDAAgD,CAAC;AAC/D,cAAc,gDAAgD,CAAC;AAC/D,cAAc,kDAAkD,CAAC;AACjE,cAAc,uCAAuC,CAAC;AACtD,cAAc,mCAAmC,CAAC;AAClD,cAAc,sCAAsC,CAAC;AACrD,cAAc,uDAAuD,CAAC;AACtE,cAAc,2DAA2D,CAAC;AAC1E,cAAc,gDAAgD,CAAC;AAC/D,cAAc,uDAAuD,CAAC;AACtE,cAAc,iDAAiD,CAAC;AAChE,cAAc,0DAA0D,CAAC;AACzE,cAAc,wDAAwD,CAAC;AACvE,cAAc,wDAAwD,CAAC;AACvE,cAAc,0DAA0D,CAAC;AACzE,cAAc,uCAAuC,CAAC;AACtD,cAAc,0CAA0C,CAAC;AACzD,cAAc,yCAAyC,CAAC;AACxD,cAAc,uCAAuC,CAAC;AACtD,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC;AACpC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,cAAc,yBAAyB,CAAC;AACxC,cAAc,wBAAwB,CAAC;AACvC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,uBAAuB,CAAC;AACtC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,0BAA0B,CAAC;AACzC,cAAc,iBAAiB,CAAC;AAChC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,+CAA+C,CAAC;AAC9D,cAAc,wCAAwC,CAAC;AACvD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,kDAAkD,CAAC;AACjE,cAAc,gDAAgD,CAAC;AAC/D,cAAc,gDAAgD,CAAC;AAC/D,cAAc,kDAAkD,CAAC;AACjE,cAAc,uCAAuC,CAAC;AACtD,cAAc,mCAAmC,CAAC;AAClD,cAAc,sCAAsC,CAAC;AACrD,cAAc,uDAAuD,CAAC;AACtE,cAAc,2DAA2D,CAAC;AAC1E,cAAc,gDAAgD,CAAC;AAC/D,cAAc,uDAAuD,CAAC;AACtE,cAAc,iDAAiD,CAAC;AAChE,cAAc,0DAA0D,CAAC;AACzE,cAAc,wDAAwD,CAAC;AACvE,cAAc,wDAAwD,CAAC;AACvE,cAAc,0DAA0D,CAAC;AACzE,cAAc,uCAAuC,CAAC;AACtD,cAAc,0CAA0C,CAAC;AACzD,cAAc,yCAAyC,CAAC;AACxD,cAAc,uCAAuC,CAAC;AACtD,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,cAAc,yBAAyB,CAAC;AACxC,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,uBAAuB,CAAC;AACtC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,0BAA0B,CAAC;AACzC,cAAc,iBAAiB,CAAC;AAChC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -28,12 +28,14 @@ export * from './i18n/format.js';
|
|
|
28
28
|
export * from './i18n/locales.js';
|
|
29
29
|
export * from './i18n/localized-text.js';
|
|
30
30
|
export * from './i18n/negotiate.js';
|
|
31
|
+
export * from './query/fold.js';
|
|
31
32
|
export * from './query/in-memory-query.js';
|
|
32
33
|
export * from './rsql/index.js';
|
|
33
34
|
export * from './types/BulkOutcome.js';
|
|
34
35
|
export * from './types/Entity.js';
|
|
35
36
|
export * from './types/EntityAction.js';
|
|
36
37
|
export * from './types/EntityDraft.js';
|
|
38
|
+
export * from './types/EntityFacets.js';
|
|
37
39
|
export * from './types/EntityFiltering.js';
|
|
38
40
|
export * from './types/EntityLoadRequest.js';
|
|
39
41
|
export * from './types/EntityPage.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from './base-entities/configuration-client/index.js';\nexport * from './base-entities/entifix-error/index.js';\nexport * from './command-palette/index.js';\nexport * from './entity-definition/decorators/accessor/index.js';\nexport * from './entity-definition/decorators/entity/index.js';\nexport * from './entity-definition/decorators/method/index.js';\nexport * from './entity-definition/decorators/use-case/index.js';\nexport * from './entity-definition/describe/index.js';\nexport * from './entity-definition/field-path.js';\nexport * from './entity-definition/helpers/index.js';\nexport * from './entity-definition/links/apply-entity-links/index.js';\nexport * from './entity-definition/links/entity-collection-link/index.js';\nexport * from './entity-definition/links/entity-link/index.js';\nexport * from './entity-definition/links/entity-link-source/index.js';\nexport * from './entity-definition/links/link-targets/index.js';\nexport * from './entity-definition/meta-entities/meta-accessor/index.js';\nexport * from './entity-definition/meta-entities/meta-entity/index.js';\nexport * from './entity-definition/meta-entities/meta-method/index.js';\nexport * from './entity-definition/meta-entities/meta-use-case/index.js';\nexport * from './entity-definition/metadata/index.js';\nexport * from './entity-definition/reconstruct/index.js';\nexport * from './entity-definition/serializer/index.js';\nexport * from './entity-definition/validate/index.js';\nexport * from './envelope/index.js';\nexport * from './hierarchy/index.js';\nexport * from './http/session-cookies.js';\nexport * from './i18n/format.js';\nexport * from './i18n/locales.js';\nexport * from './i18n/localized-text.js';\nexport * from './i18n/negotiate.js';\nexport * from './query/in-memory-query.js';\nexport * from './rsql/index.js';\nexport * from './types/BulkOutcome.js';\nexport * from './types/Entity.js';\nexport * from './types/EntityAction.js';\nexport * from './types/EntityDraft.js';\nexport * from './types/EntityFiltering.js';\nexport * from './types/EntityLoadRequest.js';\nexport * from './types/EntityPage.js';\nexport * from './types/EntityRowDraft.js';\nexport * from './types/EntitySelection.js';\nexport * from './types/EntitySorting.js';\nexport * from './types/Json.js';\nexport * from './types/StandardSchema.js';\nexport * from './types/UseCase.js';\nexport * from './wizard/index.js';\n"],"names":[],"mappings":"AAAA,cAAc,gDAAgD;AAC9D,cAAc,yCAAyC;AACvD,cAAc,6BAA6B;AAC3C,cAAc,mDAAmD;AACjE,cAAc,iDAAiD;AAC/D,cAAc,iDAAiD;AAC/D,cAAc,mDAAmD;AACjE,cAAc,wCAAwC;AACtD,cAAc,oCAAoC;AAClD,cAAc,uCAAuC;AACrD,cAAc,wDAAwD;AACtE,cAAc,4DAA4D;AAC1E,cAAc,iDAAiD;AAC/D,cAAc,wDAAwD;AACtE,cAAc,kDAAkD;AAChE,cAAc,2DAA2D;AACzE,cAAc,yDAAyD;AACvE,cAAc,yDAAyD;AACvE,cAAc,2DAA2D;AACzE,cAAc,wCAAwC;AACtD,cAAc,2CAA2C;AACzD,cAAc,0CAA0C;AACxD,cAAc,wCAAwC;AACtD,cAAc,sBAAsB;AACpC,cAAc,uBAAuB;AACrC,cAAc,4BAA4B;AAC1C,cAAc,mBAAmB;AACjC,cAAc,oBAAoB;AAClC,cAAc,2BAA2B;AACzC,cAAc,sBAAsB;AACpC,cAAc,6BAA6B;AAC3C,cAAc,kBAAkB;AAChC,cAAc,yBAAyB;AACvC,cAAc,oBAAoB;AAClC,cAAc,0BAA0B;AACxC,cAAc,yBAAyB;AACvC,cAAc,6BAA6B;AAC3C,cAAc,+BAA+B;AAC7C,cAAc,wBAAwB;AACtC,cAAc,4BAA4B;AAC1C,cAAc,6BAA6B;AAC3C,cAAc,2BAA2B;AACzC,cAAc,kBAAkB;AAChC,cAAc,4BAA4B;AAC1C,cAAc,qBAAqB;AACnC,cAAc,oBAAoB"}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from './base-entities/configuration-client/index.js';\nexport * from './base-entities/entifix-error/index.js';\nexport * from './command-palette/index.js';\nexport * from './entity-definition/decorators/accessor/index.js';\nexport * from './entity-definition/decorators/entity/index.js';\nexport * from './entity-definition/decorators/method/index.js';\nexport * from './entity-definition/decorators/use-case/index.js';\nexport * from './entity-definition/describe/index.js';\nexport * from './entity-definition/field-path.js';\nexport * from './entity-definition/helpers/index.js';\nexport * from './entity-definition/links/apply-entity-links/index.js';\nexport * from './entity-definition/links/entity-collection-link/index.js';\nexport * from './entity-definition/links/entity-link/index.js';\nexport * from './entity-definition/links/entity-link-source/index.js';\nexport * from './entity-definition/links/link-targets/index.js';\nexport * from './entity-definition/meta-entities/meta-accessor/index.js';\nexport * from './entity-definition/meta-entities/meta-entity/index.js';\nexport * from './entity-definition/meta-entities/meta-method/index.js';\nexport * from './entity-definition/meta-entities/meta-use-case/index.js';\nexport * from './entity-definition/metadata/index.js';\nexport * from './entity-definition/reconstruct/index.js';\nexport * from './entity-definition/serializer/index.js';\nexport * from './entity-definition/validate/index.js';\nexport * from './envelope/index.js';\nexport * from './hierarchy/index.js';\nexport * from './http/session-cookies.js';\nexport * from './i18n/format.js';\nexport * from './i18n/locales.js';\nexport * from './i18n/localized-text.js';\nexport * from './i18n/negotiate.js';\nexport * from './query/fold.js';\nexport * from './query/in-memory-query.js';\nexport * from './rsql/index.js';\nexport * from './types/BulkOutcome.js';\nexport * from './types/Entity.js';\nexport * from './types/EntityAction.js';\nexport * from './types/EntityDraft.js';\nexport * from './types/EntityFacets.js';\nexport * from './types/EntityFiltering.js';\nexport * from './types/EntityLoadRequest.js';\nexport * from './types/EntityPage.js';\nexport * from './types/EntityRowDraft.js';\nexport * from './types/EntitySelection.js';\nexport * from './types/EntitySorting.js';\nexport * from './types/Json.js';\nexport * from './types/StandardSchema.js';\nexport * from './types/UseCase.js';\nexport * from './wizard/index.js';\n"],"names":[],"mappings":"AAAA,cAAc,gDAAgD;AAC9D,cAAc,yCAAyC;AACvD,cAAc,6BAA6B;AAC3C,cAAc,mDAAmD;AACjE,cAAc,iDAAiD;AAC/D,cAAc,iDAAiD;AAC/D,cAAc,mDAAmD;AACjE,cAAc,wCAAwC;AACtD,cAAc,oCAAoC;AAClD,cAAc,uCAAuC;AACrD,cAAc,wDAAwD;AACtE,cAAc,4DAA4D;AAC1E,cAAc,iDAAiD;AAC/D,cAAc,wDAAwD;AACtE,cAAc,kDAAkD;AAChE,cAAc,2DAA2D;AACzE,cAAc,yDAAyD;AACvE,cAAc,yDAAyD;AACvE,cAAc,2DAA2D;AACzE,cAAc,wCAAwC;AACtD,cAAc,2CAA2C;AACzD,cAAc,0CAA0C;AACxD,cAAc,wCAAwC;AACtD,cAAc,sBAAsB;AACpC,cAAc,uBAAuB;AACrC,cAAc,4BAA4B;AAC1C,cAAc,mBAAmB;AACjC,cAAc,oBAAoB;AAClC,cAAc,2BAA2B;AACzC,cAAc,sBAAsB;AACpC,cAAc,kBAAkB;AAChC,cAAc,6BAA6B;AAC3C,cAAc,kBAAkB;AAChC,cAAc,yBAAyB;AACvC,cAAc,oBAAoB;AAClC,cAAc,0BAA0B;AACxC,cAAc,yBAAyB;AACvC,cAAc,0BAA0B;AACxC,cAAc,6BAA6B;AAC3C,cAAc,+BAA+B;AAC7C,cAAc,wBAAwB;AACtC,cAAc,4BAA4B;AAC1C,cAAc,6BAA6B;AAC3C,cAAc,2BAA2B;AACzC,cAAc,kBAAkB;AAChC,cAAc,4BAA4B;AAC1C,cAAc,qBAAqB;AACnC,cAAc,oBAAoB"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What `like` compares: text with its accents and its case taken off.
|
|
3
|
+
*
|
|
4
|
+
* One rule, stated once, because three engines answer `like` and each needs it
|
|
5
|
+
* in a different form — the in-memory matcher folds both sides, the Mongo
|
|
6
|
+
* adapter cannot fold a stored value and so widens the query instead, and the
|
|
7
|
+
* SQL adapter folds the column with `translate`. Every form is derived from
|
|
8
|
+
* {@link foldText} here, so none of them can drift into a different rule.
|
|
9
|
+
*
|
|
10
|
+
* The rule is Unicode's: decompose (NFD), drop the combining marks, lowercase.
|
|
11
|
+
* `é` is `e`, `ü` is `u`, and `ñ` is `n` — a buyer on a keyboard without the
|
|
12
|
+
* letter types `nino` for `niño`, and finding nothing is the worse answer. A
|
|
13
|
+
* letter that does not decompose (`ß`, `ø`, `æ`, `ł`) is left as it is, by
|
|
14
|
+
* every engine alike.
|
|
15
|
+
*/
|
|
16
|
+
/** `value` with its accents and case removed. */
|
|
17
|
+
export declare const foldText: (value: string) => string;
|
|
18
|
+
/**
|
|
19
|
+
* The precomposed letters each base letter stands for, both cases and the base
|
|
20
|
+
* itself included: `e` → `eEéÉèÈêÊëË…`.
|
|
21
|
+
*
|
|
22
|
+
* Built from Latin-1 Supplement and Latin Extended-A by {@link foldText}
|
|
23
|
+
* itself, rather than typed out, so a letter folds in the table exactly when
|
|
24
|
+
* it folds in the rule. Both cases are listed explicitly so a pattern built
|
|
25
|
+
* from it never depends on how a regex engine case-folds outside ASCII.
|
|
26
|
+
*/
|
|
27
|
+
export declare const FOLD_TABLE: Readonly<Record<string, string>>;
|
|
28
|
+
/** Escapes a string so it is matched literally inside a regular expression. */
|
|
29
|
+
export declare const escapeRegex: (value: string) => string;
|
|
30
|
+
/**
|
|
31
|
+
* A regular expression source that matches `value` as a substring the way
|
|
32
|
+
* `like` does, for an engine that cannot fold the text it searches: the value
|
|
33
|
+
* is folded, and each letter that has accented forms becomes a class of all of
|
|
34
|
+
* them, so `cafe` matches `Café` and `CAFÉ`. Everything else is escaped, so a
|
|
35
|
+
* `.` or a `(` in a search is matched literally.
|
|
36
|
+
*/
|
|
37
|
+
export declare const foldedPattern: (value: string) => string;
|
|
38
|
+
/** Whether `actual` contains `value`, both folded. */
|
|
39
|
+
export declare const containsFolded: (actual: string, value: string) => boolean;
|
|
40
|
+
//# sourceMappingURL=fold.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fold.d.ts","sourceRoot":"","sources":["../../src/query/fold.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,iDAAiD;AACjD,eAAO,MAAM,QAAQ,GAAI,OAAO,MAAM,KAAG,MACoB,CAAC;AAE9D;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAUpD,CAAC;AAEL,+EAA+E;AAC/E,eAAO,MAAM,WAAW,GAAI,OAAO,MAAM,KAAG,MACE,CAAC;AAE/C;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,GAAI,OAAO,MAAM,KAAG,MAMjC,CAAC;AAEd,sDAAsD;AACtD,eAAO,MAAM,cAAc,GAAI,QAAQ,MAAM,EAAE,OAAO,MAAM,KAAG,OACnB,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What `like` compares: text with its accents and its case taken off.
|
|
3
|
+
*
|
|
4
|
+
* One rule, stated once, because three engines answer `like` and each needs it
|
|
5
|
+
* in a different form — the in-memory matcher folds both sides, the Mongo
|
|
6
|
+
* adapter cannot fold a stored value and so widens the query instead, and the
|
|
7
|
+
* SQL adapter folds the column with `translate`. Every form is derived from
|
|
8
|
+
* {@link foldText} here, so none of them can drift into a different rule.
|
|
9
|
+
*
|
|
10
|
+
* The rule is Unicode's: decompose (NFD), drop the combining marks, lowercase.
|
|
11
|
+
* `é` is `e`, `ü` is `u`, and `ñ` is `n` — a buyer on a keyboard without the
|
|
12
|
+
* letter types `nino` for `niño`, and finding nothing is the worse answer. A
|
|
13
|
+
* letter that does not decompose (`ß`, `ø`, `æ`, `ł`) is left as it is, by
|
|
14
|
+
* every engine alike.
|
|
15
|
+
*/ /** `value` with its accents and case removed. */ export const foldText = (value)=>value.normalize('NFD').replace(RegExp("[\\u0300-\\u036F\\u0483-\\u0489\\u0591-\\u05BD\\u05BF\\u05C1-\\u05C2\\u05C4-\\u05C5\\u05C7\\u0610-\\u061A\\u064B-\\u065F\\u0670\\u06D6-\\u06DC\\u06DF-\\u06E4\\u06E7-\\u06E8\\u06EA-\\u06ED\\u0711\\u0730-\\u074A\\u07A6-\\u07B0\\u07EB-\\u07F3\\u07FD\\u0816-\\u0819\\u081B-\\u0823\\u0825-\\u0827\\u0829-\\u082D\\u0859-\\u085B\\u0897-\\u089F\\u08CA-\\u08E1\\u08E3-\\u0903\\u093A-\\u093C\\u093E-\\u094F\\u0951-\\u0957\\u0962-\\u0963\\u0981-\\u0983\\u09BC\\u09BE-\\u09C4\\u09C7-\\u09C8\\u09CB-\\u09CD\\u09D7\\u09E2-\\u09E3\\u09FE\\u0A01-\\u0A03\\u0A3C\\u0A3E-\\u0A42\\u0A47-\\u0A48\\u0A4B-\\u0A4D\\u0A51\\u0A70-\\u0A71\\u0A75\\u0A81-\\u0A83\\u0ABC\\u0ABE-\\u0AC5\\u0AC7-\\u0AC9\\u0ACB-\\u0ACD\\u0AE2-\\u0AE3\\u0AFA-\\u0AFF\\u0B01-\\u0B03\\u0B3C\\u0B3E-\\u0B44\\u0B47-\\u0B48\\u0B4B-\\u0B4D\\u0B55-\\u0B57\\u0B62-\\u0B63\\u0B82\\u0BBE-\\u0BC2\\u0BC6-\\u0BC8\\u0BCA-\\u0BCD\\u0BD7\\u0C00-\\u0C04\\u0C3C\\u0C3E-\\u0C44\\u0C46-\\u0C48\\u0C4A-\\u0C4D\\u0C55-\\u0C56\\u0C62-\\u0C63\\u0C81-\\u0C83\\u0CBC\\u0CBE-\\u0CC4\\u0CC6-\\u0CC8\\u0CCA-\\u0CCD\\u0CD5-\\u0CD6\\u0CE2-\\u0CE3\\u0CF3\\u0D00-\\u0D03\\u0D3B-\\u0D3C\\u0D3E-\\u0D44\\u0D46-\\u0D48\\u0D4A-\\u0D4D\\u0D57\\u0D62-\\u0D63\\u0D81-\\u0D83\\u0DCA\\u0DCF-\\u0DD4\\u0DD6\\u0DD8-\\u0DDF\\u0DF2-\\u0DF3\\u0E31\\u0E34-\\u0E3A\\u0E47-\\u0E4E\\u0EB1\\u0EB4-\\u0EBC\\u0EC8-\\u0ECE\\u0F18-\\u0F19\\u0F35\\u0F37\\u0F39\\u0F3E-\\u0F3F\\u0F71-\\u0F84\\u0F86-\\u0F87\\u0F8D-\\u0F97\\u0F99-\\u0FBC\\u0FC6\\u102B-\\u103E\\u1056-\\u1059\\u105E-\\u1060\\u1062-\\u1064\\u1067-\\u106D\\u1071-\\u1074\\u1082-\\u108D\\u108F\\u109A-\\u109D\\u135D-\\u135F\\u1712-\\u1715\\u1732-\\u1734\\u1752-\\u1753\\u1772-\\u1773\\u17B4-\\u17D3\\u17DD\\u180B-\\u180D\\u180F\\u1885-\\u1886\\u18A9\\u1920-\\u192B\\u1930-\\u193B\\u1A17-\\u1A1B\\u1A55-\\u1A5E\\u1A60-\\u1A7C\\u1A7F\\u1AB0-\\u1ADD\\u1AE0-\\u1AEB\\u1B00-\\u1B04\\u1B34-\\u1B44\\u1B6B-\\u1B73\\u1B80-\\u1B82\\u1BA1-\\u1BAD\\u1BE6-\\u1BF3\\u1C24-\\u1C37\\u1CD0-\\u1CD2\\u1CD4-\\u1CE8\\u1CED\\u1CF4\\u1CF7-\\u1CF9\\u1DC0-\\u1DFF\\u20D0-\\u20F0\\u2CEF-\\u2CF1\\u2D7F\\u2DE0-\\u2DFF\\u302A-\\u302F\\u3099-\\u309A\\uA66F-\\uA672\\uA674-\\uA67D\\uA69E-\\uA69F\\uA6F0-\\uA6F1\\uA802\\uA806\\uA80B\\uA823-\\uA827\\uA82C\\uA880-\\uA881\\uA8B4-\\uA8C5\\uA8E0-\\uA8F1\\uA8FF\\uA926-\\uA92D\\uA947-\\uA953\\uA980-\\uA983\\uA9B3-\\uA9C0\\uA9E5\\uAA29-\\uAA36\\uAA43\\uAA4C-\\uAA4D\\uAA7B-\\uAA7D\\uAAB0\\uAAB2-\\uAAB4\\uAAB7-\\uAAB8\\uAABE-\\uAABF\\uAAC1\\uAAEB-\\uAAEF\\uAAF5-\\uAAF6\\uABE3-\\uABEA\\uABEC-\\uABED\\uFB1E\\uFE00-\\uFE0F\\uFE20-\\uFE2F\\u{101FD}\\u{102E0}\\u{10376}-\\u{1037A}\\u{10A01}-\\u{10A03}\\u{10A05}-\\u{10A06}\\u{10A0C}-\\u{10A0F}\\u{10A38}-\\u{10A3A}\\u{10A3F}\\u{10AE5}-\\u{10AE6}\\u{10D24}-\\u{10D27}\\u{10D69}-\\u{10D6D}\\u{10EAB}-\\u{10EAC}\\u{10EFA}-\\u{10EFF}\\u{10F46}-\\u{10F50}\\u{10F82}-\\u{10F85}\\u{11000}-\\u{11002}\\u{11038}-\\u{11046}\\u{11070}\\u{11073}-\\u{11074}\\u{1107F}-\\u{11082}\\u{110B0}-\\u{110BA}\\u{110C2}\\u{11100}-\\u{11102}\\u{11127}-\\u{11134}\\u{11145}-\\u{11146}\\u{11173}\\u{11180}-\\u{11182}\\u{111B3}-\\u{111C0}\\u{111C9}-\\u{111CC}\\u{111CE}-\\u{111CF}\\u{1122C}-\\u{11237}\\u{1123E}\\u{11241}\\u{112DF}-\\u{112EA}\\u{11300}-\\u{11303}\\u{1133B}-\\u{1133C}\\u{1133E}-\\u{11344}\\u{11347}-\\u{11348}\\u{1134B}-\\u{1134D}\\u{11357}\\u{11362}-\\u{11363}\\u{11366}-\\u{1136C}\\u{11370}-\\u{11374}\\u{113B8}-\\u{113C0}\\u{113C2}\\u{113C5}\\u{113C7}-\\u{113CA}\\u{113CC}-\\u{113D0}\\u{113D2}\\u{113E1}-\\u{113E2}\\u{11435}-\\u{11446}\\u{1145E}\\u{114B0}-\\u{114C3}\\u{115AF}-\\u{115B5}\\u{115B8}-\\u{115C0}\\u{115DC}-\\u{115DD}\\u{11630}-\\u{11640}\\u{116AB}-\\u{116B7}\\u{1171D}-\\u{1172B}\\u{1182C}-\\u{1183A}\\u{11930}-\\u{11935}\\u{11937}-\\u{11938}\\u{1193B}-\\u{1193E}\\u{11940}\\u{11942}-\\u{11943}\\u{119D1}-\\u{119D7}\\u{119DA}-\\u{119E0}\\u{119E4}\\u{11A01}-\\u{11A0A}\\u{11A33}-\\u{11A39}\\u{11A3B}-\\u{11A3E}\\u{11A47}\\u{11A51}-\\u{11A5B}\\u{11A8A}-\\u{11A99}\\u{11B60}-\\u{11B67}\\u{11C2F}-\\u{11C36}\\u{11C38}-\\u{11C3F}\\u{11C92}-\\u{11CA7}\\u{11CA9}-\\u{11CB6}\\u{11D31}-\\u{11D36}\\u{11D3A}\\u{11D3C}-\\u{11D3D}\\u{11D3F}-\\u{11D45}\\u{11D47}\\u{11D8A}-\\u{11D8E}\\u{11D90}-\\u{11D91}\\u{11D93}-\\u{11D97}\\u{11EF3}-\\u{11EF6}\\u{11F00}-\\u{11F01}\\u{11F03}\\u{11F34}-\\u{11F3A}\\u{11F3E}-\\u{11F42}\\u{11F5A}\\u{13440}\\u{13447}-\\u{13455}\\u{1611E}-\\u{1612F}\\u{16AF0}-\\u{16AF4}\\u{16B30}-\\u{16B36}\\u{16F4F}\\u{16F51}-\\u{16F87}\\u{16F8F}-\\u{16F92}\\u{16FE4}\\u{16FF0}-\\u{16FF1}\\u{1BC9D}-\\u{1BC9E}\\u{1CF00}-\\u{1CF2D}\\u{1CF30}-\\u{1CF46}\\u{1D165}-\\u{1D169}\\u{1D16D}-\\u{1D172}\\u{1D17B}-\\u{1D182}\\u{1D185}-\\u{1D18B}\\u{1D1AA}-\\u{1D1AD}\\u{1D242}-\\u{1D244}\\u{1DA00}-\\u{1DA36}\\u{1DA3B}-\\u{1DA6C}\\u{1DA75}\\u{1DA84}\\u{1DA9B}-\\u{1DA9F}\\u{1DAA1}-\\u{1DAAF}\\u{1E000}-\\u{1E006}\\u{1E008}-\\u{1E018}\\u{1E01B}-\\u{1E021}\\u{1E023}-\\u{1E024}\\u{1E026}-\\u{1E02A}\\u{1E08F}\\u{1E130}-\\u{1E136}\\u{1E2AE}\\u{1E2EC}-\\u{1E2EF}\\u{1E4EC}-\\u{1E4EF}\\u{1E5EE}-\\u{1E5EF}\\u{1E6E3}\\u{1E6E6}\\u{1E6EE}-\\u{1E6EF}\\u{1E6F5}\\u{1E8D0}-\\u{1E8D6}\\u{1E944}-\\u{1E94A}\\u{E0100}-\\u{E01EF}]", "gu"), '').toLowerCase();
|
|
16
|
+
/**
|
|
17
|
+
* The precomposed letters each base letter stands for, both cases and the base
|
|
18
|
+
* itself included: `e` → `eEéÉèÈêÊëË…`.
|
|
19
|
+
*
|
|
20
|
+
* Built from Latin-1 Supplement and Latin Extended-A by {@link foldText}
|
|
21
|
+
* itself, rather than typed out, so a letter folds in the table exactly when
|
|
22
|
+
* it folds in the rule. Both cases are listed explicitly so a pattern built
|
|
23
|
+
* from it never depends on how a regex engine case-folds outside ASCII.
|
|
24
|
+
*/ export const FOLD_TABLE = (()=>{
|
|
25
|
+
const table = {};
|
|
26
|
+
for(let code = 0xc0; code <= 0x17f; code += 1){
|
|
27
|
+
var _table, _base, _;
|
|
28
|
+
const letter = String.fromCharCode(code);
|
|
29
|
+
const base = foldText(letter);
|
|
30
|
+
if (base === letter || !/^[a-z]$/.test(base)) continue;
|
|
31
|
+
(_ = (_table = table)[_base = base]) != null ? _ : _table[_base] = `${base}${base.toUpperCase()}`;
|
|
32
|
+
table[base] += letter;
|
|
33
|
+
}
|
|
34
|
+
return table;
|
|
35
|
+
})();
|
|
36
|
+
/** Escapes a string so it is matched literally inside a regular expression. */ export const escapeRegex = (value)=>value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
37
|
+
/**
|
|
38
|
+
* A regular expression source that matches `value` as a substring the way
|
|
39
|
+
* `like` does, for an engine that cannot fold the text it searches: the value
|
|
40
|
+
* is folded, and each letter that has accented forms becomes a class of all of
|
|
41
|
+
* them, so `cafe` matches `Café` and `CAFÉ`. Everything else is escaped, so a
|
|
42
|
+
* `.` or a `(` in a search is matched literally.
|
|
43
|
+
*/ export const foldedPattern = (value)=>Array.from(foldText(value)).map((char)=>{
|
|
44
|
+
const forms = FOLD_TABLE[char];
|
|
45
|
+
return forms === undefined ? escapeRegex(char) : `[${forms}]`;
|
|
46
|
+
}).join('');
|
|
47
|
+
/** Whether `actual` contains `value`, both folded. */ export const containsFolded = (actual, value)=>foldText(actual).includes(foldText(value));
|
|
48
|
+
|
|
49
|
+
//# sourceMappingURL=fold.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/query/fold.ts"],"sourcesContent":["/**\n * What `like` compares: text with its accents and its case taken off.\n *\n * One rule, stated once, because three engines answer `like` and each needs it\n * in a different form — the in-memory matcher folds both sides, the Mongo\n * adapter cannot fold a stored value and so widens the query instead, and the\n * SQL adapter folds the column with `translate`. Every form is derived from\n * {@link foldText} here, so none of them can drift into a different rule.\n *\n * The rule is Unicode's: decompose (NFD), drop the combining marks, lowercase.\n * `é` is `e`, `ü` is `u`, and `ñ` is `n` — a buyer on a keyboard without the\n * letter types `nino` for `niño`, and finding nothing is the worse answer. A\n * letter that does not decompose (`ß`, `ø`, `æ`, `ł`) is left as it is, by\n * every engine alike.\n */\n\n/** `value` with its accents and case removed. */\nexport const foldText = (value: string): string =>\n value.normalize('NFD').replace(/\\p{M}/gu, '').toLowerCase();\n\n/**\n * The precomposed letters each base letter stands for, both cases and the base\n * itself included: `e` → `eEéÉèÈêÊëË…`.\n *\n * Built from Latin-1 Supplement and Latin Extended-A by {@link foldText}\n * itself, rather than typed out, so a letter folds in the table exactly when\n * it folds in the rule. Both cases are listed explicitly so a pattern built\n * from it never depends on how a regex engine case-folds outside ASCII.\n */\nexport const FOLD_TABLE: Readonly<Record<string, string>> = (() => {\n const table: Record<string, string> = {};\n for (let code = 0xc0; code <= 0x17f; code += 1) {\n const letter = String.fromCharCode(code);\n const base = foldText(letter);\n if (base === letter || !/^[a-z]$/.test(base)) continue;\n table[base] ??= `${base}${base.toUpperCase()}`;\n table[base] += letter;\n }\n return table;\n})();\n\n/** Escapes a string so it is matched literally inside a regular expression. */\nexport const escapeRegex = (value: string): string =>\n value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n\n/**\n * A regular expression source that matches `value` as a substring the way\n * `like` does, for an engine that cannot fold the text it searches: the value\n * is folded, and each letter that has accented forms becomes a class of all of\n * them, so `cafe` matches `Café` and `CAFÉ`. Everything else is escaped, so a\n * `.` or a `(` in a search is matched literally.\n */\nexport const foldedPattern = (value: string): string =>\n Array.from(foldText(value))\n .map(char => {\n const forms = FOLD_TABLE[char];\n return forms === undefined ? escapeRegex(char) : `[${forms}]`;\n })\n .join('');\n\n/** Whether `actual` contains `value`, both folded. */\nexport const containsFolded = (actual: string, value: string): boolean =>\n foldText(actual).includes(foldText(value));\n"],"names":["foldText","value","normalize","replace","toLowerCase","FOLD_TABLE","table","code","base","letter","String","fromCharCode","test","toUpperCase","escapeRegex","foldedPattern","Array","from","map","char","forms","undefined","join","containsFolded","actual","includes"],"mappings":"AAAA;;;;;;;;;;;;;;CAcC,GAED,+CAA+C,GAC/C,OAAO,MAAMA,WAAW,CAACC,QACvBA,MAAMC,SAAS,CAAC,OAAOC,OAAO,CAAC,27JAAW,IAAIC,WAAW,GAAG;AAE9D;;;;;;;;CAQC,GACD,OAAO,MAAMC,aAA+C,AAAC,CAAA;IAC3D,MAAMC,QAAgC,CAAC;IACvC,IAAK,IAAIC,OAAO,MAAMA,QAAQ,OAAOA,QAAQ,EAAG;YAI9CD,QAAME,OAANF;QAHA,MAAMG,SAASC,OAAOC,YAAY,CAACJ;QACnC,MAAMC,OAAOR,SAASS;QACtB,IAAID,SAASC,UAAU,CAAC,UAAUG,IAAI,CAACJ,OAAO;SAC9CF,IAAAA,CAAAA,SAAAA,MAAK,CAACE,QAAAA,KAAK,YAAXF,IAAAA,MAAK,CAACE,MAAK,GAAK,GAAGA,OAAOA,KAAKK,WAAW,IAAI;QAC9CP,KAAK,CAACE,KAAK,IAAIC;IACjB;IACA,OAAOH;AACT,CAAA,IAAK;AAEL,6EAA6E,GAC7E,OAAO,MAAMQ,cAAc,CAACb,QAC1BA,MAAME,OAAO,CAAC,uBAAuB,QAAQ;AAE/C;;;;;;CAMC,GACD,OAAO,MAAMY,gBAAgB,CAACd,QAC5Be,MAAMC,IAAI,CAACjB,SAASC,QACjBiB,GAAG,CAACC,CAAAA;QACH,MAAMC,QAAQf,UAAU,CAACc,KAAK;QAC9B,OAAOC,UAAUC,YAAYP,YAAYK,QAAQ,CAAC,CAAC,EAAEC,MAAM,CAAC,CAAC;IAC/D,GACCE,IAAI,CAAC,IAAI;AAEd,oDAAoD,GACpD,OAAO,MAAMC,iBAAiB,CAACC,QAAgBvB,QAC7CD,SAASwB,QAAQC,QAAQ,CAACzB,SAASC,QAAQ"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Entity } from '../types/Entity.js';
|
|
2
|
+
import type { EntityFacetCounts, EntityFacetQuery } from '../types/EntityFacets.js';
|
|
2
3
|
import type { EntityFiltering } from '../types/EntityFiltering.js';
|
|
3
4
|
import type { EntitySorting } from '../types/EntitySorting.js';
|
|
4
5
|
/**
|
|
@@ -16,7 +17,8 @@ import type { EntitySorting } from '../types/EntitySorting.js';
|
|
|
16
17
|
* - `in` and `nin` against an array match when **any element** does, as `$in`;
|
|
17
18
|
* - a property may be a dotted path into a member holding an object, which is
|
|
18
19
|
* how a filter reaches one locale of a `localized` member (`name.en`);
|
|
19
|
-
* - `like` is a
|
|
20
|
+
* - `like` is a substring match that ignores accents and case — `cafe` finds
|
|
21
|
+
* `Café` — by the one rule in `fold.ts` every adapter derives its form from;
|
|
20
22
|
* - a missing value sorts below a present one, numbers and dates compare as
|
|
21
23
|
* themselves and anything else as text;
|
|
22
24
|
* - every top-level filtering entry is combined with `and`.
|
|
@@ -46,4 +48,13 @@ export declare function applyFiltering<TEntity extends Entity>(records: readonly
|
|
|
46
48
|
* accident. No key leaves the order the records were given in.
|
|
47
49
|
*/
|
|
48
50
|
export declare function applySorting<TEntity extends Entity>(records: readonly TEntity[], sorting: readonly EntitySorting<TEntity>[] | undefined): TEntity[];
|
|
51
|
+
/**
|
|
52
|
+
* How many records hold each value of each queried member, each member counted
|
|
53
|
+
* over the records **its own** filtering keeps — the in-memory form of a Mongo
|
|
54
|
+
* `$facet` of `$match`, `$unwind` and `$group`, with its rules:
|
|
55
|
+
* - an array member counts each element, as `$unwind` does;
|
|
56
|
+
* - a missing or `null` value, or an empty array, is not counted;
|
|
57
|
+
* - only a string, number or boolean is a value; anything else is skipped.
|
|
58
|
+
*/
|
|
59
|
+
export declare function countFacets<TEntity extends Entity>(records: readonly TEntity[], queries: readonly EntityFacetQuery<TEntity>[]): EntityFacetCounts;
|
|
49
60
|
//# sourceMappingURL=in-memory-query.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"in-memory-query.d.ts","sourceRoot":"","sources":["../../src/query/in-memory-query.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EAEV,eAAe,EAEhB,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"in-memory-query.d.ts","sourceRoot":"","sources":["../../src/query/in-memory-query.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EACV,iBAAiB,EACjB,gBAAgB,EAEjB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAEV,eAAe,EAEhB,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAG/D;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAInD;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CASjE;AAED,0EAA0E;AAC1E,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,CAYzE;AAsJD,wDAAwD;AACxD,wBAAgB,gBAAgB,CAAC,OAAO,SAAS,MAAM,EACrD,MAAM,EAAE,OAAO,EACf,SAAS,EAAE,SAAS,eAAe,CAAC,OAAO,CAAC,EAAE,GAAG,SAAS,GACzD,OAAO,CAMT;AAED,6EAA6E;AAC7E,wBAAgB,cAAc,CAAC,OAAO,SAAS,MAAM,EACnD,OAAO,EAAE,SAAS,OAAO,EAAE,EAC3B,SAAS,EAAE,SAAS,eAAe,CAAC,OAAO,CAAC,EAAE,GAAG,SAAS,GACzD,OAAO,EAAE,CAEX;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,OAAO,SAAS,MAAM,EACjD,OAAO,EAAE,SAAS,OAAO,EAAE,EAC3B,OAAO,EAAE,SAAS,aAAa,CAAC,OAAO,CAAC,EAAE,GAAG,SAAS,GACrD,OAAO,EAAE,CAqBX;AAWD;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,OAAO,SAAS,MAAM,EAChD,OAAO,EAAE,SAAS,OAAO,EAAE,EAC3B,OAAO,EAAE,SAAS,gBAAgB,CAAC,OAAO,CAAC,EAAE,GAC5C,iBAAiB,CAgBnB"}
|