hono-crud 0.13.16 → 0.13.17
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/CHANGELOG.md +21 -0
- package/dist/internal.d.ts +17 -3
- package/dist/internal.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.13.17
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 2314cf6: Push owner-scoped relation includes (`?include=`) down to the adapter query.
|
|
8
|
+
|
|
9
|
+
The owner-scope filter on relation includes ran as a **post-fetch** filter in the
|
|
10
|
+
core orchestrator: related rows were fetched by foreign key, then cross-tenant /
|
|
11
|
+
soft-deleted ones were dropped before the response. Now the resolved scope (tenant
|
|
12
|
+
column + value, soft-delete column) is threaded into each adapter's `fetchRelated`,
|
|
13
|
+
so the filter is pushed into the **WHERE clause** (drizzle / prisma) or the store
|
|
14
|
+
scan (memory) — the disallowed rows are never fetched. The core orchestrator keeps
|
|
15
|
+
its post-fetch `applyRelationScope` as a defense-in-depth net for adapters that
|
|
16
|
+
ignore the scope argument.
|
|
17
|
+
|
|
18
|
+
Adds the internal `RelationFetchScope` type + `resolveFetchScope`; the
|
|
19
|
+
`FetchRelated` / `SyncFetchRelated` types gain an optional 4th `scope` argument
|
|
20
|
+
(backward-compatible — a 3-arg adapter `fetchRelated` stays assignable). Also adds
|
|
21
|
+
a cross-adapter relation-scoping conformance cell (runs on memory + drizzle; a
|
|
22
|
+
named skip on the prisma leg, whose fixed examples schema has no self-relation).
|
|
23
|
+
|
|
3
24
|
## 0.13.16
|
|
4
25
|
|
|
5
26
|
### Patch Changes
|
package/dist/internal.d.ts
CHANGED
|
@@ -55,14 +55,28 @@ declare function getRegisteredCrudResources(app: object): readonly RegisteredCru
|
|
|
55
55
|
|
|
56
56
|
/** A loaded related record (opaque to the orchestrator). */
|
|
57
57
|
type RelatedRecord = Record<string, unknown>;
|
|
58
|
+
/**
|
|
59
|
+
* The push-down form of {@link RelationConfig.scope} for a single request: the
|
|
60
|
+
* concrete column + value an adapter can add to its related-row query (WHERE) so
|
|
61
|
+
* the owner-scope is enforced in SQL instead of by post-fetch filtering. Computed
|
|
62
|
+
* by {@link resolveFetchScope} from the relation `scope` + the request scope.
|
|
63
|
+
*/
|
|
64
|
+
interface RelationFetchScope {
|
|
65
|
+
/** Owner/tenant column on the related table to constrain by equality. */
|
|
66
|
+
tenantField?: string;
|
|
67
|
+
/** The request's tenant id (the equality value for `tenantField`). */
|
|
68
|
+
tenantValue?: unknown;
|
|
69
|
+
/** Soft-delete column on the related table to require `IS NULL`. */
|
|
70
|
+
excludeDeletedField?: string;
|
|
71
|
+
}
|
|
58
72
|
type ResolveRelation<Handle> = (relationConfig: RelationConfig) => Handle | null | PromiseLike<Handle | null>;
|
|
59
|
-
type FetchRelated<Handle> = (handle: Handle, keyField: string, values: unknown[]) => RelatedRecord[] | PromiseLike<RelatedRecord[]>;
|
|
73
|
+
type FetchRelated<Handle> = (handle: Handle, keyField: string, values: unknown[], scope?: RelationFetchScope) => RelatedRecord[] | PromiseLike<RelatedRecord[]>;
|
|
60
74
|
interface RelationLoaderAdapter<Handle> {
|
|
61
75
|
resolveRelation: ResolveRelation<Handle>;
|
|
62
76
|
fetchRelated: FetchRelated<Handle>;
|
|
63
77
|
}
|
|
64
78
|
type SyncResolveRelation<Handle> = (relationConfig: RelationConfig) => Handle | null;
|
|
65
|
-
type SyncFetchRelated<Handle> = (handle: Handle, keyField: string, values: unknown[]) => RelatedRecord[];
|
|
79
|
+
type SyncFetchRelated<Handle> = (handle: Handle, keyField: string, values: unknown[], scope?: RelationFetchScope) => RelatedRecord[];
|
|
66
80
|
interface SyncRelationLoaderAdapter<Handle> {
|
|
67
81
|
resolveRelation: SyncResolveRelation<Handle>;
|
|
68
82
|
fetchRelated: SyncFetchRelated<Handle>;
|
|
@@ -100,4 +114,4 @@ declare function loadRelationsForItemSync<T extends Record<string, unknown>, M e
|
|
|
100
114
|
*/
|
|
101
115
|
declare function withIncludableRelations(itemSchema: ZodObject<ZodRawShape>, meta: MetaInput, allowedIncludes: readonly string[]): ZodObject<ZodRawShape>;
|
|
102
116
|
|
|
103
|
-
export { CrudEndpoints, type FetchRelated, IncludeOptions, MetaInput, type RegisteredCrudResource, type RelatedRecord, RelationConfig, type RelationLoaderAdapter, type ResolveRelation, type SyncFetchRelated, type SyncRelationLoaderAdapter, type SyncResolveRelation, batchLoadRelations, getRegisteredCrudResources, loadRelationsForItem, loadRelationsForItemSync, resolveRelationValueAsync, resolveRelationValueSync, withIncludableRelations };
|
|
117
|
+
export { CrudEndpoints, type FetchRelated, IncludeOptions, MetaInput, type RegisteredCrudResource, type RelatedRecord, RelationConfig, type RelationFetchScope, type RelationLoaderAdapter, type ResolveRelation, type SyncFetchRelated, type SyncRelationLoaderAdapter, type SyncResolveRelation, batchLoadRelations, getRegisteredCrudResources, loadRelationsForItem, loadRelationsForItemSync, resolveRelationValueAsync, resolveRelationValueSync, withIncludableRelations };
|
package/dist/internal.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{a as MemoryTtlStore}from'./chunk-YB6AVUPQ.js';export{da as AggregateEndpoint,V as BatchCreateEndpoint,X as BatchDeleteEndpoint,Y as BatchRestoreEndpoint,W as BatchUpdateEndpoint,Z as BatchUpsertEndpoint,_ as BulkPatchEndpoint,a as CRUD_ROUTES,S as CloneEndpoint,M as CreateEndpoint,Q as DeleteEndpoint,pa as ExportEndpoint,qa as ImportEndpoint,R as ListEndpoint,O as ReadEndpoint,T as RestoreEndpoint,fa as SearchEndpoint,P as UpdateEndpoint,U as UpsertEndpoint,ba as VersionCompareEndpoint,$ as VersionHistoryEndpoint,aa as VersionReadEndpoint,ca as VersionRollbackEndpoint,u as applyUpsertRestore,m as buildCursorPage,ea as computeAggregations,l as decodeCursor,k as encodeCursor,sa as getRegisteredCrudResources,ga as searchInMemory,N as withIncludableRelations}from'./chunk-QWMISKGR.js';import'./chunk-P7HU2KIE.js';import'./chunk-H3VBYIDA.js';import'./chunk-CWQSQUV4.js';import'./chunk-SDNXN7M5.js';import'./chunk-HYXDMJ4K.js';import'./chunk-WBHWKOTP.js';import'./chunk-L5CVVJQH.js';export{p as extractBearerToken}from'./chunk-CTOFQ5RC.js';export{p as OpenAPIRoute,c as assertNever,b as isFilterOperator}from'./chunk-A27HDYSF.js';export{a as getClientIp,b as getUserId,e as isPathIncluded,d as matchAny,c as matchPath}from'./chunk-V7ABUFW5.js';export{b as getLogger}from'./chunk-DMGP7QDL.js';export{a as StorageRegistry,b as createStorageFeature}from'./chunk-5P4RVSHT.js';export{a as getContextVar,b as setContextVar}from'./chunk-TLI3TRUA.js';export{b as ApiException,a as CONTEXT_KEYS,j as ConfigurationException,d as NotFoundException,f as UnauthorizedException}from'./chunk-XR6JRDGX.js';import'./chunk-NWOJZP4P.js';function
|
|
1
|
+
export{a as MemoryTtlStore}from'./chunk-YB6AVUPQ.js';export{da as AggregateEndpoint,V as BatchCreateEndpoint,X as BatchDeleteEndpoint,Y as BatchRestoreEndpoint,W as BatchUpdateEndpoint,Z as BatchUpsertEndpoint,_ as BulkPatchEndpoint,a as CRUD_ROUTES,S as CloneEndpoint,M as CreateEndpoint,Q as DeleteEndpoint,pa as ExportEndpoint,qa as ImportEndpoint,R as ListEndpoint,O as ReadEndpoint,T as RestoreEndpoint,fa as SearchEndpoint,P as UpdateEndpoint,U as UpsertEndpoint,ba as VersionCompareEndpoint,$ as VersionHistoryEndpoint,aa as VersionReadEndpoint,ca as VersionRollbackEndpoint,u as applyUpsertRestore,m as buildCursorPage,ea as computeAggregations,l as decodeCursor,k as encodeCursor,sa as getRegisteredCrudResources,ga as searchInMemory,N as withIncludableRelations}from'./chunk-QWMISKGR.js';import'./chunk-P7HU2KIE.js';import'./chunk-H3VBYIDA.js';import'./chunk-CWQSQUV4.js';import'./chunk-SDNXN7M5.js';import'./chunk-HYXDMJ4K.js';import'./chunk-WBHWKOTP.js';import'./chunk-L5CVVJQH.js';export{p as extractBearerToken}from'./chunk-CTOFQ5RC.js';export{p as OpenAPIRoute,c as assertNever,b as isFilterOperator}from'./chunk-A27HDYSF.js';export{a as getClientIp,b as getUserId,e as isPathIncluded,d as matchAny,c as matchPath}from'./chunk-V7ABUFW5.js';export{b as getLogger}from'./chunk-DMGP7QDL.js';export{a as StorageRegistry,b as createStorageFeature}from'./chunk-5P4RVSHT.js';export{a as getContextVar,b as setContextVar}from'./chunk-TLI3TRUA.js';export{b as ApiException,a as CONTEXT_KEYS,j as ConfigurationException,d as NotFoundException,f as UnauthorizedException}from'./chunk-XR6JRDGX.js';import'./chunk-NWOJZP4P.js';function m(e,t,n){let o=t.scope;if(!o||!n)return e;let{tenantField:r,softDeleteField:l}=o,{tenantId:a,includeDeleted:i}=n,p=r!=null&&a!=null,c=l!=null&&!i;return !p&&!c?e:e.filter(u=>!(p&&u[r]!==a||c&&u[l]!=null))}function y(e,t){let n=e.scope;if(!n||!t)return;let o=n.tenantField!=null&&t.tenantId!=null?n.tenantField:void 0,r=n.softDeleteField!=null&&!t.includeDeleted?n.softDeleteField:void 0;if(!(o==null&&r==null))return {tenantField:o,tenantValue:t.tenantId,excludeDeletedField:r}}async function Se(e,t,n,o){if(!e.length||!o?.relations?.length||!t.model.relations)return e;let r=e.map(l=>({...l}));for(let l of o.relations){let a=t.model.relations[l];if(!a)continue;let i=await n.resolveRelation(a);i!=null&&(r=await Ee[a.type](r,l,a,i,n,o.scope));}return r}function g(e){return async(t,n,o,r,l,a)=>{let i=o.localKey||"id",p=[...new Set(t.map(d=>d[i]).filter(d=>d!=null))];if(p.length===0)return t.map(d=>({...d,[n]:e?null:[]}));let c=y(o,a),u=await l.fetchRelated(r,o.foreignKey,p,c),f=m(u,o,a),s=new Map;for(let d of f){let R=d[o.foreignKey],x=s.get(R);x?x.push(d):s.set(R,[d]);}return t.map(d=>{let R=s.get(d[i])||[];return {...d,[n]:e?R[0]||null:R}})}}function Ce(){return async(e,t,n,o,r,l)=>{let a=n.localKey||"id",i=[...new Set(e.map(s=>s[n.foreignKey]).filter(s=>s!=null))];if(i.length===0)return e.map(s=>({...s,[t]:null}));let p=y(n,l),c=await r.fetchRelated(o,a,i,p),u=m(c,n,l),f=new Map;for(let s of u)f.set(s[a],s);return e.map(s=>({...s,[t]:f.get(s[n.foreignKey])||null}))}}var Ee={hasOne:g(true),hasMany:g(false),belongsTo:Ce()},h={hasOne:e=>e[0]??null,hasMany:e=>e,belongsTo:e=>e[0]??null};function S(e,t){let n=e.localKey||"id";if(e.type==="belongsTo"){let r=t[e.foreignKey];return r==null?null:{gateValue:r,keyField:n}}let o=t[n];return o==null?null:{gateValue:o,keyField:e.foreignKey}}async function C(e,t,n,o,r){let l=h[t.type],a=S(t,e);if(!a)return l([]);let i=m(await o(n,a.keyField,[a.gateValue],y(t,r)),t,r);return l(i)}function E(e,t,n,o,r){let l=h[t.type],a=S(t,e);if(!a)return l([]);let i=m(o(n,a.keyField,[a.gateValue],y(t,r)),t,r);return l(i)}async function Fe(e,t,n,o){if(!o?.relations?.length||!t.model.relations)return e;let r={...e};for(let l of o.relations){let a=t.model.relations[l];if(!a)continue;let i=await n.resolveRelation(a);i!=null&&(r[l]=await C(r,a,i,n.fetchRelated,o.scope));}return r}function ke(e,t,n,o){if(!o?.relations?.length||!t.model.relations)return e;let r={...e};for(let l of o.relations){let a=t.model.relations[l];if(!a)continue;let i=n.resolveRelation(a);i!=null&&(r[l]=E(r,a,i,n.fetchRelated,o.scope));}return r}export{Se as batchLoadRelations,Fe as loadRelationsForItem,ke as loadRelationsForItemSync,C as resolveRelationValueAsync,E as resolveRelationValueSync};
|