@entifix/static 0.8.0 → 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
CHANGED
|
@@ -21,9 +21,9 @@ pnpm add @entifix/static effect
|
|
|
21
21
|
|
|
22
22
|
## Entry points
|
|
23
23
|
|
|
24
|
-
| Import | What it holds
|
|
25
|
-
| ----------------- |
|
|
26
|
-
| `@entifix/static` | `makeStaticRepository`, `staticRepositoryFromRecords` |
|
|
24
|
+
| Import | What it holds |
|
|
25
|
+
| ----------------- | ------------------------------------------------------------------------------- |
|
|
26
|
+
| `@entifix/static` | `makeStaticRepository`, `staticRepositoryFromRecords`, `makeStaticFacetCounter` |
|
|
27
27
|
|
|
28
28
|
---
|
|
29
29
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { EntityRepository } from '@entifix/business';
|
|
1
|
+
import type { EntityFacetCounter, EntityRepository } from '@entifix/business';
|
|
2
2
|
import { type Entity, type EntityConstructor } from '@entifix/core';
|
|
3
3
|
export interface StaticRepositoryOptions {
|
|
4
4
|
/** The name an error message uses. Defaults to the entity's class name. */
|
|
@@ -21,4 +21,10 @@ export interface StaticRepositoryOptions {
|
|
|
21
21
|
* {@link staticRepositoryFromRecords}, which validates them first.
|
|
22
22
|
*/
|
|
23
23
|
export declare function makeStaticRepository<TEntity extends Entity>(entityConstructor: EntityConstructor<TEntity>, records: readonly TEntity[], { label }?: StaticRepositoryOptions): EntityRepository;
|
|
24
|
+
/**
|
|
25
|
+
* Facet counts over the same records a {@link makeStaticRepository} serves:
|
|
26
|
+
* how many hold each value of a member, each member under its own filtering,
|
|
27
|
+
* with the in-memory query's rules.
|
|
28
|
+
*/
|
|
29
|
+
export declare function makeStaticFacetCounter<TEntity extends Entity>(records: readonly TEntity[]): EntityFacetCounter;
|
|
24
30
|
//# sourceMappingURL=static-repository.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"static-repository.d.ts","sourceRoot":"","sources":["../src/static-repository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"static-repository.d.ts","sourceRoot":"","sources":["../src/static-repository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC9E,OAAO,EAQL,KAAK,MAAM,EACX,KAAK,iBAAiB,EAOvB,MAAM,eAAe,CAAC;AAkCvB,MAAM,WAAW,uBAAuB;IACtC,2EAA2E;IAC3E,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,SAAS,MAAM,EACzD,iBAAiB,EAAE,iBAAiB,CAAC,OAAO,CAAC,EAC7C,OAAO,EAAE,SAAS,OAAO,EAAE,EAC3B,EAAE,KAA8B,EAAE,GAAE,uBAA4B,GAC/D,gBAAgB,CA0DlB;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,SAAS,MAAM,EAC3D,OAAO,EAAE,SAAS,OAAO,EAAE,GAC1B,kBAAkB,CAMpB"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { applyFiltering, applySorting, deserializeSingleEntity, EntifixConnError, EntifixLogicError, extractMetaAccessors, serializeEntity } from '@entifix/core';
|
|
1
|
+
import { applyFiltering, applySorting, countFacets, deserializeSingleEntity, EntifixConnError, EntifixLogicError, extractMetaAccessors, serializeEntity } from '@entifix/core';
|
|
2
2
|
import { Effect } from 'effect';
|
|
3
3
|
/** The Mongo adapter's defaults, which this one copies exactly. */ const DEFAULT_PAGE = 1;
|
|
4
4
|
const DEFAULT_PAGE_SIZE = 10;
|
|
@@ -69,5 +69,17 @@ const DEFAULT_PAGE_SIZE = 10;
|
|
|
69
69
|
delete: ()=>readOnly('delete')
|
|
70
70
|
};
|
|
71
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* Facet counts over the same records a {@link makeStaticRepository} serves:
|
|
74
|
+
* how many hold each value of a member, each member under its own filtering,
|
|
75
|
+
* with the in-memory query's rules.
|
|
76
|
+
*/ export function makeStaticFacetCounter(records) {
|
|
77
|
+
const stored = [
|
|
78
|
+
...records
|
|
79
|
+
];
|
|
80
|
+
return {
|
|
81
|
+
count: (queries)=>Effect.sync(()=>countFacets(stored, queries))
|
|
82
|
+
};
|
|
83
|
+
}
|
|
72
84
|
|
|
73
85
|
//# sourceMappingURL=static-repository.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/static-repository.ts"],"sourcesContent":["import type { EntityRepository } from '@entifix/business';\nimport {\n applyFiltering,\n applySorting,\n deserializeSingleEntity,\n EntifixConnError,\n type EntifixError,\n EntifixLogicError,\n type Entity,\n type EntityConstructor,\n type EntityId,\n type EntityLoadRequest,\n type EntityPage,\n extractMetaAccessors,\n serializeEntity,\n} from '@entifix/core';\nimport { Effect } from 'effect';\n\n/** The Mongo adapter's defaults, which this one copies exactly. */\nconst DEFAULT_PAGE = 1;\nconst DEFAULT_PAGE_SIZE = 10;\n\n/**\n * A copy of a record, so a caller can never reach into the store.\n *\n * Through the entity's own mapping — serialize, then deserialize — as a real\n * adapter maps every record on the way out. An entity with no accessors has no\n * mapping to go through, so it is copied member by member instead.\n */\nfunction copyOf<TEntity extends Entity>(\n record: TEntity,\n): Effect.Effect<TEntity, EntifixError> {\n const entityConstructor = record.constructor as EntityConstructor<TEntity>;\n if (extractMetaAccessors(entityConstructor).length === 0) {\n return Effect.succeed(\n Object.assign(\n Object.create(Object.getPrototypeOf(record) as object) as TEntity,\n record,\n ),\n );\n }\n // `undefined` only comes back for `null`/`undefined` input, and\n // `serializeEntity` always hands over an object.\n return deserializeSingleEntity(\n entityConstructor,\n serializeEntity(entityConstructor, record),\n ) as Effect.Effect<TEntity, EntifixError>;\n}\n\nexport interface StaticRepositoryOptions {\n /** The name an error message uses. Defaults to the entity's class name. */\n readonly label?: string;\n}\n\n/**\n * A read-only {@link EntityRepository} over records held in memory — content\n * bundled with a static build, served through the ordinary use cases, and\n * swappable for a REST adapter the day a backend appears.\n *\n * `get` and `load` carry the Mongo adapter's semantics, from core's in-memory\n * query evaluation: every filter operator, groups, array membership, a dotted\n * path into a localized member, multi-key sorting, and 1-based paging that\n * defaults to ten. Both hand out copies.\n *\n * `save` and `delete` fail with an `EntifixLogicError`: a static source is\n * read-only, and pretending otherwise is worse than refusing.\n *\n * It holds instances. Records that arrive as JSON go through\n * {@link staticRepositoryFromRecords}, which validates them first.\n */\nexport function makeStaticRepository<TEntity extends Entity>(\n entityConstructor: EntityConstructor<TEntity>,\n records: readonly TEntity[],\n { label = entityConstructor.name }: StaticRepositoryOptions = {},\n): EntityRepository {\n const stored = [...records];\n\n const readOnly = (verb: string) =>\n Effect.fail(\n new EntifixLogicError(\n `Cannot ${verb} a ${label}: it is served from static content, which is read-only`,\n undefined,\n { entity: label, operation: verb },\n ),\n );\n\n const get = (id: EntityId) => {\n const found = stored.find(record => record.id === id);\n return found === undefined\n ? Effect.fail(\n new EntifixConnError('Entity not found', undefined, {\n entity: label,\n id,\n }),\n )\n : copyOf(found);\n };\n\n const load = (request: EntityLoadRequest<TEntity>) =>\n Effect.suspend(() => {\n const matched = applySorting(\n applyFiltering(stored, request.filtering),\n request.sorting,\n );\n const page = request.page ?? DEFAULT_PAGE;\n const pageSize = request.pageSize ?? DEFAULT_PAGE_SIZE;\n const start = (page - 1) * pageSize;\n\n return Effect.forEach(\n matched.slice(start, start + pageSize),\n copyOf,\n ).pipe(\n Effect.map(\n items =>\n ({\n items,\n total: matched.length,\n request,\n }) satisfies EntityPage<TEntity>,\n ),\n );\n });\n\n // Each method closes over its own state, so nothing is required from the\n // Effect environment; that is assignable to the port's channel, and the\n // cast keeps its generic signatures, as entifix's other adapters do.\n return {\n get,\n load,\n save: () => readOnly('save'),\n delete: () => readOnly('delete'),\n } as unknown as EntityRepository;\n}\n"],"names":["applyFiltering","applySorting","deserializeSingleEntity","EntifixConnError","EntifixLogicError","extractMetaAccessors","serializeEntity","Effect","DEFAULT_PAGE","DEFAULT_PAGE_SIZE","copyOf","record","entityConstructor","length","succeed","Object","assign","create","getPrototypeOf","makeStaticRepository","records","label","name","stored","readOnly","verb","fail","undefined","entity","operation","get","id","found","find","load","request","suspend","matched","filtering","sorting","page","pageSize","start","forEach","slice","pipe","map","items","total","save","delete"],"mappings":"AACA,SACEA,cAAc,EACdC,YAAY,EACZC,uBAAuB,EACvBC,gBAAgB,EAEhBC,iBAAiB,
|
|
1
|
+
{"version":3,"sources":["../src/static-repository.ts"],"sourcesContent":["import type { EntityFacetCounter, EntityRepository } from '@entifix/business';\nimport {\n applyFiltering,\n applySorting,\n countFacets,\n deserializeSingleEntity,\n EntifixConnError,\n type EntifixError,\n EntifixLogicError,\n type Entity,\n type EntityConstructor,\n type EntityFacetQuery,\n type EntityId,\n type EntityLoadRequest,\n type EntityPage,\n extractMetaAccessors,\n serializeEntity,\n} from '@entifix/core';\nimport { Effect } from 'effect';\n\n/** The Mongo adapter's defaults, which this one copies exactly. */\nconst DEFAULT_PAGE = 1;\nconst DEFAULT_PAGE_SIZE = 10;\n\n/**\n * A copy of a record, so a caller can never reach into the store.\n *\n * Through the entity's own mapping — serialize, then deserialize — as a real\n * adapter maps every record on the way out. An entity with no accessors has no\n * mapping to go through, so it is copied member by member instead.\n */\nfunction copyOf<TEntity extends Entity>(\n record: TEntity,\n): Effect.Effect<TEntity, EntifixError> {\n const entityConstructor = record.constructor as EntityConstructor<TEntity>;\n if (extractMetaAccessors(entityConstructor).length === 0) {\n return Effect.succeed(\n Object.assign(\n Object.create(Object.getPrototypeOf(record) as object) as TEntity,\n record,\n ),\n );\n }\n // `undefined` only comes back for `null`/`undefined` input, and\n // `serializeEntity` always hands over an object.\n return deserializeSingleEntity(\n entityConstructor,\n serializeEntity(entityConstructor, record),\n ) as Effect.Effect<TEntity, EntifixError>;\n}\n\nexport interface StaticRepositoryOptions {\n /** The name an error message uses. Defaults to the entity's class name. */\n readonly label?: string;\n}\n\n/**\n * A read-only {@link EntityRepository} over records held in memory — content\n * bundled with a static build, served through the ordinary use cases, and\n * swappable for a REST adapter the day a backend appears.\n *\n * `get` and `load` carry the Mongo adapter's semantics, from core's in-memory\n * query evaluation: every filter operator, groups, array membership, a dotted\n * path into a localized member, multi-key sorting, and 1-based paging that\n * defaults to ten. Both hand out copies.\n *\n * `save` and `delete` fail with an `EntifixLogicError`: a static source is\n * read-only, and pretending otherwise is worse than refusing.\n *\n * It holds instances. Records that arrive as JSON go through\n * {@link staticRepositoryFromRecords}, which validates them first.\n */\nexport function makeStaticRepository<TEntity extends Entity>(\n entityConstructor: EntityConstructor<TEntity>,\n records: readonly TEntity[],\n { label = entityConstructor.name }: StaticRepositoryOptions = {},\n): EntityRepository {\n const stored = [...records];\n\n const readOnly = (verb: string) =>\n Effect.fail(\n new EntifixLogicError(\n `Cannot ${verb} a ${label}: it is served from static content, which is read-only`,\n undefined,\n { entity: label, operation: verb },\n ),\n );\n\n const get = (id: EntityId) => {\n const found = stored.find(record => record.id === id);\n return found === undefined\n ? Effect.fail(\n new EntifixConnError('Entity not found', undefined, {\n entity: label,\n id,\n }),\n )\n : copyOf(found);\n };\n\n const load = (request: EntityLoadRequest<TEntity>) =>\n Effect.suspend(() => {\n const matched = applySorting(\n applyFiltering(stored, request.filtering),\n request.sorting,\n );\n const page = request.page ?? DEFAULT_PAGE;\n const pageSize = request.pageSize ?? DEFAULT_PAGE_SIZE;\n const start = (page - 1) * pageSize;\n\n return Effect.forEach(\n matched.slice(start, start + pageSize),\n copyOf,\n ).pipe(\n Effect.map(\n items =>\n ({\n items,\n total: matched.length,\n request,\n }) satisfies EntityPage<TEntity>,\n ),\n );\n });\n\n // Each method closes over its own state, so nothing is required from the\n // Effect environment; that is assignable to the port's channel, and the\n // cast keeps its generic signatures, as entifix's other adapters do.\n return {\n get,\n load,\n save: () => readOnly('save'),\n delete: () => readOnly('delete'),\n } as unknown as EntityRepository;\n}\n\n/**\n * Facet counts over the same records a {@link makeStaticRepository} serves:\n * how many hold each value of a member, each member under its own filtering,\n * with the in-memory query's rules.\n */\nexport function makeStaticFacetCounter<TEntity extends Entity>(\n records: readonly TEntity[],\n): EntityFacetCounter {\n const stored = [...records];\n return {\n count: (queries: readonly EntityFacetQuery<TEntity>[]) =>\n Effect.sync(() => countFacets(stored, queries)),\n } as unknown as EntityFacetCounter;\n}\n"],"names":["applyFiltering","applySorting","countFacets","deserializeSingleEntity","EntifixConnError","EntifixLogicError","extractMetaAccessors","serializeEntity","Effect","DEFAULT_PAGE","DEFAULT_PAGE_SIZE","copyOf","record","entityConstructor","length","succeed","Object","assign","create","getPrototypeOf","makeStaticRepository","records","label","name","stored","readOnly","verb","fail","undefined","entity","operation","get","id","found","find","load","request","suspend","matched","filtering","sorting","page","pageSize","start","forEach","slice","pipe","map","items","total","save","delete","makeStaticFacetCounter","count","queries","sync"],"mappings":"AACA,SACEA,cAAc,EACdC,YAAY,EACZC,WAAW,EACXC,uBAAuB,EACvBC,gBAAgB,EAEhBC,iBAAiB,EAOjBC,oBAAoB,EACpBC,eAAe,QACV,gBAAgB;AACvB,SAASC,MAAM,QAAQ,SAAS;AAEhC,iEAAiE,GACjE,MAAMC,eAAe;AACrB,MAAMC,oBAAoB;AAE1B;;;;;;CAMC,GACD,SAASC,OACPC,MAAe;IAEf,MAAMC,oBAAoBD,OAAO,WAAW;IAC5C,IAAIN,qBAAqBO,mBAAmBC,MAAM,KAAK,GAAG;QACxD,OAAON,OAAOO,OAAO,CACnBC,OAAOC,MAAM,CACXD,OAAOE,MAAM,CAACF,OAAOG,cAAc,CAACP,UACpCA;IAGN;IACA,gEAAgE;IAChE,iDAAiD;IACjD,OAAOT,wBACLU,mBACAN,gBAAgBM,mBAAmBD;AAEvC;AAOA;;;;;;;;;;;;;;;CAeC,GACD,OAAO,SAASQ,qBACdP,iBAA6C,EAC7CQ,OAA2B,EAC3B,EAAEC,QAAQT,kBAAkBU,IAAI,EAA2B,GAAG,CAAC,CAAC;IAEhE,MAAMC,SAAS;WAAIH;KAAQ;IAE3B,MAAMI,WAAW,CAACC,OAChBlB,OAAOmB,IAAI,CACT,IAAItB,kBACF,CAAC,OAAO,EAAEqB,KAAK,GAAG,EAAEJ,MAAM,sDAAsD,CAAC,EACjFM,WACA;YAAEC,QAAQP;YAAOQ,WAAWJ;QAAK;IAIvC,MAAMK,MAAM,CAACC;QACX,MAAMC,QAAQT,OAAOU,IAAI,CAACtB,CAAAA,SAAUA,OAAOoB,EAAE,KAAKA;QAClD,OAAOC,UAAUL,YACbpB,OAAOmB,IAAI,CACT,IAAIvB,iBAAiB,oBAAoBwB,WAAW;YAClDC,QAAQP;YACRU;QACF,MAEFrB,OAAOsB;IACb;IAEA,MAAME,OAAO,CAACC,UACZ5B,OAAO6B,OAAO,CAAC;gBAKAD,eACIA;YALjB,MAAME,UAAUrC,aACdD,eAAewB,QAAQY,QAAQG,SAAS,GACxCH,QAAQI,OAAO;YAEjB,MAAMC,QAAOL,gBAAAA,QAAQK,IAAI,YAAZL,gBAAgB3B;YAC7B,MAAMiC,YAAWN,oBAAAA,QAAQM,QAAQ,YAAhBN,oBAAoB1B;YACrC,MAAMiC,QAAQ,AAACF,CAAAA,OAAO,CAAA,IAAKC;YAE3B,OAAOlC,OAAOoC,OAAO,CACnBN,QAAQO,KAAK,CAACF,OAAOA,QAAQD,WAC7B/B,QACAmC,IAAI,CACJtC,OAAOuC,GAAG,CACRC,CAAAA,QACG,CAAA;oBACCA;oBACAC,OAAOX,QAAQxB,MAAM;oBACrBsB;gBACF,CAAA;QAGR;IAEF,yEAAyE;IACzE,wEAAwE;IACxE,qEAAqE;IACrE,OAAO;QACLL;QACAI;QACAe,MAAM,IAAMzB,SAAS;QACrB0B,QAAQ,IAAM1B,SAAS;IACzB;AACF;AAEA;;;;CAIC,GACD,OAAO,SAAS2B,uBACd/B,OAA2B;IAE3B,MAAMG,SAAS;WAAIH;KAAQ;IAC3B,OAAO;QACLgC,OAAO,CAACC,UACN9C,OAAO+C,IAAI,CAAC,IAAMrD,YAAYsB,QAAQ8B;IAC1C;AACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@entifix/static",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "A read-only entity repository over records bundled with the build: Mongo's query semantics, records validated against entity metadata before they are served.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -51,12 +51,12 @@
|
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@swc/helpers": "0.5.23",
|
|
54
|
-
"@entifix/
|
|
55
|
-
"@entifix/
|
|
54
|
+
"@entifix/business": "0.9.0",
|
|
55
|
+
"@entifix/core": "0.9.0"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
58
|
"effect": "4.0.5",
|
|
59
|
-
"@entifix/testing-unit": "0.
|
|
59
|
+
"@entifix/testing-unit": "0.9.0"
|
|
60
60
|
},
|
|
61
61
|
"peerDependencies": {
|
|
62
62
|
"effect": "^4.0.5"
|