@haverstack/core 0.33.0 → 0.36.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 +1 -1
- package/dist/access.d.ts +39 -34
- package/dist/access.d.ts.map +1 -1
- package/dist/access.js +94 -68
- package/dist/access.js.map +1 -1
- package/dist/changes.d.ts +74 -58
- package/dist/changes.d.ts.map +1 -1
- package/dist/changes.js +115 -101
- package/dist/changes.js.map +1 -1
- package/dist/combine.d.ts.map +1 -1
- package/dist/combine.js +7 -0
- package/dist/combine.js.map +1 -1
- package/dist/grants.d.ts +78 -33
- package/dist/grants.d.ts.map +1 -1
- package/dist/grants.js +164 -72
- package/dist/grants.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/query-validation.d.ts +30 -1
- package/dist/query-validation.d.ts.map +1 -1
- package/dist/query-validation.js +128 -2
- package/dist/query-validation.js.map +1 -1
- package/dist/record-changes.d.ts +77 -57
- package/dist/record-changes.d.ts.map +1 -1
- package/dist/record-changes.js +153 -89
- package/dist/record-changes.js.map +1 -1
- package/dist/scoped-stack.d.ts +120 -120
- package/dist/scoped-stack.d.ts.map +1 -1
- package/dist/scoped-stack.js +210 -190
- package/dist/scoped-stack.js.map +1 -1
- package/dist/stack.d.ts +178 -168
- package/dist/stack.d.ts.map +1 -1
- package/dist/stack.js +346 -277
- package/dist/stack.js.map +1 -1
- package/dist/testing.d.ts +16 -5
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +124 -52
- package/dist/testing.js.map +1 -1
- package/dist/types.d.ts +155 -113
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +0 -2
- package/dist/validate.js.map +1 -1
- package/dist/wire-record.d.ts.map +1 -1
- package/dist/wire-record.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -66,7 +66,7 @@ await stack.associate(note.id, { kind: 'tag', label: 'favourite' });
|
|
|
66
66
|
// Or change several things at once — one version, one atomic write
|
|
67
67
|
await stack.mutate(note.id, {
|
|
68
68
|
contentPatch: { title: 'Final title' },
|
|
69
|
-
permissions: [{
|
|
69
|
+
permissions: [{ kind: 'anyone', label: 'read' }],
|
|
70
70
|
unlisted: false,
|
|
71
71
|
});
|
|
72
72
|
|
package/dist/access.d.ts
CHANGED
|
@@ -3,10 +3,16 @@
|
|
|
3
3
|
* -------------------------------------------------------
|
|
4
4
|
* Implements the Permissions model from the spec: a pure predicate over a
|
|
5
5
|
* Record's `permissions` field, with no dependency on a transport layer or
|
|
6
|
-
* storage backend. Used by ScopedStack (see stack.ts) to enforce
|
|
7
|
-
* control; exported standalone for callers that want the raw
|
|
6
|
+
* storage backend. Used by ScopedStack (see scoped-stack.ts) to enforce
|
|
7
|
+
* access control; exported standalone for callers that want the raw
|
|
8
|
+
* predicate.
|
|
9
|
+
*
|
|
10
|
+
* A permission set is read as data throughout, never as the type: it can
|
|
11
|
+
* arrive from an import or a foreign server, where no compiler has seen
|
|
12
|
+
* it. Every tier is affirmative, so an element read past its own kind —
|
|
13
|
+
* or carrying no kind at all — confers nothing.
|
|
8
14
|
*/
|
|
9
|
-
import type { Association,
|
|
15
|
+
import type { Association, AuthorityAssociation, DataAssociation, EntityId, RecordId, StackRecord } from './types.js';
|
|
10
16
|
import type { ValidationError } from './validate.js';
|
|
11
17
|
export type AccessMode = 'read' | 'write';
|
|
12
18
|
/** An entity's standing within a `_group` Record's roster. `admin` implies `member` for ACL purposes. */
|
|
@@ -17,64 +23,63 @@ export type GroupRole = 'member' | 'admin';
|
|
|
17
23
|
*/
|
|
18
24
|
export type RecordResolver = (id: RecordId) => Promise<StackRecord | null>;
|
|
19
25
|
/**
|
|
20
|
-
*
|
|
21
|
-
* permissions = owner only; public = anyone reads; entity = direct match;
|
|
22
|
-
* group = walk the _group Record's roster associations. See
|
|
23
|
-
* docs/spec/access-control.md § Record-level permissions.
|
|
26
|
+
* Whether an entity has read or write access to a Record.
|
|
24
27
|
*
|
|
25
28
|
* The entity here is the **subject** — record-level permissions are written
|
|
26
29
|
* about who data is for, never about the software that carried the request.
|
|
27
30
|
* A delegated app's own authority is a separate question, asked of the
|
|
28
|
-
* principal. See docs/spec/access-control.md §
|
|
29
|
-
* subject.
|
|
31
|
+
* principal. See docs/spec/access-control.md § Record-level permissions and
|
|
32
|
+
* § Delegation: principal and subject.
|
|
30
33
|
*/
|
|
31
34
|
export declare function checkAccess(record: StackRecord, subjectEntityId: EntityId | null, ownerEntityId: EntityId | null, mode: AccessMode, resolveRecord: RecordResolver): Promise<boolean>;
|
|
32
35
|
/**
|
|
33
|
-
* Rejects permission
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
+
* Rejects a permission set where some grantee holds `write` with no `read`
|
|
37
|
+
* beside it — the shape checkAccess() refuses to honor. A cross-element
|
|
38
|
+
* invariant, so it is asked of the set the write would *produce*, the same
|
|
39
|
+
* way hasGroupAdmin() reads its post-state below.
|
|
36
40
|
* See docs/spec/access-control.md § Write implies read.
|
|
37
41
|
*/
|
|
38
|
-
export declare function validatePermissions(permissions:
|
|
42
|
+
export declare function validatePermissions(permissions: AuthorityAssociation[] | undefined, path?: string): ValidationError[];
|
|
43
|
+
/**
|
|
44
|
+
* Whether a Record is a roster anything may resolve against, on two counts.
|
|
45
|
+
* The family check: without it an app modelling its own `member` links
|
|
46
|
+
* would turn every record it points a permission at into an ACL. The
|
|
47
|
+
* tombstone check: deleting a Group is how a Group is withdrawn. Shared
|
|
48
|
+
* with the grant path, so the two layers cannot disagree about which Groups
|
|
49
|
+
* still reach anyone. See docs/spec/identity.md § Group.
|
|
50
|
+
*/
|
|
51
|
+
export declare function carriesRoster(record: StackRecord): boolean;
|
|
39
52
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* privileged than `member`, so a matching admin association wins regardless
|
|
43
|
-
* of association order.
|
|
53
|
+
* An entity's role within a `_group` Record's roster. `admin`
|
|
54
|
+
* short-circuits, so a matching admin association wins regardless of order.
|
|
44
55
|
*
|
|
45
|
-
* Reads associations alone and cannot tell whose they are:
|
|
46
|
-
*
|
|
47
|
-
*
|
|
56
|
+
* Reads associations alone and cannot tell whose they are: callers pass
|
|
57
|
+
* carriesRoster() first, or an app's own `member` relationships become a
|
|
58
|
+
* roster.
|
|
48
59
|
*/
|
|
49
|
-
export declare function groupRoleFromAssociations(associations:
|
|
60
|
+
export declare function groupRoleFromAssociations(associations: DataAssociation[] | undefined, entityId: EntityId): GroupRole | null;
|
|
50
61
|
/**
|
|
51
62
|
* Whether a roster carries at least one `admin` — the invariant every write
|
|
52
63
|
* to a `_group` Record must leave standing. Asked of the roster a write
|
|
53
64
|
* would *produce*, never the one it started from: that is what lets an
|
|
54
65
|
* admin remove themselves while another remains, and refuses the same
|
|
55
66
|
* removal when they are the last, without either case naming who is going.
|
|
56
|
-
*
|
|
57
67
|
* Reads associations alone, on the same terms as
|
|
58
|
-
* groupRoleFromAssociations() above
|
|
59
|
-
* the Record is in the `_group` family first. See docs/spec/identity.md
|
|
60
|
-
* § Group.
|
|
68
|
+
* groupRoleFromAssociations() above.
|
|
61
69
|
*/
|
|
62
|
-
export declare function hasGroupAdmin(associations:
|
|
70
|
+
export declare function hasGroupAdmin(associations: DataAssociation[] | undefined): boolean;
|
|
63
71
|
/**
|
|
64
72
|
* Whether one association is an `admin` roster entry. The `entity` scope is
|
|
65
|
-
* what makes
|
|
66
|
-
*
|
|
67
|
-
*
|
|
73
|
+
* what makes it one — a `record` target carrying the same label confers
|
|
74
|
+
* nothing, so it must not count toward the invariant either.
|
|
75
|
+
* See docs/spec/data-model.md § Relationship targets.
|
|
68
76
|
*/
|
|
69
77
|
export declare function isGroupAdminAssociation(association: Association): boolean;
|
|
70
78
|
/**
|
|
71
79
|
* Whether a request is the stack owner acting alone — the tier behind every
|
|
72
80
|
* unconditional owner authority. Being the owner is never sufficient on its
|
|
73
|
-
* own, whichever side of a delegation the owner is on.
|
|
74
|
-
*
|
|
75
|
-
* holding a session and `ScopedStack`, which holds the pair separately, get
|
|
76
|
-
* one answer. See docs/spec/access-control.md § Delegation: principal and
|
|
77
|
-
* subject.
|
|
81
|
+
* own, whichever side of a delegation the owner is on.
|
|
82
|
+
* See docs/spec/access-control.md § Delegation: principal and subject.
|
|
78
83
|
*/
|
|
79
84
|
export declare function isOwnerActingAlone(session: {
|
|
80
85
|
principalId: EntityId | null;
|
package/dist/access.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"access.d.ts","sourceRoot":"","sources":["../src/access.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"access.d.ts","sourceRoot":"","sources":["../src/access.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EACV,WAAW,EACX,oBAAoB,EACpB,eAAe,EACf,QAAQ,EAER,QAAQ,EACR,WAAW,EACZ,MAAM,YAAY,CAAC;AAGpB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,OAAO,CAAC;AAE1C,yGAAyG;AACzG,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE3C;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,EAAE,EAAE,QAAQ,KAAK,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;AAE3E;;;;;;;;GAQG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,WAAW,EACnB,eAAe,EAAE,QAAQ,GAAG,IAAI,EAChC,aAAa,EAAE,QAAQ,GAAG,IAAI,EAC9B,IAAI,EAAE,UAAU,EAChB,aAAa,EAAE,cAAc,GAC5B,OAAO,CAAC,OAAO,CAAC,CAyBlB;AAyCD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,oBAAoB,EAAE,GAAG,SAAS,EAC/C,IAAI,SAAgB,GACnB,eAAe,EAAE,CAanB;AAaD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAE1D;AAED;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,eAAe,EAAE,GAAG,SAAS,EAC3C,QAAQ,EAAE,QAAQ,GACjB,SAAS,GAAG,IAAI,CAalB;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,YAAY,EAAE,eAAe,EAAE,GAAG,SAAS,GAAG,OAAO,CAElF;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAMzE;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE;IAAE,WAAW,EAAE,QAAQ,GAAG,IAAI,CAAC;IAAC,SAAS,EAAE,QAAQ,GAAG,IAAI,CAAA;CAAE,EACrE,aAAa,EAAE,QAAQ,GACtB,OAAO,CAET"}
|
package/dist/access.js
CHANGED
|
@@ -3,25 +3,28 @@
|
|
|
3
3
|
* -------------------------------------------------------
|
|
4
4
|
* Implements the Permissions model from the spec: a pure predicate over a
|
|
5
5
|
* Record's `permissions` field, with no dependency on a transport layer or
|
|
6
|
-
* storage backend. Used by ScopedStack (see stack.ts) to enforce
|
|
7
|
-
* control; exported standalone for callers that want the raw
|
|
6
|
+
* storage backend. Used by ScopedStack (see scoped-stack.ts) to enforce
|
|
7
|
+
* access control; exported standalone for callers that want the raw
|
|
8
|
+
* predicate.
|
|
9
|
+
*
|
|
10
|
+
* A permission set is read as data throughout, never as the type: it can
|
|
11
|
+
* arrive from an import or a foreign server, where no compiler has seen
|
|
12
|
+
* it. Every tier is affirmative, so an element read past its own kind —
|
|
13
|
+
* or carrying no kind at all — confers nothing.
|
|
8
14
|
*/
|
|
9
15
|
import { SYSTEM_TYPES } from './types.js';
|
|
16
|
+
import { granteeEqual } from './record-changes.js';
|
|
10
17
|
import { baseIdOf } from './schema.js';
|
|
11
18
|
/**
|
|
12
|
-
*
|
|
13
|
-
* permissions = owner only; public = anyone reads; entity = direct match;
|
|
14
|
-
* group = walk the _group Record's roster associations. See
|
|
15
|
-
* docs/spec/access-control.md § Record-level permissions.
|
|
19
|
+
* Whether an entity has read or write access to a Record.
|
|
16
20
|
*
|
|
17
21
|
* The entity here is the **subject** — record-level permissions are written
|
|
18
22
|
* about who data is for, never about the software that carried the request.
|
|
19
23
|
* A delegated app's own authority is a separate question, asked of the
|
|
20
|
-
* principal. See docs/spec/access-control.md §
|
|
21
|
-
* subject.
|
|
24
|
+
* principal. See docs/spec/access-control.md § Record-level permissions and
|
|
25
|
+
* § Delegation: principal and subject.
|
|
22
26
|
*/
|
|
23
27
|
export async function checkAccess(record, subjectEntityId, ownerEntityId, mode, resolveRecord) {
|
|
24
|
-
// Owner always has full access.
|
|
25
28
|
if (subjectEntityId && subjectEntityId === ownerEntityId)
|
|
26
29
|
return true;
|
|
27
30
|
const perms = record.permissions;
|
|
@@ -29,77 +32,106 @@ export async function checkAccess(record, subjectEntityId, ownerEntityId, mode,
|
|
|
29
32
|
if (!perms || perms.length === 0)
|
|
30
33
|
return false;
|
|
31
34
|
for (const p of perms) {
|
|
32
|
-
if (p
|
|
33
|
-
|
|
34
|
-
if (p.access === 'entity' && p.entityId === subjectEntityId) {
|
|
35
|
-
if (entryConveys(p, mode))
|
|
36
|
-
return true;
|
|
37
|
-
}
|
|
38
|
-
if (p.access === 'group' && subjectEntityId) {
|
|
39
|
-
const role = await resolveGroupRole(p.groupId, subjectEntityId, resolveRecord);
|
|
40
|
-
const satisfiesRole = p.role === 'admin' ? role === 'admin' : role !== null;
|
|
41
|
-
if (satisfiesRole && entryConveys(p, mode))
|
|
35
|
+
if (p?.kind === 'anyone') {
|
|
36
|
+
if (mode === 'read' && isWorldRead(p))
|
|
42
37
|
return true;
|
|
38
|
+
continue;
|
|
43
39
|
}
|
|
40
|
+
if (p?.kind !== 'permission' || !p.grantee)
|
|
41
|
+
continue;
|
|
42
|
+
if (p.label !== mode)
|
|
43
|
+
continue;
|
|
44
|
+
// The write bit is inert without read alongside it: the mutate surface
|
|
45
|
+
// hands back the record it wrote and opens its whole history, so a
|
|
46
|
+
// write element standing alone would withhold nothing. Refused at the
|
|
47
|
+
// write by validatePermissions() and again here.
|
|
48
|
+
// See docs/spec/access-control.md § Write implies read.
|
|
49
|
+
if (mode === 'write' && !holdsRead(perms, p.grantee))
|
|
50
|
+
continue;
|
|
51
|
+
if (await granteeCovers(p.grantee, subjectEntityId, resolveRecord))
|
|
52
|
+
return true;
|
|
44
53
|
}
|
|
45
54
|
return false;
|
|
46
55
|
}
|
|
47
56
|
/**
|
|
48
|
-
* Whether
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* array can also arrive from an import or a foreign server. See
|
|
54
|
-
* docs/spec/access-control.md § Write implies read.
|
|
57
|
+
* Whether the set conveys read to this grantee — a `read` of its own, or
|
|
58
|
+
* an `anyone` that already reaches everyone. Demanding a second, redundant
|
|
59
|
+
* `read` beside the write would refuse a set that withholds nothing;
|
|
60
|
+
* revoking the `anyone` is what the invariant catches instead.
|
|
61
|
+
* See docs/spec/access-control.md § Write implies read.
|
|
55
62
|
*/
|
|
56
|
-
function
|
|
57
|
-
return
|
|
63
|
+
function holdsRead(permissions, grantee) {
|
|
64
|
+
return permissions.some((p) => p?.kind === 'anyone'
|
|
65
|
+
? isWorldRead(p)
|
|
66
|
+
: p?.kind === 'permission' && p.label === 'read' && granteeEqual(p.grantee, grantee));
|
|
58
67
|
}
|
|
59
68
|
/**
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
69
|
+
* Whether an `anyone` element reaches the world. `read` is the only bit it
|
|
70
|
+
* can carry, so one labelled otherwise names no reach — refused at the
|
|
71
|
+
* write by validateAssociation() and read as nothing here.
|
|
72
|
+
*/
|
|
73
|
+
function isWorldRead(association) {
|
|
74
|
+
return association.label === 'read';
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Whether a grantee reaches this subject. `role: 'member'` is the wider
|
|
78
|
+
* set — an admin satisfies it — and `role: 'admin'` the narrower.
|
|
79
|
+
*/
|
|
80
|
+
async function granteeCovers(grantee, subjectEntityId, resolveRecord) {
|
|
81
|
+
if (!subjectEntityId)
|
|
82
|
+
return false;
|
|
83
|
+
if (grantee.scope === 'entity')
|
|
84
|
+
return grantee.entityId === subjectEntityId;
|
|
85
|
+
const role = await resolveGroupRole(grantee.groupId, subjectEntityId, resolveRecord);
|
|
86
|
+
return grantee.role === 'admin' ? role === 'admin' : role !== null;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Rejects a permission set where some grantee holds `write` with no `read`
|
|
90
|
+
* beside it — the shape checkAccess() refuses to honor. A cross-element
|
|
91
|
+
* invariant, so it is asked of the set the write would *produce*, the same
|
|
92
|
+
* way hasGroupAdmin() reads its post-state below.
|
|
63
93
|
* See docs/spec/access-control.md § Write implies read.
|
|
64
94
|
*/
|
|
65
95
|
export function validatePermissions(permissions, path = 'permissions') {
|
|
96
|
+
const list = permissions ?? [];
|
|
66
97
|
const errors = [];
|
|
67
|
-
|
|
68
|
-
if (p.
|
|
98
|
+
list.forEach((p, i) => {
|
|
99
|
+
if (p?.kind !== 'permission' || p.label !== 'write')
|
|
69
100
|
return;
|
|
70
|
-
if (
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
101
|
+
if (holdsRead(list, p.grantee))
|
|
102
|
+
return;
|
|
103
|
+
errors.push({
|
|
104
|
+
path: `${path}[${i}]`,
|
|
105
|
+
message: 'write requires read: a write-holder reaches the record and its history through the mutate surface, so a `write` element with no `read` for the same grantee withholds nothing',
|
|
106
|
+
});
|
|
76
107
|
});
|
|
77
108
|
return errors;
|
|
78
109
|
}
|
|
79
|
-
/**
|
|
80
|
-
* Resolve a role from the `_group` Record a permission's `groupId` names.
|
|
81
|
-
* Only a real `_group` Record carries a roster: without the family check
|
|
82
|
-
* any Record's relationship associations would serve as one, so an app
|
|
83
|
-
* modelling its own `member` links would silently turn every record it
|
|
84
|
-
* points a permission at into an ACL. The same rule the grant path applies
|
|
85
|
-
* (see resolveGroupRoleMemoized in stack.ts). See
|
|
86
|
-
* docs/spec/access-control.md § Record-level permissions.
|
|
87
|
-
*/
|
|
110
|
+
/** Resolve a role from the `_group` Record a permission's `groupId` names. */
|
|
88
111
|
async function resolveGroupRole(groupRecordId, entityId, resolveRecord) {
|
|
89
112
|
const group = await resolveRecord(groupRecordId);
|
|
90
|
-
if (!group ||
|
|
113
|
+
if (!group || !carriesRoster(group))
|
|
91
114
|
return null;
|
|
92
115
|
return groupRoleFromAssociations(group.associations, entityId);
|
|
93
116
|
}
|
|
94
117
|
/**
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
118
|
+
* Whether a Record is a roster anything may resolve against, on two counts.
|
|
119
|
+
* The family check: without it an app modelling its own `member` links
|
|
120
|
+
* would turn every record it points a permission at into an ACL. The
|
|
121
|
+
* tombstone check: deleting a Group is how a Group is withdrawn. Shared
|
|
122
|
+
* with the grant path, so the two layers cannot disagree about which Groups
|
|
123
|
+
* still reach anyone. See docs/spec/identity.md § Group.
|
|
124
|
+
*/
|
|
125
|
+
export function carriesRoster(record) {
|
|
126
|
+
return baseIdOf(record.typeId) === SYSTEM_TYPES.GROUP && !record.deletedAt;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* An entity's role within a `_group` Record's roster. `admin`
|
|
130
|
+
* short-circuits, so a matching admin association wins regardless of order.
|
|
99
131
|
*
|
|
100
|
-
* Reads associations alone and cannot tell whose they are:
|
|
101
|
-
*
|
|
102
|
-
*
|
|
132
|
+
* Reads associations alone and cannot tell whose they are: callers pass
|
|
133
|
+
* carriesRoster() first, or an app's own `member` relationships become a
|
|
134
|
+
* roster.
|
|
103
135
|
*/
|
|
104
136
|
export function groupRoleFromAssociations(associations, entityId) {
|
|
105
137
|
let role = null;
|
|
@@ -121,20 +153,17 @@ export function groupRoleFromAssociations(associations, entityId) {
|
|
|
121
153
|
* would *produce*, never the one it started from: that is what lets an
|
|
122
154
|
* admin remove themselves while another remains, and refuses the same
|
|
123
155
|
* removal when they are the last, without either case naming who is going.
|
|
124
|
-
*
|
|
125
156
|
* Reads associations alone, on the same terms as
|
|
126
|
-
* groupRoleFromAssociations() above
|
|
127
|
-
* the Record is in the `_group` family first. See docs/spec/identity.md
|
|
128
|
-
* § Group.
|
|
157
|
+
* groupRoleFromAssociations() above.
|
|
129
158
|
*/
|
|
130
159
|
export function hasGroupAdmin(associations) {
|
|
131
160
|
return (associations ?? []).some(isGroupAdminAssociation);
|
|
132
161
|
}
|
|
133
162
|
/**
|
|
134
163
|
* Whether one association is an `admin` roster entry. The `entity` scope is
|
|
135
|
-
* what makes
|
|
136
|
-
*
|
|
137
|
-
*
|
|
164
|
+
* what makes it one — a `record` target carrying the same label confers
|
|
165
|
+
* nothing, so it must not count toward the invariant either.
|
|
166
|
+
* See docs/spec/data-model.md § Relationship targets.
|
|
138
167
|
*/
|
|
139
168
|
export function isGroupAdminAssociation(association) {
|
|
140
169
|
return (association.kind === 'relationship' &&
|
|
@@ -144,11 +173,8 @@ export function isGroupAdminAssociation(association) {
|
|
|
144
173
|
/**
|
|
145
174
|
* Whether a request is the stack owner acting alone — the tier behind every
|
|
146
175
|
* unconditional owner authority. Being the owner is never sufficient on its
|
|
147
|
-
* own, whichever side of a delegation the owner is on.
|
|
148
|
-
*
|
|
149
|
-
* holding a session and `ScopedStack`, which holds the pair separately, get
|
|
150
|
-
* one answer. See docs/spec/access-control.md § Delegation: principal and
|
|
151
|
-
* subject.
|
|
176
|
+
* own, whichever side of a delegation the owner is on.
|
|
177
|
+
* See docs/spec/access-control.md § Delegation: principal and subject.
|
|
152
178
|
*/
|
|
153
179
|
export function isOwnerActingAlone(session, ownerEntityId) {
|
|
154
180
|
return session.principalId === ownerEntityId && session.subjectId === ownerEntityId;
|
package/dist/access.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"access.js","sourceRoot":"","sources":["../src/access.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"access.js","sourceRoot":"","sources":["../src/access.ts"],"names":[],"mappings":"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"}
|
package/dist/changes.d.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* property of one function here, not a convention every emission site has
|
|
14
14
|
* to remember. See docs/spec/events.md.
|
|
15
15
|
*/
|
|
16
|
-
import type {
|
|
16
|
+
import type { AssociationChange, ChangeActor, JournalEntryInput, ChangeFilter, ChangeKind, ChangeOp, RecordChange, StackRecord, SubscribeOptions, Unsubscribe } from './types.js';
|
|
17
17
|
/**
|
|
18
18
|
* What the emitter knows: the envelope, plus the record it describes.
|
|
19
19
|
* `record` is the filtering input and is never delivered unprojected —
|
|
@@ -46,15 +46,10 @@ export type EmittedChange = {
|
|
|
46
46
|
export declare function matchesFilter(emitted: EmittedChange, filter?: ChangeFilter): boolean;
|
|
47
47
|
/**
|
|
48
48
|
* Whether an emission passes the unlisted-enumeration boundary for one
|
|
49
|
-
* subscription.
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* otherwise failing this very check. Without that exception a subscriber
|
|
54
|
-
* who already knows the record would never be told to drop it. Every other
|
|
55
|
-
* transition (create-unlisted, an edit while already unlisted, a purge of
|
|
56
|
-
* a record that was never listed, the `list` transition itself) needs no
|
|
57
|
-
* special-casing: it falls out of checking the record's current state.
|
|
49
|
+
* subscription. The `unlist` transition is the one exception to the
|
|
50
|
+
* default exclusion: without it a subscriber who already knows the record
|
|
51
|
+
* would never be told to drop it. Every other transition falls out of
|
|
52
|
+
* checking the record's current state.
|
|
58
53
|
* See docs/spec/events.md § The unlisted transition.
|
|
59
54
|
*/
|
|
60
55
|
export declare function passesUnlistedBoundary(emitted: EmittedChange, includeUnlisted?: boolean): boolean;
|
|
@@ -67,9 +62,7 @@ export declare function passesUnlistedBoundary(emitted: EmittedChange, includeUn
|
|
|
67
62
|
* nothing.
|
|
68
63
|
*
|
|
69
64
|
* The record rides by reference, shared across every frame projected from
|
|
70
|
-
* one emission: handlers are contractually read-only over it
|
|
71
|
-
* per subscriber would charge every consumer for a defect none of them
|
|
72
|
-
* have. See docs/spec/events.md § The event shape.
|
|
65
|
+
* one emission: handlers are contractually read-only over it.
|
|
73
66
|
*/
|
|
74
67
|
export declare function emitted(emission: EmittedChange, includeRecords: boolean): RecordChange;
|
|
75
68
|
/**
|
|
@@ -87,12 +80,7 @@ export declare abstract class Subscription {
|
|
|
87
80
|
abstract accept(emission: EmittedChange): void;
|
|
88
81
|
get isClosed(): boolean;
|
|
89
82
|
close(): void;
|
|
90
|
-
/**
|
|
91
|
-
* Hand a frame to the handler. A handler that throws cannot fail the
|
|
92
|
-
* write it is being told about — the write is already durable — so the
|
|
93
|
-
* error goes to onError, or is rethrown asynchronously so that it
|
|
94
|
-
* surfaces as an unhandled error rather than vanishing.
|
|
95
|
-
*/
|
|
83
|
+
/** Hand a frame to the handler; anything it throws goes to reportError(). */
|
|
96
84
|
protected deliver(emission: EmittedChange): void;
|
|
97
85
|
protected reportError(err: unknown): void;
|
|
98
86
|
}
|
|
@@ -117,42 +105,70 @@ export declare class RelayDelivery {
|
|
|
117
105
|
close(): void;
|
|
118
106
|
}
|
|
119
107
|
/**
|
|
120
|
-
*
|
|
108
|
+
* One change, held between the two halves it is written in.
|
|
121
109
|
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
* purge also drops `parentId` and the create-time `appId`: the frame says
|
|
131
|
-
* that a record of some type was destroyed, and nothing further about
|
|
132
|
-
* whose it was. See docs/spec/events.md § Purged records carry nothing.
|
|
110
|
+
* A change lands twice: the durable half travels into the adapter's
|
|
111
|
+
* transaction ahead of the write, and the live half is built from the
|
|
112
|
+
* record that write produced. Naming the change once — its ops, its kind,
|
|
113
|
+
* its actor, what it moved — is what makes "the entry set is the event
|
|
114
|
+
* set" true by construction rather than by convention. No verb can journal
|
|
115
|
+
* one thing and announce another, and a verb that writes only one half has
|
|
116
|
+
* to say so where it does it. See docs/spec/journal.md § The entry set is
|
|
117
|
+
* the event set.
|
|
133
118
|
*/
|
|
134
|
-
export declare
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
119
|
+
export declare class PendingChange {
|
|
120
|
+
private readonly moved;
|
|
121
|
+
readonly ops: ChangeOp[];
|
|
122
|
+
readonly kind: ChangeKind;
|
|
123
|
+
constructor(ops: ChangeOp | ChangeOp[], moved?: {
|
|
124
|
+
/**
|
|
125
|
+
* The requester behind a write that stamps nothing on the record
|
|
126
|
+
* itself. A version-bumping write stamps `updatedBy`/`updatedVia`,
|
|
127
|
+
* so its emission reads them back instead — see emission() below.
|
|
128
|
+
*/
|
|
129
|
+
actor?: ChangeActor;
|
|
130
|
+
previousParentId?: string | null;
|
|
131
|
+
/**
|
|
132
|
+
* What the write moved, association by association. One list for
|
|
133
|
+
* both halves: the journal takes it as it stands and the feed is
|
|
134
|
+
* flattened out of it, so neither can report an edit the other
|
|
135
|
+
* doesn't.
|
|
136
|
+
*/
|
|
137
|
+
associations?: AssociationChange[];
|
|
138
|
+
});
|
|
139
|
+
/**
|
|
140
|
+
* The durable half, handed to the adapter as `journal` so that it lands
|
|
141
|
+
* in the same write as the mutation it describes.
|
|
142
|
+
*
|
|
143
|
+
* Absent for a purge, which destroys the log an entry would be appended
|
|
144
|
+
* to. The rule lives here rather than at each call site that would
|
|
145
|
+
* otherwise have to remember it. See docs/spec/journal.md § A hard
|
|
146
|
+
* delete destroys the journal.
|
|
147
|
+
*/
|
|
148
|
+
get journal(): JournalEntryInput | undefined;
|
|
149
|
+
/**
|
|
150
|
+
* The live half, built from the record the write produced — for a purge,
|
|
151
|
+
* from the record as it stood immediately before destruction.
|
|
152
|
+
*
|
|
153
|
+
* The actor is read off the record for every version-bumping op, so it
|
|
154
|
+
* agrees with what was persisted by construction. A no-bump op stamps
|
|
155
|
+
* nothing, so the requester travels here instead — reading the record
|
|
156
|
+
* would report whoever's last version-bumping write this is. A purged
|
|
157
|
+
* frame drops `parentId` and the create-time `appId` too: it says a
|
|
158
|
+
* record of some type was destroyed and nothing about whose it was.
|
|
159
|
+
*/
|
|
160
|
+
emission(record: StackRecord, at?: Date): EmittedChange;
|
|
161
|
+
/**
|
|
162
|
+
* The frame's two flat lists, flattened out of the one the journal
|
|
163
|
+
* takes. Present only where non-empty.
|
|
164
|
+
*
|
|
165
|
+
* The data half alone: the `permissions` op is what announces an ACL
|
|
166
|
+
* move, so a subscriber watching tags is never handed the sharing graph
|
|
167
|
+
* as a side effect of the partition sharing one delta.
|
|
168
|
+
* See docs/spec/events.md § The event shape.
|
|
169
|
+
*/
|
|
170
|
+
private associationDeltas;
|
|
171
|
+
}
|
|
156
172
|
/**
|
|
157
173
|
* The subscriber registry. One per `Stack`; `ScopedStack` filters the same
|
|
158
174
|
* stream rather than opening its own, so a scoped view can never observe a
|
|
@@ -166,6 +182,7 @@ export declare class ChangeEmitter {
|
|
|
166
182
|
/** Ends every subscription — a closed stack emits nothing further. */
|
|
167
183
|
closeAll(): void;
|
|
168
184
|
}
|
|
185
|
+
/** The kind each op produces. See docs/spec/events.md § The event shape. */
|
|
169
186
|
export declare const CHANGE_KINDS: Record<ChangeOp, ChangeKind>;
|
|
170
187
|
/**
|
|
171
188
|
* `since` only means something where a relay exists: a stack with no
|
|
@@ -174,11 +191,10 @@ export declare const CHANGE_KINDS: Record<ChangeOp, ChangeKind>;
|
|
|
174
191
|
* caller believe it resumed when it did not, so a stack that cannot honor
|
|
175
192
|
* `since` refuses it rather than ignoring it.
|
|
176
193
|
*
|
|
177
|
-
* Its shape is checked here rather than left to the adapter, so
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
* docs/spec/events.md § Subscribing.
|
|
194
|
+
* Its shape is checked here rather than left to the adapter, so a malformed
|
|
195
|
+
* cursor is the same error whoever is underneath. The value stays opaque:
|
|
196
|
+
* this asks whether it is framable, never what it means.
|
|
197
|
+
* See docs/spec/events.md § Subscribing.
|
|
182
198
|
*/
|
|
183
199
|
export declare function assertSinceUsable(since: string | undefined, relaysChanges: boolean): void;
|
|
184
200
|
//# sourceMappingURL=changes.d.ts.map
|
package/dist/changes.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"changes.d.ts","sourceRoot":"","sources":["../src/changes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EACV,
|
|
1
|
+
{"version":3,"file":"changes.d.ts","sourceRoot":"","sources":["../src/changes.ts"],"names":[],"mappings":"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"}
|