@haverstack/core 0.40.0 → 0.42.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 +121 -59
- package/dist/access.d.ts +34 -36
- package/dist/access.d.ts.map +1 -1
- package/dist/access.js +65 -42
- package/dist/access.js.map +1 -1
- package/dist/{combine.d.ts → adapter/combine.d.ts} +8 -1
- package/dist/adapter/combine.d.ts.map +1 -0
- package/dist/{combine.js → adapter/combine.js} +7 -0
- package/dist/adapter/combine.js.map +1 -0
- package/dist/{adapter-entry.d.ts → adapter/index.d.ts} +6 -5
- package/dist/adapter/index.d.ts.map +1 -0
- package/dist/{adapter-entry.js → adapter/index.js} +5 -4
- package/dist/adapter/index.js.map +1 -0
- package/dist/{sort.d.ts → adapter/sort.d.ts} +20 -20
- package/dist/adapter/sort.d.ts.map +1 -0
- package/dist/{sort.js → adapter/sort.js} +37 -27
- package/dist/adapter/sort.js.map +1 -0
- package/dist/associations/identity.d.ts +93 -0
- package/dist/associations/identity.d.ts.map +1 -0
- package/dist/associations/identity.js +198 -0
- package/dist/associations/identity.js.map +1 -0
- package/dist/associations/validation.d.ts +90 -0
- package/dist/associations/validation.d.ts.map +1 -0
- package/dist/associations/validation.js +339 -0
- package/dist/associations/validation.js.map +1 -0
- package/dist/attachment-records.d.ts +48 -0
- package/dist/attachment-records.d.ts.map +1 -0
- package/dist/attachment-records.js +52 -0
- package/dist/attachment-records.js.map +1 -0
- package/dist/collection.d.ts +182 -0
- package/dist/collection.d.ts.map +1 -0
- package/dist/collection.js +407 -0
- package/dist/collection.js.map +1 -0
- package/dist/content-match.d.ts +25 -0
- package/dist/content-match.d.ts.map +1 -0
- package/dist/content-match.js +51 -0
- package/dist/content-match.js.map +1 -0
- package/dist/content-translation.d.ts +42 -0
- package/dist/content-translation.d.ts.map +1 -0
- package/dist/content-translation.js +108 -0
- package/dist/content-translation.js.map +1 -0
- package/dist/did/did.d.ts.map +1 -0
- package/dist/{did.js → did/did.js} +3 -4
- package/dist/did/did.js.map +1 -0
- package/dist/{did-entry.d.ts → did/index.d.ts} +1 -1
- package/dist/did/index.d.ts.map +1 -0
- package/dist/{did-entry.js → did/index.js} +3 -3
- package/dist/did/index.js.map +1 -0
- package/dist/errors.d.ts +36 -63
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +35 -58
- package/dist/errors.js.map +1 -1
- package/dist/grants.d.ts +20 -43
- package/dist/grants.d.ts.map +1 -1
- package/dist/grants.js +71 -134
- package/dist/grants.js.map +1 -1
- package/dist/id.d.ts +13 -31
- package/dist/id.d.ts.map +1 -1
- package/dist/id.js +13 -31
- package/dist/id.js.map +1 -1
- package/dist/index.d.ts +14 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -5
- package/dist/index.js.map +1 -1
- package/dist/install.d.ts +141 -0
- package/dist/install.d.ts.map +1 -0
- package/dist/install.js +219 -0
- package/dist/install.js.map +1 -0
- package/dist/merge.d.ts.map +1 -1
- package/dist/merge.js +5 -11
- package/dist/merge.js.map +1 -1
- package/dist/migration-steps.d.ts +102 -0
- package/dist/migration-steps.d.ts.map +1 -0
- package/dist/migration-steps.js +293 -0
- package/dist/migration-steps.js.map +1 -0
- package/dist/query-validation.d.ts +28 -94
- package/dist/query-validation.d.ts.map +1 -1
- package/dist/query-validation.js +156 -336
- package/dist/query-validation.js.map +1 -1
- package/dist/record-changes.d.ts +2 -117
- package/dist/record-changes.d.ts.map +1 -1
- package/dist/record-changes.js +10 -212
- package/dist/record-changes.js.map +1 -1
- package/dist/schema.d.ts +18 -7
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +59 -30
- package/dist/schema.js.map +1 -1
- package/dist/scoped-stack/authority.d.ts +156 -0
- package/dist/scoped-stack/authority.d.ts.map +1 -0
- package/dist/scoped-stack/authority.js +384 -0
- package/dist/scoped-stack/authority.js.map +1 -0
- package/dist/scoped-stack/scoped-stack.d.ts +338 -0
- package/dist/scoped-stack/scoped-stack.d.ts.map +1 -0
- package/dist/scoped-stack/scoped-stack.js +776 -0
- package/dist/scoped-stack/scoped-stack.js.map +1 -0
- package/dist/scoped-stack/subscription.d.ts +55 -0
- package/dist/scoped-stack/subscription.d.ts.map +1 -0
- package/dist/scoped-stack/subscription.js +110 -0
- package/dist/scoped-stack/subscription.js.map +1 -0
- package/dist/stack/attachments.d.ts +24 -0
- package/dist/stack/attachments.d.ts.map +1 -0
- package/dist/stack/attachments.js +113 -0
- package/dist/stack/attachments.js.map +1 -0
- package/dist/{changes.d.ts → stack/changes.d.ts} +29 -65
- package/dist/stack/changes.d.ts.map +1 -0
- package/dist/{changes.js → stack/changes.js} +32 -66
- package/dist/stack/changes.js.map +1 -0
- package/dist/stack/client.d.ts +271 -0
- package/dist/stack/client.d.ts.map +1 -0
- package/dist/stack/client.js +9 -0
- package/dist/stack/client.js.map +1 -0
- package/dist/stack/identity-bindings.d.ts.map +1 -0
- package/dist/{identity-bindings.js → stack/identity-bindings.js} +8 -14
- package/dist/stack/identity-bindings.js.map +1 -0
- package/dist/stack/install-app.d.ts +22 -0
- package/dist/stack/install-app.d.ts.map +1 -0
- package/dist/stack/install-app.js +260 -0
- package/dist/stack/install-app.js.map +1 -0
- package/dist/stack/integrity.d.ts +94 -0
- package/dist/stack/integrity.d.ts.map +1 -0
- package/dist/stack/integrity.js +268 -0
- package/dist/stack/integrity.js.map +1 -0
- package/dist/{limits.d.ts → stack/limits.d.ts} +2 -5
- package/dist/stack/limits.d.ts.map +1 -0
- package/dist/{limits.js → stack/limits.js} +3 -6
- package/dist/stack/limits.js.map +1 -0
- package/dist/stack/migrations.d.ts +59 -0
- package/dist/stack/migrations.d.ts.map +1 -0
- package/dist/stack/migrations.js +141 -0
- package/dist/stack/migrations.js.map +1 -0
- package/dist/{stack-reads.d.ts → stack/reads.d.ts} +33 -6
- package/dist/stack/reads.d.ts.map +1 -0
- package/dist/{stack-reads.js → stack/reads.js} +49 -7
- package/dist/stack/reads.js.map +1 -0
- package/dist/stack/record-id.d.ts +35 -0
- package/dist/stack/record-id.d.ts.map +1 -0
- package/dist/stack/record-id.js +82 -0
- package/dist/stack/record-id.js.map +1 -0
- package/dist/stack/stack.d.ts +438 -0
- package/dist/stack/stack.d.ts.map +1 -0
- package/dist/stack/stack.js +1366 -0
- package/dist/stack/stack.js.map +1 -0
- package/dist/stack/system-types.d.ts +16 -0
- package/dist/stack/system-types.d.ts.map +1 -0
- package/dist/stack/system-types.js +111 -0
- package/dist/stack/system-types.js.map +1 -0
- package/dist/stack/type-grants.d.ts +20 -0
- package/dist/stack/type-grants.d.ts.map +1 -0
- package/dist/stack/type-grants.js +69 -0
- package/dist/stack/type-grants.js.map +1 -0
- package/dist/stack/write-expectation.d.ts +78 -0
- package/dist/stack/write-expectation.d.ts.map +1 -0
- package/dist/stack/write-expectation.js +71 -0
- package/dist/stack/write-expectation.js.map +1 -0
- package/dist/testing.d.ts +21 -26
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +50 -70
- package/dist/testing.js.map +1 -1
- package/dist/type-handle.d.ts +67 -78
- package/dist/type-handle.d.ts.map +1 -1
- package/dist/type-handle.js +44 -111
- package/dist/type-handle.js.map +1 -1
- package/dist/types/adapter.d.ts +223 -0
- package/dist/types/adapter.d.ts.map +1 -0
- package/dist/types/adapter.js +5 -0
- package/dist/types/adapter.js.map +1 -0
- package/dist/types/associations.d.ts +156 -0
- package/dist/types/associations.d.ts.map +1 -0
- package/dist/types/associations.js +13 -0
- package/dist/types/associations.js.map +1 -0
- package/dist/types/capabilities.d.ts +89 -0
- package/dist/types/capabilities.d.ts.map +1 -0
- package/dist/types/capabilities.js +5 -0
- package/dist/types/capabilities.js.map +1 -0
- package/dist/types/events.d.ts +155 -0
- package/dist/types/events.d.ts.map +1 -0
- package/dist/types/events.js +5 -0
- package/dist/types/events.js.map +1 -0
- package/dist/types/ids.d.ts +43 -0
- package/dist/types/ids.d.ts.map +1 -0
- package/dist/types/ids.js +5 -0
- package/dist/types/ids.js.map +1 -0
- package/dist/types/index.d.ts +19 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +19 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/journal.d.ts +47 -0
- package/dist/types/journal.d.ts.map +1 -0
- package/dist/types/journal.js +5 -0
- package/dist/types/journal.js.map +1 -0
- package/dist/types/query.d.ts +185 -0
- package/dist/types/query.d.ts.map +1 -0
- package/dist/types/query.js +10 -0
- package/dist/types/query.js.map +1 -0
- package/dist/types/records.d.ts +76 -0
- package/dist/types/records.d.ts.map +1 -0
- package/dist/types/records.js +5 -0
- package/dist/types/records.js.map +1 -0
- package/dist/types/schema.d.ts +74 -0
- package/dist/types/schema.d.ts.map +1 -0
- package/dist/types/schema.js +5 -0
- package/dist/types/schema.js.map +1 -0
- package/dist/types/system.d.ts +161 -0
- package/dist/types/system.d.ts.map +1 -0
- package/dist/types/system.js +32 -0
- package/dist/types/system.js.map +1 -0
- package/dist/types/tokens.d.ts +39 -0
- package/dist/types/tokens.d.ts.map +1 -0
- package/dist/types/tokens.js +5 -0
- package/dist/types/tokens.js.map +1 -0
- package/dist/validate.d.ts +29 -70
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +65 -97
- package/dist/validate.js.map +1 -1
- package/dist/{attachment-download.d.ts → wire/attachment-download.d.ts} +0 -57
- package/dist/wire/attachment-download.d.ts.map +1 -0
- package/dist/{attachment-download.js → wire/attachment-download.js} +0 -61
- package/dist/wire/attachment-download.js.map +1 -0
- package/dist/{auth.d.ts → wire/auth.d.ts} +5 -8
- package/dist/wire/auth.d.ts.map +1 -0
- package/dist/{auth.js → wire/auth.js} +5 -8
- package/dist/wire/auth.js.map +1 -0
- package/dist/{wire-body.d.ts → wire/body.d.ts} +13 -5
- package/dist/wire/body.d.ts.map +1 -0
- package/dist/{wire-body.js → wire/body.js} +65 -7
- package/dist/wire/body.js.map +1 -0
- package/dist/{wire-entry.d.ts → wire/index.d.ts} +12 -12
- package/dist/wire/index.d.ts.map +1 -0
- package/dist/{wire-entry.js → wire/index.js} +13 -17
- package/dist/wire/index.js.map +1 -0
- package/dist/{wire-record.d.ts → wire/record.d.ts} +10 -22
- package/dist/wire/record.d.ts.map +1 -0
- package/dist/{wire-record.js → wire/record.js} +12 -24
- package/dist/wire/record.js.map +1 -0
- package/dist/{wire-request.d.ts → wire/request.d.ts} +20 -37
- package/dist/wire/request.d.ts.map +1 -0
- package/dist/{wire-request.js → wire/request.js} +79 -54
- package/dist/wire/request.js.map +1 -0
- package/package.json +7 -7
- package/dist/adapter-entry.d.ts.map +0 -1
- package/dist/adapter-entry.js.map +0 -1
- package/dist/attachment-download.d.ts.map +0 -1
- package/dist/attachment-download.js.map +0 -1
- package/dist/auth.d.ts.map +0 -1
- package/dist/auth.js.map +0 -1
- package/dist/changes.d.ts.map +0 -1
- package/dist/changes.js.map +0 -1
- package/dist/combine.d.ts.map +0 -1
- package/dist/combine.js.map +0 -1
- package/dist/did-entry.d.ts.map +0 -1
- package/dist/did-entry.js.map +0 -1
- package/dist/did.d.ts.map +0 -1
- package/dist/did.js.map +0 -1
- package/dist/identity-bindings.d.ts.map +0 -1
- package/dist/identity-bindings.js.map +0 -1
- package/dist/limits.d.ts.map +0 -1
- package/dist/limits.js.map +0 -1
- package/dist/record-id.d.ts +0 -70
- package/dist/record-id.d.ts.map +0 -1
- package/dist/record-id.js +0 -127
- package/dist/record-id.js.map +0 -1
- package/dist/scoped-stack.d.ts +0 -532
- package/dist/scoped-stack.d.ts.map +0 -1
- package/dist/scoped-stack.js +0 -1219
- package/dist/scoped-stack.js.map +0 -1
- package/dist/sort.d.ts.map +0 -1
- package/dist/sort.js.map +0 -1
- package/dist/stack-reads.d.ts.map +0 -1
- package/dist/stack-reads.js.map +0 -1
- package/dist/stack.d.ts +0 -971
- package/dist/stack.d.ts.map +0 -1
- package/dist/stack.js +0 -2012
- package/dist/stack.js.map +0 -1
- package/dist/types.d.ts +0 -1250
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -49
- package/dist/types.js.map +0 -1
- package/dist/wire-body.d.ts.map +0 -1
- package/dist/wire-body.js.map +0 -1
- package/dist/wire-entry.d.ts.map +0 -1
- package/dist/wire-entry.js.map +0 -1
- package/dist/wire-record.d.ts.map +0 -1
- package/dist/wire-record.js.map +0 -1
- package/dist/wire-request.d.ts.map +0 -1
- package/dist/wire-request.js.map +0 -1
- /package/dist/{did.d.ts → did/did.d.ts} +0 -0
- /package/dist/{identity-bindings.d.ts → stack/identity-bindings.d.ts} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/adapter/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,YAAY,EACV,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,EAChB,QAAQ,EACR,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,cAAc,EACd,aAAa,GACd,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,eAAe,EACf,6BAA6B,EAC7B,uBAAuB,EACvB,wBAAwB,EACxB,cAAc,EACd,qBAAqB,GACtB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EACjB,YAAY,GACb,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,2BAA2B,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC"}
|
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
* adapters). Types an app reads too, such as StackCapabilities, live in
|
|
8
8
|
* the root entry point instead: every export has exactly one home.
|
|
9
9
|
*/
|
|
10
|
-
export { OwnerMismatchError } from '
|
|
10
|
+
export { OwnerMismatchError } from '../errors.js';
|
|
11
11
|
export { combineAdapters } from './combine.js';
|
|
12
|
-
export { assertQueryCapabilities, assertSortCapability, assertValidSort, assertValidAssociationFilters, assertValidJournalQuery, assertValidVersionsQuery, filtersContent, parseContentFilterKey, } from '
|
|
13
|
-
export { contentSortEntry, contentSortKey, compareSortEntries } from './sort.js';
|
|
14
|
-
|
|
12
|
+
export { assertQueryCapabilities, assertSortCapability, assertValidSort, assertValidAssociationFilters, assertValidJournalQuery, assertValidVersionsQuery, filtersContent, parseContentFilterKey, } from '../query-validation.js';
|
|
13
|
+
export { contentSortEntry, contentSortKey, compareSortEntries, constantSortEntry, sortConstant, } from './sort.js';
|
|
14
|
+
export { AUTHORITY_ASSOCIATION_KINDS, partitionAssociations } from '../associations/identity.js';
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/adapter/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAaH,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,eAAe,EACf,6BAA6B,EAC7B,uBAAuB,EACvB,wBAAwB,EACxB,cAAc,EACd,qBAAqB,GACtB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EACjB,YAAY,GACb,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,2BAA2B,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC"}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*
|
|
11
11
|
* See docs/spec/data-model.md § Sorting by a content field.
|
|
12
12
|
*/
|
|
13
|
-
import type { ScalarFieldKind } from '
|
|
13
|
+
import type { ScalarFieldKind } from '../types/index.js';
|
|
14
14
|
/**
|
|
15
15
|
* A field's value reduced to something ordered: a number, or text plus
|
|
16
16
|
* the key its ordering actually uses (see contentSortKey).
|
|
@@ -24,30 +24,30 @@ export type SortEntry = {
|
|
|
24
24
|
key: string;
|
|
25
25
|
};
|
|
26
26
|
/**
|
|
27
|
-
* Fold text into the key it sorts by
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* `toLowerCase()`, never `toLocaleLowerCase()` — a locale-sensitive fold
|
|
33
|
-
* would order Turkish `İstanbul` differently from every other runtime's,
|
|
34
|
-
* and the key is stored, so the divergence would be baked into an index.
|
|
35
|
-
* What this deliberately does not promise is in
|
|
36
|
-
* docs/spec/data-model.md § Text ordering.
|
|
27
|
+
* Fold text into the key it sorts by — decompose, drop combining marks,
|
|
28
|
+
* lowercase — so `Émile` files with `emile`. `toLowerCase()`, never the
|
|
29
|
+
* locale form: the key is stored, so a locale-sensitive fold would bake one
|
|
30
|
+
* runtime's order into the index. See docs/spec/data-model.md § Text ordering.
|
|
37
31
|
*/
|
|
38
32
|
export declare const contentSortKey: (value: string) => string;
|
|
39
33
|
/**
|
|
40
|
-
* The ordered form of `value`
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
* `date` normalizes to epoch milliseconds so a date field orders the way
|
|
47
|
-
* `createdAt` does instead of by ISO string collation, and `boolean` to
|
|
48
|
-
* 0/1 so false precedes true.
|
|
34
|
+
* The ordered form of `value` in a field of the declared `kind`, or null
|
|
35
|
+
* when there is nothing to order by. The kind comes from the schema, not
|
|
36
|
+
* the value, so a field orders one way on every record. `date` orders as
|
|
37
|
+
* epoch ms like `createdAt`, `boolean` as 0/1, and an enum as text, since
|
|
38
|
+
* its declaration order is not part of the schema.
|
|
49
39
|
*/
|
|
50
40
|
export declare function contentSortEntry(kind: ScalarFieldKind, value: unknown): SortEntry | null;
|
|
41
|
+
/**
|
|
42
|
+
* A constant a `contentFieldByType` key orders by, reduced from a value of
|
|
43
|
+
* `kind` to what it orders as: a number, text, or null for no value. The
|
|
44
|
+
* reduction happens before the query is sent, so an adapter orders it
|
|
45
|
+
* without knowing the kind. See docs/spec/data-model.md § Sorting by a
|
|
46
|
+
* content field.
|
|
47
|
+
*/
|
|
48
|
+
export declare function sortConstant(kind: ScalarFieldKind, value: unknown): string | number | null;
|
|
49
|
+
/** The entry a reduced constant orders as; see sortConstant(). */
|
|
50
|
+
export declare function constantSortEntry(value: string | number | null): SortEntry | null;
|
|
51
51
|
/**
|
|
52
52
|
* Order two records by one sort field, absent values last. `direction`
|
|
53
53
|
* reverses the order between values but never moves absence: a record
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sort.d.ts","sourceRoot":"","sources":["../../src/adapter/sort.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEzD;;;GAGG;AACH,MAAM,MAAM,SAAS,GAAG;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnG;;;;;GAKG;AACH,eAAO,MAAM,cAAc,GAAI,OAAO,MAAM,KAAG,MACe,CAAC;AAU/D;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,IAAI,CAsBxF;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAG1F;AAED,kEAAkE;AAClE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CAIjF;AA+BD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,CAAC,EAAE,SAAS,GAAG,IAAI,EACnB,CAAC,EAAE,SAAS,GAAG,IAAI,EACnB,SAAS,EAAE,KAAK,GAAG,MAAM,GACxB,MAAM,CAGR"}
|
|
@@ -11,38 +11,25 @@
|
|
|
11
11
|
* See docs/spec/data-model.md § Sorting by a content field.
|
|
12
12
|
*/
|
|
13
13
|
/**
|
|
14
|
-
* Fold text into the key it sorts by
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* `toLowerCase()`, never `toLocaleLowerCase()` — a locale-sensitive fold
|
|
20
|
-
* would order Turkish `İstanbul` differently from every other runtime's,
|
|
21
|
-
* and the key is stored, so the divergence would be baked into an index.
|
|
22
|
-
* What this deliberately does not promise is in
|
|
23
|
-
* docs/spec/data-model.md § Text ordering.
|
|
14
|
+
* Fold text into the key it sorts by — decompose, drop combining marks,
|
|
15
|
+
* lowercase — so `Émile` files with `emile`. `toLowerCase()`, never the
|
|
16
|
+
* locale form: the key is stored, so a locale-sensitive fold would bake one
|
|
17
|
+
* runtime's order into the index. See docs/spec/data-model.md § Text ordering.
|
|
24
18
|
*/
|
|
25
19
|
export const contentSortKey = (value) => value.normalize('NFKD').replace(/\p{M}/gu, '').toLowerCase();
|
|
26
20
|
/**
|
|
27
|
-
* A
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* record at a page boundary, and two adapters in two zones would answer
|
|
32
|
-
* one query in two orders. A zone-less date-time is read as UTC, which is
|
|
33
|
-
* what `Date.parse` already does for a date-only string.
|
|
21
|
+
* A zone-less date-time, read as UTC as `Date.parse` already reads a
|
|
22
|
+
* date-only string. Read in the host's zone, a stored index value and a
|
|
23
|
+
* cursor re-derived elsewhere would disagree at a page boundary, and two
|
|
24
|
+
* adapters in two zones would order one query two ways.
|
|
34
25
|
*/
|
|
35
26
|
const ZONELESS_DATE_TIME_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?$/;
|
|
36
27
|
/**
|
|
37
|
-
* The ordered form of `value`
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* `date` normalizes to epoch milliseconds so a date field orders the way
|
|
44
|
-
* `createdAt` does instead of by ISO string collation, and `boolean` to
|
|
45
|
-
* 0/1 so false precedes true.
|
|
28
|
+
* The ordered form of `value` in a field of the declared `kind`, or null
|
|
29
|
+
* when there is nothing to order by. The kind comes from the schema, not
|
|
30
|
+
* the value, so a field orders one way on every record. `date` orders as
|
|
31
|
+
* epoch ms like `createdAt`, `boolean` as 0/1, and an enum as text, since
|
|
32
|
+
* its declaration order is not part of the schema.
|
|
46
33
|
*/
|
|
47
34
|
export function contentSortEntry(kind, value) {
|
|
48
35
|
switch (kind) {
|
|
@@ -58,12 +45,35 @@ export function contentSortEntry(kind, value) {
|
|
|
58
45
|
const ms = Date.parse(ZONELESS_DATE_TIME_RE.test(value) ? `${value}Z` : value);
|
|
59
46
|
return Number.isNaN(ms) ? null : { kind: 'num', num: ms };
|
|
60
47
|
}
|
|
61
|
-
|
|
48
|
+
case 'string':
|
|
49
|
+
case 'text':
|
|
50
|
+
case 'record-ref':
|
|
51
|
+
case 'file-ref':
|
|
52
|
+
case 'enum':
|
|
62
53
|
return typeof value === 'string'
|
|
63
54
|
? { kind: 'text', text: value, key: contentSortKey(value) }
|
|
64
55
|
: null;
|
|
65
56
|
}
|
|
66
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* A constant a `contentFieldByType` key orders by, reduced from a value of
|
|
60
|
+
* `kind` to what it orders as: a number, text, or null for no value. The
|
|
61
|
+
* reduction happens before the query is sent, so an adapter orders it
|
|
62
|
+
* without knowing the kind. See docs/spec/data-model.md § Sorting by a
|
|
63
|
+
* content field.
|
|
64
|
+
*/
|
|
65
|
+
export function sortConstant(kind, value) {
|
|
66
|
+
const entry = contentSortEntry(kind, value);
|
|
67
|
+
return entry === null ? null : entry.kind === 'num' ? entry.num : entry.text;
|
|
68
|
+
}
|
|
69
|
+
/** The entry a reduced constant orders as; see sortConstant(). */
|
|
70
|
+
export function constantSortEntry(value) {
|
|
71
|
+
if (typeof value === 'number')
|
|
72
|
+
return Number.isFinite(value) ? { kind: 'num', num: value } : null;
|
|
73
|
+
if (typeof value === 'string')
|
|
74
|
+
return { kind: 'text', text: value, key: contentSortKey(value) };
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
67
77
|
/**
|
|
68
78
|
* Order two strings by code point. JS `<` compares UTF-16 code units,
|
|
69
79
|
* which files every supplementary character (U+10000 and above, stored as
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sort.js","sourceRoot":"","sources":["../../src/adapter/sort.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Association identity and edits
|
|
3
|
+
* -------------------------------------------------------
|
|
4
|
+
* When two associations are the same one, what a list of edits leaves
|
|
5
|
+
* behind, and what it moved. Identity is asked by every layer that touches
|
|
6
|
+
* an association — the no-op decision, the journal's delta, an adapter
|
|
7
|
+
* applying edits, the feed — so they answer it with one comparison and
|
|
8
|
+
* can never disagree. See docs/spec/data-model.md § Associations.
|
|
9
|
+
*/
|
|
10
|
+
import type { Association, AssociationChange, AssociationEdit, AuthorityAssociation, DataAssociation, Grantee, RelationshipTarget } from '../types/index.js';
|
|
11
|
+
/**
|
|
12
|
+
* Association identity — what dissociate() matches and a repeat associate()
|
|
13
|
+
* collides with; the SQLite adapter's primary key. An attachment's
|
|
14
|
+
* `attachmentRecordId` annotates a reference rather than naming one, so it
|
|
15
|
+
* is outside; a group grantee's `role` names a different set of people, so
|
|
16
|
+
* it is inside. See docs/spec/data-model.md § Associations.
|
|
17
|
+
*/
|
|
18
|
+
export declare function associationEqual(a: Association, b: Association): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Structural equality per grantee arm. `role` is required, so there is no
|
|
21
|
+
* absent-means-any spelling to normalize.
|
|
22
|
+
*/
|
|
23
|
+
export declare function granteeEqual(a: Grantee, b: Grantee): boolean;
|
|
24
|
+
/** Structural equality per target arm — what dissociate() matches on. */
|
|
25
|
+
export declare function targetEqual(a: RelationshipTarget, b: RelationshipTarget): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Identity plus every annotation outside it, so a write that only re-points
|
|
28
|
+
* an attachment association's `attachmentRecordId` still counts as a
|
|
29
|
+
* change. Identity alone decides which stored association a write lands on;
|
|
30
|
+
* this decides whether it says anything new.
|
|
31
|
+
*/
|
|
32
|
+
export declare function associationIdentical(a: Association, b: Association): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* The association kinds that carry authority. Every other kind is data.
|
|
35
|
+
* Storage that partitions by kind in a query reads this list rather than
|
|
36
|
+
* restating it, so a new authority kind can't fall into the data half.
|
|
37
|
+
*/
|
|
38
|
+
export declare const AUTHORITY_ASSOCIATION_KINDS: readonly AuthorityAssociation['kind'][];
|
|
39
|
+
/**
|
|
40
|
+
* Whether an association carries authority rather than data — the partition
|
|
41
|
+
* `StackRecord.permissions` and `StackRecord.associations` project, and the
|
|
42
|
+
* line `associate()`/`dissociate()` refuse to cross.
|
|
43
|
+
* See docs/spec/access-control.md § Record-level permissions.
|
|
44
|
+
*/
|
|
45
|
+
export declare function isAuthorityAssociation(a: Association): a is AuthorityAssociation;
|
|
46
|
+
/**
|
|
47
|
+
* One stored association set split into the two the record presents. An app
|
|
48
|
+
* editing tags never sees the authority half, so it cannot drop it.
|
|
49
|
+
*/
|
|
50
|
+
export declare function partitionAssociations(associations: Association[]): {
|
|
51
|
+
associations: DataAssociation[];
|
|
52
|
+
permissions: AuthorityAssociation[];
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* What a `changes.associations` list moves against a record's current set,
|
|
56
|
+
* one tagged edit each. An entry matching an existing identity displaced it:
|
|
57
|
+
* a `repoint`, carrying the prior association in full as `previous` — the
|
|
58
|
+
* only place an overwritten `attachmentRecordId` survives.
|
|
59
|
+
* See docs/spec/journal.md § The entry.
|
|
60
|
+
*/
|
|
61
|
+
export declare function associationDelta(before: Association[], after: Association[]): AssociationChange[];
|
|
62
|
+
/**
|
|
63
|
+
* The two flat lists a change frame carries, derived from the tagged list.
|
|
64
|
+
* The feed reports what is true now, so a `repoint` appears only under its
|
|
65
|
+
* new value and a `remove` by identity alone — the prior state the journal
|
|
66
|
+
* keeps has no place on a notification. The data half alone: `reshare` is
|
|
67
|
+
* what announces an ACL move. See docs/spec/events.md § The event shape.
|
|
68
|
+
*/
|
|
69
|
+
export declare function feedAssociationDelta(changes: AssociationChange[]): {
|
|
70
|
+
added: DataAssociation[];
|
|
71
|
+
removed: DataAssociation[];
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* An association edit list travels one surface: authority and data share
|
|
75
|
+
* storage and a delta, and never share a call. Refused here so every
|
|
76
|
+
* adapter inherits the same rule.
|
|
77
|
+
* See docs/spec/access-control.md § Storage unifies; the API does not.
|
|
78
|
+
*/
|
|
79
|
+
export declare function assertOneSurface(changes: readonly AssociationEdit[]): void;
|
|
80
|
+
/**
|
|
81
|
+
* The set a list of edits leaves behind: removes first, then adds, an add
|
|
82
|
+
* landing on an identity already held in place. Adds naming one identity
|
|
83
|
+
* collapse, last wins; an add outlives a remove of the same identity.
|
|
84
|
+
* See docs/spec/adapters.md § Amending associations.
|
|
85
|
+
*/
|
|
86
|
+
export declare function applyAssociationEdits(current: Association[], changes: readonly AssociationEdit[]): Association[];
|
|
87
|
+
/**
|
|
88
|
+
* The same op over every element of a list — what associate(),
|
|
89
|
+
* dissociate(), grantAccess() and revokeAccess() hand to the amend verb
|
|
90
|
+
* each is one half of.
|
|
91
|
+
*/
|
|
92
|
+
export declare function editsOf(op: 'add' | 'remove', associations: Association[]): AssociationEdit[];
|
|
93
|
+
//# sourceMappingURL=identity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/associations/identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EACV,WAAW,EACX,iBAAiB,EACjB,eAAe,EACf,oBAAoB,EACpB,eAAe,EACf,OAAO,EACP,kBAAkB,EACnB,MAAM,mBAAmB,CAAC;AAE3B;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,WAAW,GAAG,OAAO,CAaxE;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CAQ5D;AAED,yEAAyE;AACzE,wBAAgB,WAAW,CAAC,CAAC,EAAE,kBAAkB,EAAE,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAQjF;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,WAAW,GAAG,OAAO,CAM5E;AAcD;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,EAAE,SAAS,oBAAoB,CAAC,MAAM,CAAC,EAG9E,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,WAAW,GAAG,CAAC,IAAI,oBAAoB,CAEhF;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,WAAW,EAAE,GAAG;IAClE,YAAY,EAAE,eAAe,EAAE,CAAC;IAChC,WAAW,EAAE,oBAAoB,EAAE,CAAC;CACrC,CAKA;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,iBAAiB,EAAE,CAcjG;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,iBAAiB,EAAE,GAAG;IAClE,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,OAAO,EAAE,eAAe,EAAE,CAAC;CAC5B,CAYA;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,GAAG,IAAI,CAO1E;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,WAAW,EAAE,EACtB,OAAO,EAAE,SAAS,eAAe,EAAE,GAClC,WAAW,EAAE,CAWf;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,EAAE,EAAE,KAAK,GAAG,QAAQ,EAAE,YAAY,EAAE,WAAW,EAAE,GAAG,eAAe,EAAE,CAE5F"}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Association identity and edits
|
|
3
|
+
* -------------------------------------------------------
|
|
4
|
+
* When two associations are the same one, what a list of edits leaves
|
|
5
|
+
* behind, and what it moved. Identity is asked by every layer that touches
|
|
6
|
+
* an association — the no-op decision, the journal's delta, an adapter
|
|
7
|
+
* applying edits, the feed — so they answer it with one comparison and
|
|
8
|
+
* can never disagree. See docs/spec/data-model.md § Associations.
|
|
9
|
+
*/
|
|
10
|
+
import { StackBadRequestError } from '../errors.js';
|
|
11
|
+
/**
|
|
12
|
+
* Association identity — what dissociate() matches and a repeat associate()
|
|
13
|
+
* collides with; the SQLite adapter's primary key. An attachment's
|
|
14
|
+
* `attachmentRecordId` annotates a reference rather than naming one, so it
|
|
15
|
+
* is outside; a group grantee's `role` names a different set of people, so
|
|
16
|
+
* it is inside. See docs/spec/data-model.md § Associations.
|
|
17
|
+
*/
|
|
18
|
+
export function associationEqual(a, b) {
|
|
19
|
+
// Never "the same one": naming a malformed association is
|
|
20
|
+
// validateAssociation()'s job, which this must reach without throwing.
|
|
21
|
+
if (!a || !b)
|
|
22
|
+
return false;
|
|
23
|
+
if (a.kind !== b.kind || a.label !== b.label)
|
|
24
|
+
return false;
|
|
25
|
+
if (a.kind === 'attachment' && b.kind === 'attachment')
|
|
26
|
+
return a.fileId === b.fileId;
|
|
27
|
+
if (a.kind === 'relationship' && b.kind === 'relationship') {
|
|
28
|
+
return targetEqual(a.target, b.target);
|
|
29
|
+
}
|
|
30
|
+
if (a.kind === 'permission' && b.kind === 'permission') {
|
|
31
|
+
return granteeEqual(a.grantee, b.grantee);
|
|
32
|
+
}
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Structural equality per grantee arm. `role` is required, so there is no
|
|
37
|
+
* absent-means-any spelling to normalize.
|
|
38
|
+
*/
|
|
39
|
+
export function granteeEqual(a, b) {
|
|
40
|
+
if (!a || !b)
|
|
41
|
+
return false;
|
|
42
|
+
if (a.kind !== b.kind)
|
|
43
|
+
return false;
|
|
44
|
+
if (a.kind === 'entity' && b.kind === 'entity')
|
|
45
|
+
return a.entityId === b.entityId;
|
|
46
|
+
if (a.kind === 'group' && b.kind === 'group') {
|
|
47
|
+
return a.groupId === b.groupId && a.role === b.role;
|
|
48
|
+
}
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
/** Structural equality per target arm — what dissociate() matches on. */
|
|
52
|
+
export function targetEqual(a, b) {
|
|
53
|
+
if (a.kind !== b.kind)
|
|
54
|
+
return false;
|
|
55
|
+
if (a.kind === 'record' && b.kind === 'record') {
|
|
56
|
+
return a.recordId === b.recordId && (a.stackUrl ?? '') === (b.stackUrl ?? '');
|
|
57
|
+
}
|
|
58
|
+
if (a.kind === 'entity' && b.kind === 'entity')
|
|
59
|
+
return a.entityId === b.entityId;
|
|
60
|
+
if (a.kind === 'external' && b.kind === 'external')
|
|
61
|
+
return a.ns === b.ns && a.id === b.id;
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Identity plus every annotation outside it, so a write that only re-points
|
|
66
|
+
* an attachment association's `attachmentRecordId` still counts as a
|
|
67
|
+
* change. Identity alone decides which stored association a write lands on;
|
|
68
|
+
* this decides whether it says anything new.
|
|
69
|
+
*/
|
|
70
|
+
export function associationIdentical(a, b) {
|
|
71
|
+
if (!associationEqual(a, b))
|
|
72
|
+
return false;
|
|
73
|
+
if (a.kind === 'attachment' && b.kind === 'attachment') {
|
|
74
|
+
return (a.attachmentRecordId ?? '') === (b.attachmentRecordId ?? '');
|
|
75
|
+
}
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* An association's identity alone, its annotation dropped — what a change
|
|
80
|
+
* event reports for an association that's gone, as a `purged` frame names
|
|
81
|
+
* a destroyed record without repeating its content.
|
|
82
|
+
* See docs/spec/events.md § The event shape.
|
|
83
|
+
*/
|
|
84
|
+
function stripAssociationAnnotation(association) {
|
|
85
|
+
if (association.kind !== 'attachment')
|
|
86
|
+
return association;
|
|
87
|
+
const { attachmentRecordId: _attachmentRecordId, ...identity } = association;
|
|
88
|
+
return identity;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* The association kinds that carry authority. Every other kind is data.
|
|
92
|
+
* Storage that partitions by kind in a query reads this list rather than
|
|
93
|
+
* restating it, so a new authority kind can't fall into the data half.
|
|
94
|
+
*/
|
|
95
|
+
export const AUTHORITY_ASSOCIATION_KINDS = [
|
|
96
|
+
'permission',
|
|
97
|
+
'anyone',
|
|
98
|
+
];
|
|
99
|
+
/**
|
|
100
|
+
* Whether an association carries authority rather than data — the partition
|
|
101
|
+
* `StackRecord.permissions` and `StackRecord.associations` project, and the
|
|
102
|
+
* line `associate()`/`dissociate()` refuse to cross.
|
|
103
|
+
* See docs/spec/access-control.md § Record-level permissions.
|
|
104
|
+
*/
|
|
105
|
+
export function isAuthorityAssociation(a) {
|
|
106
|
+
return AUTHORITY_ASSOCIATION_KINDS.includes(a.kind);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* One stored association set split into the two the record presents. An app
|
|
110
|
+
* editing tags never sees the authority half, so it cannot drop it.
|
|
111
|
+
*/
|
|
112
|
+
export function partitionAssociations(associations) {
|
|
113
|
+
return {
|
|
114
|
+
associations: associations.filter((a) => !isAuthorityAssociation(a)),
|
|
115
|
+
permissions: associations.filter(isAuthorityAssociation),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* What a `changes.associations` list moves against a record's current set,
|
|
120
|
+
* one tagged edit each. An entry matching an existing identity displaced it:
|
|
121
|
+
* a `repoint`, carrying the prior association in full as `previous` — the
|
|
122
|
+
* only place an overwritten `attachmentRecordId` survives.
|
|
123
|
+
* See docs/spec/journal.md § The entry.
|
|
124
|
+
*/
|
|
125
|
+
export function associationDelta(before, after) {
|
|
126
|
+
const changes = after
|
|
127
|
+
.filter((a) => !before.some((b) => associationIdentical(a, b)))
|
|
128
|
+
.map((association) => {
|
|
129
|
+
const previous = before.find((b) => associationEqual(b, association));
|
|
130
|
+
return previous
|
|
131
|
+
? { op: 'repoint', association, previous }
|
|
132
|
+
: { op: 'add', association };
|
|
133
|
+
});
|
|
134
|
+
return changes.concat(before
|
|
135
|
+
.filter((b) => !after.some((a) => associationEqual(a, b)))
|
|
136
|
+
.map((association) => ({ op: 'remove', association })));
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* The two flat lists a change frame carries, derived from the tagged list.
|
|
140
|
+
* The feed reports what is true now, so a `repoint` appears only under its
|
|
141
|
+
* new value and a `remove` by identity alone — the prior state the journal
|
|
142
|
+
* keeps has no place on a notification. The data half alone: `reshare` is
|
|
143
|
+
* what announces an ACL move. See docs/spec/events.md § The event shape.
|
|
144
|
+
*/
|
|
145
|
+
export function feedAssociationDelta(changes) {
|
|
146
|
+
const added = [];
|
|
147
|
+
const removed = [];
|
|
148
|
+
for (const c of changes) {
|
|
149
|
+
if (c.op === 'remove') {
|
|
150
|
+
const identity = stripAssociationAnnotation(c.association);
|
|
151
|
+
if (!isAuthorityAssociation(identity))
|
|
152
|
+
removed.push(identity);
|
|
153
|
+
}
|
|
154
|
+
else if (!isAuthorityAssociation(c.association)) {
|
|
155
|
+
added.push(c.association);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return { added, removed };
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* An association edit list travels one surface: authority and data share
|
|
162
|
+
* storage and a delta, and never share a call. Refused here so every
|
|
163
|
+
* adapter inherits the same rule.
|
|
164
|
+
* See docs/spec/access-control.md § Storage unifies; the API does not.
|
|
165
|
+
*/
|
|
166
|
+
export function assertOneSurface(changes) {
|
|
167
|
+
const authority = changes.filter((c) => isAuthorityAssociation(c.association)).length;
|
|
168
|
+
if (authority === 0 || authority === changes.length)
|
|
169
|
+
return;
|
|
170
|
+
throw new StackBadRequestError('An association edit list carries authority or data, never both: permission and anyone ' +
|
|
171
|
+
'elements travel on the permissions surface.');
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* The set a list of edits leaves behind: removes first, then adds, an add
|
|
175
|
+
* landing on an identity already held in place. Adds naming one identity
|
|
176
|
+
* collapse, last wins; an add outlives a remove of the same identity.
|
|
177
|
+
* See docs/spec/adapters.md § Amending associations.
|
|
178
|
+
*/
|
|
179
|
+
export function applyAssociationEdits(current, changes) {
|
|
180
|
+
let next = current.filter((a) => !changes.some((c) => c.op === 'remove' && associationEqual(c.association, a)));
|
|
181
|
+
for (const c of changes) {
|
|
182
|
+
if (c.op !== 'add')
|
|
183
|
+
continue;
|
|
184
|
+
next = next.some((a) => associationEqual(a, c.association))
|
|
185
|
+
? next.map((a) => (associationEqual(a, c.association) ? c.association : a))
|
|
186
|
+
: [...next, c.association];
|
|
187
|
+
}
|
|
188
|
+
return next;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* The same op over every element of a list — what associate(),
|
|
192
|
+
* dissociate(), grantAccess() and revokeAccess() hand to the amend verb
|
|
193
|
+
* each is one half of.
|
|
194
|
+
*/
|
|
195
|
+
export function editsOf(op, associations) {
|
|
196
|
+
return associations.map((association) => ({ op, association }));
|
|
197
|
+
}
|
|
198
|
+
//# sourceMappingURL=identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/associations/identity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Association validation
|
|
3
|
+
* -------------------------------------------------------
|
|
4
|
+
* The rules an association is held to before it is stored: a known kind,
|
|
5
|
+
* only the keys that kind defines, a well-formed target or grantee, the
|
|
6
|
+
* right half of the authority/data partition, and each identity named
|
|
7
|
+
* once. A discriminated union is a compile-time promise, and a server
|
|
8
|
+
* mapping a request body onto one supplies raw JSON no compiler has seen,
|
|
9
|
+
* so the rules live in the invariant layer where no adapter can forget
|
|
10
|
+
* one. See docs/spec/data-model.md § Associations.
|
|
11
|
+
*/
|
|
12
|
+
import type { ValidationError } from '../validate.js';
|
|
13
|
+
import type { Association, AssociationEdit, AuthorityAssociation, DataAssociation, GrantGrantee, RelationshipTarget, RelationshipTargetPattern } from '../types/index.js';
|
|
14
|
+
/** Every key each relationship target arm defines. */
|
|
15
|
+
export declare const TARGET_KEYS: Record<RelationshipTarget['kind'], readonly string[]>;
|
|
16
|
+
/** Every key each grantee arm defines, the default tier included. */
|
|
17
|
+
export declare const GRANTEE_KEYS: Record<GrantGrantee['kind'], readonly string[]>;
|
|
18
|
+
/**
|
|
19
|
+
* The keys of `value` its arm does not define. An element carrying one is
|
|
20
|
+
* refused rather than stored: an adapter keeps only the keys it has
|
|
21
|
+
* columns for, so the rest would answer 200 and then vanish.
|
|
22
|
+
* See docs/spec/data-model.md § Associations.
|
|
23
|
+
*/
|
|
24
|
+
export declare function unknownKeys(value: object, keys: readonly string[]): string[];
|
|
25
|
+
/** unknownKeys() as a refusal. 400, not 422: the key addresses nothing. */
|
|
26
|
+
export declare function assertKnownKeys(value: object, keys: readonly string[], path: string): void;
|
|
27
|
+
/**
|
|
28
|
+
* Collect what makes a relationship target malformed. Absence is
|
|
29
|
+
* meaningful on `stackUrl` and an external `id` — this stack, and the
|
|
30
|
+
* whole namespace — so every part that names something must be non-empty:
|
|
31
|
+
* an empty string stores and matches as though it were absent.
|
|
32
|
+
* See docs/spec/data-model.md § Relationship targets.
|
|
33
|
+
*/
|
|
34
|
+
export declare function targetErrors(target: RelationshipTarget | RelationshipTargetPattern, path: string, opts?: {
|
|
35
|
+
externalIdOptional?: boolean;
|
|
36
|
+
}): ValidationError[];
|
|
37
|
+
/**
|
|
38
|
+
* Reject an association outside the closed set the types promise: a request
|
|
39
|
+
* body is raw JSON, and an unknown target kind would otherwise be stored as
|
|
40
|
+
* a Record in this stack. A key its kind does not define is a 400 at every
|
|
41
|
+
* depth, as on a request body. See docs/spec/data-model.md § Associations.
|
|
42
|
+
*/
|
|
43
|
+
export declare function validateAssociation(association: Association, path?: string): ValidationError[];
|
|
44
|
+
/**
|
|
45
|
+
* What makes a grantee malformed, under `path`: first a key its tier does not
|
|
46
|
+
* define — a 400 under `throw`, as on a request body, or collected on a
|
|
47
|
+
* stored `_grant` — then its tier's own fields. `role` has no default to fall
|
|
48
|
+
* back on. `authenticated` is a `_grant` tier alone; `anyRole` admits `'any'`.
|
|
49
|
+
*/
|
|
50
|
+
export declare function granteeErrors(grantee: unknown, path: string, opts: {
|
|
51
|
+
unknownKeys: 'throw' | 'collect';
|
|
52
|
+
authenticated?: boolean;
|
|
53
|
+
anyRole?: boolean;
|
|
54
|
+
}): ValidationError[];
|
|
55
|
+
/**
|
|
56
|
+
* Refuse an authority element on the data surface. The two halves share
|
|
57
|
+
* storage but never a call, or an app editing tags could replace an ACL it
|
|
58
|
+
* was never shown. Thrown, not collected: the caller named the wrong
|
|
59
|
+
* surface, and wanted grantAccess()/revokeAccess().
|
|
60
|
+
* See docs/spec/access-control.md § Record-level permissions.
|
|
61
|
+
*/
|
|
62
|
+
export declare function assertDataAssociations(associations: readonly Association[], surface: string): asserts associations is DataAssociation[];
|
|
63
|
+
/**
|
|
64
|
+
* The mirror of assertDataAssociations(): the `permissions` surface takes
|
|
65
|
+
* authority kinds alone, so a tag routed through it is refused rather than
|
|
66
|
+
* quietly becoming an ACL entry the `associations` projection never shows.
|
|
67
|
+
*/
|
|
68
|
+
export declare function assertAuthorityAssociations(permissions: readonly Association[], surface: string): asserts permissions is AuthorityAssociation[];
|
|
69
|
+
/**
|
|
70
|
+
* Every association in a list, plus the list's own rule: identities are
|
|
71
|
+
* distinct. An adapter keys associations by identity, so a repeat would
|
|
72
|
+
* displace the first and leave which one stands to chance — a caller bug,
|
|
73
|
+
* refused rather than collapsed. See docs/spec/data-model.md § Associations.
|
|
74
|
+
*/
|
|
75
|
+
export declare function validateAssociations(associations: Association[] | undefined, path?: string): ValidationError[];
|
|
76
|
+
/**
|
|
77
|
+
* Hold an edit list to the rules every verb shares: non-empty, known `op`s
|
|
78
|
+
* over well-formed associations, one surface, each identity once — removing
|
|
79
|
+
* and adding one identity is as ambiguous as naming it twice. `repoint` is
|
|
80
|
+
* the journal's word, never a request. See docs/spec/data-model.md § Mutations.
|
|
81
|
+
*/
|
|
82
|
+
export declare function assertAssociationEdits(changes: unknown, surface: string, half: 'data' | 'authority'): asserts changes is AssociationEdit[];
|
|
83
|
+
/**
|
|
84
|
+
* assertAssociationEdits() for the verbs taking a bare list — associate(),
|
|
85
|
+
* grantAccess() and their inverses — asked before wrapping, so problems are
|
|
86
|
+
* reported under `param`, the name the caller used. Same order of checks,
|
|
87
|
+
* so a wrong-surface element is refused as such before duplicates count.
|
|
88
|
+
*/
|
|
89
|
+
export declare function assertAssociationList(associations: unknown, surface: string, param: string, half: 'data' | 'authority'): asserts associations is Association[];
|
|
90
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/associations/validation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,KAAK,EAEV,WAAW,EACX,eAAe,EAEf,oBAAoB,EACpB,eAAe,EAGf,YAAY,EAKZ,kBAAkB,EAClB,yBAAyB,EAE1B,MAAM,mBAAmB,CAAC;AAY3B,sDAAsD;AACtD,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,kBAAkB,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAI7E,CAAC;AAgBF,qEAAqE;AACrE,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAIxE,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAE5E;AAED,2EAA2E;AAC3E,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAM1F;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,kBAAkB,GAAG,yBAAyB,EACtD,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE;IAAE,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAAO,GAC1C,eAAe,EAAE,CAyBnB;AAmBD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,WAAW,EACxB,IAAI,SAAgB,GACnB,eAAe,EAAE,CAoBnB;AAmCD;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE;IAAE,WAAW,EAAE,OAAO,GAAG,SAAS,CAAC;IAAC,aAAa,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GACrF,eAAe,EAAE,CA+CnB;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,YAAY,EAAE,SAAS,WAAW,EAAE,EACpC,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,YAAY,IAAI,eAAe,EAAE,CAO3C;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,WAAW,EAAE,SAAS,WAAW,EAAE,EACnC,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,WAAW,IAAI,oBAAoB,EAAE,CAO/C;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,YAAY,EAAE,WAAW,EAAE,GAAG,SAAS,EACvC,IAAI,SAAiB,GACpB,eAAe,EAAE,CAMnB;AAgBD;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,GAAG,WAAW,GACzB,OAAO,CAAC,OAAO,IAAI,eAAe,EAAE,CAoCtC;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,OAAO,EACrB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,GAAG,WAAW,GACzB,OAAO,CAAC,YAAY,IAAI,WAAW,EAAE,CAoBvC"}
|