Skip to content

Commit 5ca314a

Browse files
huangyiireneclaude
andauthored
docs(spec): publicSharing.enabled is a standing policy held at redemption; resolveToken names the switched-off block among its null causes (#16196)
* docs(spec): publicSharing.enabled is a standing policy held at redemption; resolveToken names the switched-off block The TSDoc above `publicSharing.enabled` described only the mint half ("no share links can be issued"). The switch is a standing policy re-read on every redemption, so with it off no existing link resolves either — links minted while it was on, and links minted through the system-context / `permissive` mint bypass alike. The comment now states that, in the shape the sibling `eligibility` predicate's prose uses. `IShareLinkService.resolveToken`'s `null`-cause list gains the switched-off block in the position the gates run; the contract's design notes gain a matching entry beside the eligibility one, and the `isSystem` mint bypass is marked mint-only. Documentation only: no schema, shape or behaviour change; the `.describe()` string feeding the generated reference is untouched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T6HeZvT9wdSJD1ZxJb5Eno * docs(spec): changeset states where the corrected TSDoc reaches consumers, as measured on the built package The contract file's new lines all survive into dist/contracts/index.d.ts; the object.zod.ts property comment reaches no .d.ts and ships through the directly published source file and the data sourcemap. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T6HeZvT9wdSJD1ZxJb5Eno --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 99261a7 commit 5ca314a

3 files changed

Lines changed: 52 additions & 9 deletions

File tree

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
"@objectstack/spec": patch
3+
---
4+
5+
Document `publicSharing.enabled` as the standing policy it is, and name the switched-off block among `resolveToken`'s `null` causes.
6+
7+
The TSDoc above `publicSharing.enabled` read "when false, no share links can be issued for this object" — true, but only the mint half. Since the switch became a standing policy held at every redemption, a block that is off also stops every existing link on it from resolving: links minted while it was on, and links minted through the system-context / `permissive` mint bypass alike. Re-enabling the block serves them again; no row moves. The comment now says so, in the shape the sibling `eligibility` predicate's prose already uses.
8+
9+
`IShareLinkService.resolveToken` enumerated the causes of its undifferentiated `null` — unknown, revoked, expired, audience, password, record gone, ineligible — without the switched-off block, so an implementer reading the list to enumerate refusal causes got an incomplete set. The list now carries it, in the position the gates run; the contract's design notes gain a matching entry beside the eligibility one, and the `isSystem` mint bypass is marked as mint-only.
10+
11+
Documentation only: no schema, shape or behaviour change, and the `.describe()` string that feeds the generated reference is untouched. Where the corrected text reaches consumers, measured on the built package: every new line in `share-link-service.ts` ships in the published `dist/contracts/index.d.ts` (the interface-member docs and the module design notes both survive the declaration bundle); the `object.zod.ts` property comment reaches no `.d.ts` (the schema's declaration is an inferred type) and ships through the source file `@objectstack/spec` publishes directly (`src/**/*.zod.ts`) and through `dist/data/index.js.map`.

packages/spec/src/contracts/share-link-service.ts

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,20 @@
4646
* predicate that cannot be evaluated refuses. The redemption refusal is
4747
* the undifferentiated `null` documented on {@link
4848
* IShareLinkService.resolveToken}.
49+
*
50+
* 7. **`publicSharing.enabled` is a STANDING policy too, not a mint-time
51+
* switch (#14033).** Note 2 is the mint half only. Implementations
52+
* re-read the object's CURRENT block on every `resolveToken`, and a
53+
* block that is off refuses every token on it — those minted while it
54+
* was on, and those minted under the system-context / `permissive` mint
55+
* bypass alike: redemption is an anonymous act, and how the row got
56+
* there buys it nothing. Retroactive on deploy, as note 6 was. Not a
57+
* revocation: no row moves, and re-enabling the block serves them again.
58+
* Off ⇒ nothing inside the block is evaluated (the predicate is not run,
59+
* the redaction set is not computed); on ⇒ the sibling keys keep the
60+
* redemption-time behaviour of note 6. The refusal is the same
61+
* undifferentiated `null` documented on {@link
62+
* IShareLinkService.resolveToken}.
4963
*/
5064

5165
import type { ExecutionContext } from '../kernel/execution-context.zod.js';
@@ -183,7 +197,8 @@ export interface ShareLinkExecutionContext {
183197
*
184198
* Implementations MUST treat `context.isSystem === true` as a bypass
185199
* (skip the per-object opt-in check) so platform bootstrappers can seed
186-
* demo links.
200+
* demo links. That bypass is MINT-only: a link seeded this way is governed
201+
* at redemption like any other (design note 7, #14033).
187202
*
188203
* ## The context every method takes (#6206 ruling, #6430)
189204
*
@@ -231,16 +246,18 @@ export interface IShareLinkService {
231246
* `last_used_at` as a side effect of a SUCCESSFUL resolution only.
232247
*
233248
* Returns `null` when the token does not exist, is revoked, is expired,
234-
* fails the audience or password gate, names a record that no longer exists
235-
* (#5190), or names a record that no longer satisfies the object's
236-
* `publicSharing.eligibility` predicate (#13608).
249+
* fails the audience or password gate, names an object whose
250+
* `publicSharing.enabled` switch is off — the block absent or disabled,
251+
* however the link was minted (#14033, design note 7) — names a record that
252+
* no longer exists (#5190), or names a record that no longer satisfies the
253+
* object's `publicSharing.eligibility` predicate (#13608).
237254
*
238255
* ⛔ That single `null` is the contract, not an implementation detail. The
239256
* caller of this method may hold nothing but a token, and distinguishing
240-
* "does not exist" from "revoked" from "no longer eligible" for such a
241-
* caller is an existence oracle. Implementations MUST NOT return a
242-
* distinguishable answer per reason, and MUST NOT throw one either; the
243-
* readable reason belongs in the server-side log.
257+
* "does not exist" from "revoked" from "switched off" from "no longer
258+
* eligible" for such a caller is an existence oracle. Implementations MUST
259+
* NOT return a distinguishable answer per reason, and MUST NOT throw one
260+
* either; the readable reason belongs in the server-side log.
244261
*
245262
* @param token raw token from the URL / cookie
246263
* @param probe contextual gates the caller has already evaluated

packages/spec/src/data/object.zod.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2275,7 +2275,22 @@ const ObjectSchemaBase = strictObject(
22752275
'external access is that key, one level up.',
22762276
},
22772277
}, {
2278-
/** Master switch. When false (default), no share links can be issued for this object. */
2278+
/**
2279+
* Master switch — a STANDING policy held at every redemption, not a
2280+
* mint-time check (#14033; the same shape as the `eligibility` predicate
2281+
* below, #13608).
2282+
*
2283+
* When false (default), no share links can be issued for this object AND
2284+
* no share link on it resolves: `resolveToken` re-reads this switch on
2285+
* every redemption, so links minted while it was on stop serving the
2286+
* moment it is turned off — links minted through the system-context /
2287+
* `permissive` mint bypass included (redemption is an anonymous act; how
2288+
* the row got there buys it nothing). Not a revocation: no row moves, and
2289+
* re-enabling the block serves them again. Off ⇒ nothing inside this block
2290+
* is evaluated; on ⇒ the sibling keys apply at redemption. The refusal is
2291+
* the undifferentiated `null` documented on `IShareLinkService.resolveToken`
2292+
* (`contracts/share-link-service.ts`).
2293+
*/
22792294
enabled: z.boolean().default(false).describe('Allow records of this object to be published via share link'),
22802295
/**
22812296
* Audiences the platform will accept when issuing a link.

0 commit comments

Comments
 (0)