@@ -198,27 +198,6 @@ export function createMetaDomain(deps: DomainHandlerDeps): DomainRoute {
198198 } ;
199199}
200200
201- /**
202- * [#12702 · #20320] May this caller SAVE `:type/:name` on THIS transport? The
203- * admission of the `PUT` branch below, spelled ONCE: that branch asks it, and
204- * so does the `?state=draft` read's author exemption (ruling 5856774816,
205- * item 1: 「whoever can save it must see it whole, or a save drops entries
206- * silently」), which `MetaReadGateCaller.mayWriteItem` says must be the
207- * transport's own save-door answer. A second spelling at the read could drift
208- * from the door it stands for. `canonicalType` is the folded segment and
209- * `activeOrganizationId` the one resolution the caller already made, so
210- * authorization and scope read one value (#8919).
211- */
212- function metaSaveVerdict ( ec : any , canonicalType : string , activeOrganizationId : string | undefined ) {
213- return metaWriteCapabilityVerdict ( {
214- isSystem : ec ?. isSystem === true ,
215- systemPermissions : ec ?. systemPermissions ,
216- canonicalType,
217- activeOrganizationId,
218- operation : 'save' ,
219- } ) ;
220- }
221-
222201/**
223202 * [#20320] The READ route shape this domain would serve `parts` with, as the
224203 * shared `isPublicAudienceRead` names it — or `undefined` for every shape that
@@ -379,8 +358,8 @@ const NAV_PRUNE_LOGGED = new Set<string>();
379358 *
380359 * [#20320] `mayWriteItem` — handed in only by a door whose policy honours the
381360 * author exemption (the `?state=draft` read): this transport's own save-door
382- * answer ({@link metaSaveVerdict }), carried on a COPY of the context — the
383- * same object serves every other consumer of this request.
361+ * answer (`saveVerdict` in {@link handleMetadataRequest }), carried on a COPY of
362+ * the context — the same object serves every other consumer of this request.
384363 */
385364function metaItemReadGateSources (
386365 deps : DomainHandlerDeps ,
@@ -530,6 +509,9 @@ async function answerMetaList(
530509 }
531510}
532511
512+ /** [#20320] This transport's save-door admission of `:type/:name` — see `saveVerdict` in {@link handleMetadataRequest}. */
513+ type MetaSaveVerdict = ( canonicalType : string , activeOrganizationId : string | undefined ) => { allowed : boolean } ;
514+
533515/**
534516 * [#20320] `GET /meta/:type/:name?state=draft` for a caller who may read
535517 * drafts — the pending draft ROW (ADR-0033), answered exactly as `RestServer`'s
@@ -543,9 +525,9 @@ async function answerMetaList(
543525 * never the published item;
544526 * - the per-caller gate under `STORED_VERSION_DOOR_POLICY` — the constant
545527 * `RestServer`'s draft branch runs (#20290): per-caller arms only, and an
546- * app WHOLE for a caller this transport's save door admits
547- * ({ @link metaSaveVerdict} , carried as `mayWriteItem`), pruned per caller
548- * for everyone else;
528+ * app WHOLE for a caller this transport's save door admits (`saveVerdict`,
529+ * the `PUT` branch's own admission , carried as `mayWriteItem`), pruned per
530+ * caller for everyone else;
549531 * - the ADR-0106 mask for an object, as every object exit here runs it.
550532 *
551533 * Only an admitted caller arrives: the switch is declared with
@@ -558,6 +540,7 @@ async function readPendingDraft(
558540 name : string ,
559541 packageId : string | undefined ,
560542 previewDrafts : boolean ,
543+ saveVerdict : MetaSaveVerdict ,
561544) : Promise < HttpDispatcherResult > {
562545 const singularType = pluralToSingular ( type ) ;
563546 const protocol = await resolveProtocol ( deps , context ) ;
@@ -575,9 +558,7 @@ async function readPendingDraft(
575558 }
576559 if ( envelope ?. item == null ) return { handled : true , response : deps . error ( 'Not found' , 404 ) } ;
577560
578- const mayWriteItem = metaSaveVerdict (
579- context . executionContext , canonicalMetaUrlType ( type ) , activeOrganizationId ,
580- ) . allowed ;
561+ const mayWriteItem = saveVerdict ( canonicalMetaUrlType ( type ) , activeOrganizationId ) . allowed ;
581562 const gated = await gateMetaItemDocument (
582563 deps , context , protocol , singularType , name , envelope . item , STORED_VERSION_DOOR_POLICY , mayWriteItem ,
583564 ) ;
@@ -851,6 +832,27 @@ export async function handleMetadataRequest(deps: DomainHandlerDeps, path: strin
851832 // Extract optional package filter from query string
852833 const packageId = query ?. package || undefined ;
853834
835+ // [#12702 · #20320] May this caller SAVE `:type/:name` on THIS
836+ // transport? The admission of the `PUT` branch below, spelled ONCE:
837+ // that branch asks it, and so does the `?state=draft` read's author
838+ // exemption (ruling 5856774816, item 1: 「whoever can save it must see
839+ // it whole, or a save drops entries silently」), which
840+ // `MetaReadGateCaller.mayWriteItem` says must be the transport's own
841+ // save-door answer. A second spelling at the read could drift from the
842+ // door it stands for. `canonicalType` is the folded segment and
843+ // `activeOrganizationId` the one resolution each caller already made,
844+ // so authorization and scope read one value.
845+ const saveVerdict : MetaSaveVerdict = ( canonicalType , activeOrganizationId ) => {
846+ const ec : any = _context . executionContext ;
847+ return metaWriteCapabilityVerdict ( {
848+ isSystem : ec ?. isSystem === true ,
849+ systemPermissions : ec ?. systemPermissions ,
850+ canonicalType,
851+ activeOrganizationId,
852+ operation : 'save' ,
853+ } ) ;
854+ } ;
855+
854856 // PUT /metadata/:type/:name (Save)
855857 //
856858 // [#8842] The condition is the METHOD alone. It used to be
@@ -910,14 +912,13 @@ export async function handleMetadataRequest(deps: DomainHandlerDeps, path: strin
910912 // single-resolution shape the REST doors carry, #8919). Resolving
911913 // it is a session read, not a protocol probe: the 403-vs-501
912914 // discipline above is untouched.
913- const ec : any = _context . executionContext ;
914915 // [#10503] Folded at the boundary, once — the verdict and the
915916 // scope decision below must read the same spelling.
916917 const canonicalType = canonicalMetaUrlType ( type ) ;
917918 const activeOrganizationId = await deps . resolveActiveOrganizationId ( _context ) ;
918- // [#20320] Spelled once ({ @link metaSaveVerdict} ), because the
919+ // [#20320] Spelled once (`saveVerdict` above ), because the
919920 // `?state=draft` read's author exemption asks this same question.
920- const verdict = metaSaveVerdict ( ec , canonicalType , activeOrganizationId ) ;
921+ const verdict = saveVerdict ( canonicalType , activeOrganizationId ) ;
921922 if ( ! verdict . allowed ) {
922923 // `deps.error(msg, 403)` derives the code from the status —
923924 // `PERMISSION_DENIED`, this transport's pinned spelling.
@@ -1136,7 +1137,7 @@ export async function handleMetadataRequest(deps: DomainHandlerDeps, path: strin
11361137 && mayReadPendingDrafts ( _context . executionContext ) ;
11371138
11381139 try {
1139- if ( isDraftRead ) return await readPendingDraft ( deps , _context , type , name , packageId , previewDrafts ) ;
1140+ if ( isDraftRead ) return await readPendingDraft ( deps , _context , type , name , packageId , previewDrafts , saveVerdict ) ;
11401141
11411142 // Try specific calls based on type
11421143 if ( type === 'objects' || type === 'object' ) {
0 commit comments