@graphprotocol/grc-20 0.0.4
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 +153 -0
- package/dist/abis.d.ts +2 -0
- package/dist/abis.d.ts.map +1 -0
- package/dist/abis.js +2 -0
- package/dist/abis.js.map +1 -0
- package/dist/constants.d.ts +2 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +3 -0
- package/dist/constants.js.map +1 -0
- package/dist/contracts.d.ts +7 -0
- package/dist/contracts.d.ts.map +1 -0
- package/dist/contracts.js +10 -0
- package/dist/contracts.js.map +1 -0
- package/dist/error.d.ts +2 -0
- package/dist/error.d.ts.map +1 -0
- package/dist/error.js +8 -0
- package/dist/error.js.map +1 -0
- package/dist/example.d.ts +2 -0
- package/dist/example.d.ts.map +1 -0
- package/dist/example.js +27 -0
- package/dist/example.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/proto.d.ts +3 -0
- package/dist/proto.d.ts.map +1 -0
- package/dist/proto.js +3 -0
- package/dist/proto.js.map +1 -0
- package/dist/run.d.ts +2 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/run.js +4 -0
- package/dist/run.js.map +1 -0
- package/dist/src/abis/dao.d.ts +899 -0
- package/dist/src/abis/dao.d.ts.map +1 -0
- package/dist/src/abis/dao.js +1159 -0
- package/dist/src/abis/dao.js.map +1 -0
- package/dist/src/abis/governance-setup.d.ts +351 -0
- package/dist/src/abis/governance-setup.d.ts.map +1 -0
- package/dist/src/abis/governance-setup.js +461 -0
- package/dist/src/abis/governance-setup.js.map +1 -0
- package/dist/src/abis/index.d.ts +10 -0
- package/dist/src/abis/index.d.ts.map +1 -0
- package/dist/src/abis/index.js +10 -0
- package/dist/src/abis/index.js.map +1 -0
- package/dist/src/abis/main-voting.d.ts +1443 -0
- package/dist/src/abis/main-voting.d.ts.map +1 -0
- package/dist/src/abis/main-voting.js +1866 -0
- package/dist/src/abis/main-voting.js.map +1 -0
- package/dist/src/abis/member-access.d.ts +567 -0
- package/dist/src/abis/member-access.d.ts.map +1 -0
- package/dist/src/abis/member-access.js +735 -0
- package/dist/src/abis/member-access.js.map +1 -0
- package/dist/src/abis/personal-space-admin-setup.d.ts +259 -0
- package/dist/src/abis/personal-space-admin-setup.d.ts.map +1 -0
- package/dist/src/abis/personal-space-admin-setup.js +340 -0
- package/dist/src/abis/personal-space-admin-setup.js.map +1 -0
- package/dist/src/abis/personal-space-admin.d.ts +408 -0
- package/dist/src/abis/personal-space-admin.d.ts.map +1 -0
- package/dist/src/abis/personal-space-admin.js +526 -0
- package/dist/src/abis/personal-space-admin.js.map +1 -0
- package/dist/src/abis/plugin-setup-processor.d.ts +998 -0
- package/dist/src/abis/plugin-setup-processor.d.ts.map +1 -0
- package/dist/src/abis/plugin-setup-processor.js +1293 -0
- package/dist/src/abis/plugin-setup-processor.js.map +1 -0
- package/dist/src/abis/space-setup.d.ts +294 -0
- package/dist/src/abis/space-setup.d.ts.map +1 -0
- package/dist/src/abis/space-setup.js +387 -0
- package/dist/src/abis/space-setup.js.map +1 -0
- package/dist/src/abis/space.d.ts +263 -0
- package/dist/src/abis/space.d.ts.map +1 -0
- package/dist/src/abis/space.js +340 -0
- package/dist/src/abis/space.js.map +1 -0
- package/dist/src/account.d.ts +2 -0
- package/dist/src/account.d.ts.map +1 -0
- package/dist/src/account.js +2 -0
- package/dist/src/account.js.map +1 -0
- package/dist/src/base58.d.ts +36 -0
- package/dist/src/base58.d.ts.map +1 -0
- package/dist/src/base58.js +62 -0
- package/dist/src/base58.js.map +1 -0
- package/dist/src/base58.test.d.ts +2 -0
- package/dist/src/base58.test.d.ts.map +1 -0
- package/dist/src/base58.test.js +2 -0
- package/dist/src/base58.test.js.map +1 -0
- package/dist/src/blocks.d.ts +4 -0
- package/dist/src/blocks.d.ts.map +1 -0
- package/dist/src/blocks.js +4 -0
- package/dist/src/blocks.js.map +1 -0
- package/dist/src/core/account.d.ts +8 -0
- package/dist/src/core/account.d.ts.map +1 -0
- package/dist/src/core/account.js +50 -0
- package/dist/src/core/account.js.map +1 -0
- package/dist/src/core/base58.d.ts +37 -0
- package/dist/src/core/base58.d.ts.map +1 -0
- package/dist/src/core/base58.js +62 -0
- package/dist/src/core/base58.js.map +1 -0
- package/dist/src/core/base58.test.d.ts +2 -0
- package/dist/src/core/base58.test.d.ts.map +1 -0
- package/dist/src/core/base58.test.js +15 -0
- package/dist/src/core/base58.test.js.map +1 -0
- package/dist/src/core/blocks/data.d.ts +11 -0
- package/dist/src/core/blocks/data.d.ts.map +1 -0
- package/dist/src/core/blocks/data.js +48 -0
- package/dist/src/core/blocks/data.js.map +1 -0
- package/dist/src/core/blocks/image.d.ts +9 -0
- package/dist/src/core/blocks/image.d.ts.map +1 -0
- package/dist/src/core/blocks/image.js +9 -0
- package/dist/src/core/blocks/image.js.map +1 -0
- package/dist/src/core/blocks/text.d.ts +9 -0
- package/dist/src/core/blocks/text.d.ts.map +1 -0
- package/dist/src/core/blocks/text.js +30 -0
- package/dist/src/core/blocks/text.js.map +1 -0
- package/dist/src/core/get-checksum-address.d.ts +10 -0
- package/dist/src/core/get-checksum-address.d.ts.map +1 -0
- package/dist/src/core/get-checksum-address.js +20 -0
- package/dist/src/core/get-checksum-address.js.map +1 -0
- package/dist/src/core/ids/content.d.ts +59 -0
- package/dist/src/core/ids/content.d.ts.map +1 -0
- package/dist/src/core/ids/content.js +59 -0
- package/dist/src/core/ids/content.js.map +1 -0
- package/dist/src/core/ids/generate.d.ts +2 -0
- package/dist/src/core/ids/generate.d.ts.map +1 -0
- package/dist/src/core/ids/generate.js +4 -0
- package/dist/src/core/ids/generate.js.map +1 -0
- package/dist/src/core/ids/network.d.ts +4 -0
- package/dist/src/core/ids/network.d.ts.map +1 -0
- package/dist/src/core/ids/network.js +4 -0
- package/dist/src/core/ids/network.js.map +1 -0
- package/dist/src/core/ids/system.d.ts +212 -0
- package/dist/src/core/ids/system.d.ts.map +1 -0
- package/dist/src/core/ids/system.js +220 -0
- package/dist/src/core/ids/system.js.map +1 -0
- package/dist/src/core/image.d.ts +13 -0
- package/dist/src/core/image.d.ts.map +1 -0
- package/dist/src/core/image.js +33 -0
- package/dist/src/core/image.js.map +1 -0
- package/dist/src/core/position-strings/cursors.d.ts +56 -0
- package/dist/src/core/position-strings/cursors.d.ts.map +1 -0
- package/dist/src/core/position-strings/cursors.js +69 -0
- package/dist/src/core/position-strings/cursors.js.map +1 -0
- package/dist/src/core/position-strings/find-position.d.ts +24 -0
- package/dist/src/core/position-strings/find-position.d.ts.map +1 -0
- package/dist/src/core/position-strings/find-position.js +38 -0
- package/dist/src/core/position-strings/find-position.js.map +1 -0
- package/dist/src/core/position-strings/ids.d.ts +37 -0
- package/dist/src/core/position-strings/ids.d.ts.map +1 -0
- package/dist/src/core/position-strings/ids.js +79 -0
- package/dist/src/core/position-strings/ids.js.map +1 -0
- package/dist/src/core/position-strings/index.d.ts +5 -0
- package/dist/src/core/position-strings/index.d.ts.map +1 -0
- package/dist/src/core/position-strings/index.js +6 -0
- package/dist/src/core/position-strings/index.js.map +1 -0
- package/dist/src/core/position-strings/position-source.d.ts +128 -0
- package/dist/src/core/position-strings/position-source.d.ts.map +1 -0
- package/dist/src/core/position-strings/position-source.js +314 -0
- package/dist/src/core/position-strings/position-source.js.map +1 -0
- package/dist/src/core/position-strings/test/cursors.test.d.ts +2 -0
- package/dist/src/core/position-strings/test/cursors.test.d.ts.map +1 -0
- package/dist/src/core/position-strings/test/cursors.test.js +104 -0
- package/dist/src/core/position-strings/test/cursors.test.js.map +1 -0
- package/dist/src/core/position-strings/test/find-position.test.d.ts +2 -0
- package/dist/src/core/position-strings/test/find-position.test.d.ts.map +1 -0
- package/dist/src/core/position-strings/test/find-position.test.js +48 -0
- package/dist/src/core/position-strings/test/find-position.test.js.map +1 -0
- package/dist/src/core/position-strings/test/fuzz.test.d.ts +2 -0
- package/dist/src/core/position-strings/test/fuzz.test.d.ts.map +1 -0
- package/dist/src/core/position-strings/test/fuzz.test.js +78 -0
- package/dist/src/core/position-strings/test/fuzz.test.js.map +1 -0
- package/dist/src/core/position-strings/test/ids.test.d.ts +2 -0
- package/dist/src/core/position-strings/test/ids.test.d.ts.map +1 -0
- package/dist/src/core/position-strings/test/ids.test.js +17 -0
- package/dist/src/core/position-strings/test/ids.test.js.map +1 -0
- package/dist/src/core/position-strings/test/manual.test.d.ts +2 -0
- package/dist/src/core/position-strings/test/manual.test.d.ts.map +1 -0
- package/dist/src/core/position-strings/test/manual.test.js +307 -0
- package/dist/src/core/position-strings/test/manual.test.js.map +1 -0
- package/dist/src/core/position-strings/test/util.d.ts +6 -0
- package/dist/src/core/position-strings/test/util.d.ts.map +1 -0
- package/dist/src/core/position-strings/test/util.js +26 -0
- package/dist/src/core/position-strings/test/util.js.map +1 -0
- package/dist/src/core/position-strings/util.d.ts +8 -0
- package/dist/src/core/position-strings/util.d.ts.map +1 -0
- package/dist/src/core/position-strings/util.js +26 -0
- package/dist/src/core/position-strings/util.js.map +1 -0
- package/dist/src/core/position.d.ts +7 -0
- package/dist/src/core/position.d.ts.map +1 -0
- package/dist/src/core/position.js +7 -0
- package/dist/src/core/position.js.map +1 -0
- package/dist/src/core/relation.d.ts +30 -0
- package/dist/src/core/relation.d.ts.map +1 -0
- package/dist/src/core/relation.js +40 -0
- package/dist/src/core/relation.js.map +1 -0
- package/dist/src/core/scheme.d.ts +10 -0
- package/dist/src/core/scheme.d.ts.map +1 -0
- package/dist/src/core/scheme.js +21 -0
- package/dist/src/core/scheme.js.map +1 -0
- package/dist/src/core/triple.d.ts +14 -0
- package/dist/src/core/triple.d.ts.map +1 -0
- package/dist/src/core/triple.js +20 -0
- package/dist/src/core/triple.js.map +1 -0
- package/dist/src/encodings/get-accept-editor-arguments.d.ts +11 -0
- package/dist/src/encodings/get-accept-editor-arguments.d.ts.map +1 -0
- package/dist/src/encodings/get-accept-editor-arguments.js +23 -0
- package/dist/src/encodings/get-accept-editor-arguments.js.map +1 -0
- package/dist/src/encodings/get-accept-subspace-arguments.d.ts +11 -0
- package/dist/src/encodings/get-accept-subspace-arguments.d.ts.map +1 -0
- package/dist/src/encodings/get-accept-subspace-arguments.js +23 -0
- package/dist/src/encodings/get-accept-subspace-arguments.js.map +1 -0
- package/dist/src/encodings/get-calldata-for-space-governance-type.d.ts +8 -0
- package/dist/src/encodings/get-calldata-for-space-governance-type.d.ts.map +1 -0
- package/dist/src/encodings/get-calldata-for-space-governance-type.js +19 -0
- package/dist/src/encodings/get-calldata-for-space-governance-type.js.map +1 -0
- package/dist/src/encodings/get-process-geo-proposal-arguments.d.ts +7 -0
- package/dist/src/encodings/get-process-geo-proposal-arguments.d.ts.map +1 -0
- package/dist/src/encodings/get-process-geo-proposal-arguments.js +23 -0
- package/dist/src/encodings/get-process-geo-proposal-arguments.js.map +1 -0
- package/dist/src/encodings/get-remove-editor-arguments.d.ts +11 -0
- package/dist/src/encodings/get-remove-editor-arguments.d.ts.map +1 -0
- package/dist/src/encodings/get-remove-editor-arguments.js +23 -0
- package/dist/src/encodings/get-remove-editor-arguments.js.map +1 -0
- package/dist/src/encodings/get-remove-subspace-arguments.d.ts +11 -0
- package/dist/src/encodings/get-remove-subspace-arguments.d.ts.map +1 -0
- package/dist/src/encodings/get-remove-subspace-arguments.js +23 -0
- package/dist/src/encodings/get-remove-subspace-arguments.js.map +1 -0
- package/dist/src/encodings/index.d.ts +7 -0
- package/dist/src/encodings/index.d.ts.map +1 -0
- package/dist/src/encodings/index.js +7 -0
- package/dist/src/encodings/index.js.map +1 -0
- package/dist/src/id.d.ts +15 -0
- package/dist/src/id.d.ts.map +1 -0
- package/dist/src/id.js +21 -0
- package/dist/src/id.js.map +1 -0
- package/dist/src/image.d.ts +2 -0
- package/dist/src/image.d.ts.map +1 -0
- package/dist/src/image.js +2 -0
- package/dist/src/image.js.map +1 -0
- package/dist/src/position.d.ts +2 -0
- package/dist/src/position.d.ts.map +1 -0
- package/dist/src/position.js +2 -0
- package/dist/src/position.js.map +1 -0
- package/dist/src/proto/create-edit-proposal.d.ts +9 -0
- package/dist/src/proto/create-edit-proposal.d.ts.map +1 -0
- package/dist/src/proto/create-edit-proposal.js +54 -0
- package/dist/src/proto/create-edit-proposal.js.map +1 -0
- package/dist/src/proto/create-edit-proposal.test.d.ts +2 -0
- package/dist/src/proto/create-edit-proposal.test.d.ts.map +1 -0
- package/dist/src/proto/create-edit-proposal.test.js +140 -0
- package/dist/src/proto/create-edit-proposal.test.js.map +1 -0
- package/dist/src/proto/create-membership-proposal.d.ts +7 -0
- package/dist/src/proto/create-membership-proposal.d.ts.map +1 -0
- package/dist/src/proto/create-membership-proposal.js +26 -0
- package/dist/src/proto/create-membership-proposal.js.map +1 -0
- package/dist/src/proto/create-subspace-proposal.d.ts +7 -0
- package/dist/src/proto/create-subspace-proposal.d.ts.map +1 -0
- package/dist/src/proto/create-subspace-proposal.js +12 -0
- package/dist/src/proto/create-subspace-proposal.js.map +1 -0
- package/dist/src/proto/edit.d.ts +9 -0
- package/dist/src/proto/edit.d.ts.map +1 -0
- package/dist/src/proto/edit.js +54 -0
- package/dist/src/proto/edit.js.map +1 -0
- package/dist/src/proto/edit.test.d.ts +2 -0
- package/dist/src/proto/edit.test.d.ts.map +1 -0
- package/dist/src/proto/edit.test.js +140 -0
- package/dist/src/proto/edit.test.js.map +1 -0
- package/dist/src/proto/gen/src/proto/ipfs_pb.d.ts +404 -0
- package/dist/src/proto/gen/src/proto/ipfs_pb.d.ts.map +1 -0
- package/dist/src/proto/gen/src/proto/ipfs_pb.js +602 -0
- package/dist/src/proto/gen/src/proto/ipfs_pb.js.map +1 -0
- package/dist/src/proto/index.d.ts +2 -0
- package/dist/src/proto/index.d.ts.map +1 -0
- package/dist/src/proto/index.js +2 -0
- package/dist/src/proto/index.js.map +1 -0
- package/dist/src/relation.d.ts +2 -0
- package/dist/src/relation.d.ts.map +1 -0
- package/dist/src/relation.js +2 -0
- package/dist/src/relation.js.map +1 -0
- package/dist/src/scheme.d.ts +2 -0
- package/dist/src/scheme.d.ts.map +1 -0
- package/dist/src/scheme.js +2 -0
- package/dist/src/scheme.js.map +1 -0
- package/dist/src/system-ids.d.ts +4 -0
- package/dist/src/system-ids.d.ts.map +1 -0
- package/dist/src/system-ids.js +4 -0
- package/dist/src/system-ids.js.map +1 -0
- package/dist/src/triple.d.ts +2 -0
- package/dist/src/triple.d.ts.map +1 -0
- package/dist/src/triple.js +2 -0
- package/dist/src/triple.js.map +1 -0
- package/dist/src/types.d.ts +86 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +13 -0
- package/dist/src/types.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +47 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns `{ index, isPresent }`, where:
|
|
3
|
+
* - `index` is the current index of `position` in `positions`,
|
|
4
|
+
* or where it would be if added.
|
|
5
|
+
* - `isPresent` is true if `position` is present in `positions`.
|
|
6
|
+
*
|
|
7
|
+
* If this method is inconvenient (e.g., the positions are in a database
|
|
8
|
+
* instead of an array), you can instead compute
|
|
9
|
+
* `index` by finding the number of positions less than `position`.
|
|
10
|
+
* For example, in SQL, use:
|
|
11
|
+
* ```sql
|
|
12
|
+
* SELECT COUNT(*) FROM table WHERE position < $position
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* See also: `Cursors.toIndex`.
|
|
16
|
+
*
|
|
17
|
+
* @param positions The target list's positions, in lexicographic order.
|
|
18
|
+
* There should be no duplicate positions.
|
|
19
|
+
*/
|
|
20
|
+
export declare function findPosition(position: string, positions: ArrayLike<string>): {
|
|
21
|
+
index: number;
|
|
22
|
+
isPresent: boolean;
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=find-position.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"find-position.d.ts","sourceRoot":"","sources":["../../../../src/core/position-strings/find-position.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,CAAC,MAAM,CAAC,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAqBlH"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { assert } from './util.js';
|
|
2
|
+
/**
|
|
3
|
+
* Returns `{ index, isPresent }`, where:
|
|
4
|
+
* - `index` is the current index of `position` in `positions`,
|
|
5
|
+
* or where it would be if added.
|
|
6
|
+
* - `isPresent` is true if `position` is present in `positions`.
|
|
7
|
+
*
|
|
8
|
+
* If this method is inconvenient (e.g., the positions are in a database
|
|
9
|
+
* instead of an array), you can instead compute
|
|
10
|
+
* `index` by finding the number of positions less than `position`.
|
|
11
|
+
* For example, in SQL, use:
|
|
12
|
+
* ```sql
|
|
13
|
+
* SELECT COUNT(*) FROM table WHERE position < $position
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* See also: `Cursors.toIndex`.
|
|
17
|
+
*
|
|
18
|
+
* @param positions The target list's positions, in lexicographic order.
|
|
19
|
+
* There should be no duplicate positions.
|
|
20
|
+
*/
|
|
21
|
+
export function findPosition(position, positions) {
|
|
22
|
+
// Binary search: index is the "rank" of position, computed using
|
|
23
|
+
// https://en.wikipedia.org/wiki/Binary_search_algorithm#Procedure_for_finding_the_leftmost_element
|
|
24
|
+
let L = 0;
|
|
25
|
+
let R = positions.length;
|
|
26
|
+
while (L < R) {
|
|
27
|
+
const m = Math.floor((L + R) / 2);
|
|
28
|
+
const left = positions[m];
|
|
29
|
+
if (left < position)
|
|
30
|
+
L = m + 1;
|
|
31
|
+
else
|
|
32
|
+
R = m;
|
|
33
|
+
}
|
|
34
|
+
const left = positions[L - 1];
|
|
35
|
+
assert((L === 0 || left < position) && (L === positions.length || left >= position), 'Bad binary search (positions out of order?):', position, L);
|
|
36
|
+
return { index: L, isPresent: left === position };
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=find-position.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"find-position.js","sourceRoot":"","sources":["../../../../src/core/position-strings/find-position.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAEnC;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,YAAY,CAAC,QAAgB,EAAE,SAA4B;IACzE,iEAAiE;IACjE,mGAAmG;IACnG,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,IAAI,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC;IACzB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACb,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAClC,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAW,CAAC;QACpC,IAAI,IAAI,GAAG,QAAQ;YAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;;YAC1B,CAAC,GAAG,CAAC,CAAC;IACb,CAAC;IAED,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,GAAG,CAAC,CAAW,CAAC;IAExC,MAAM,CACJ,CAAC,CAAC,KAAK,CAAC,IAAI,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,KAAK,SAAS,CAAC,MAAM,IAAI,IAAI,IAAI,QAAQ,CAAC,EAC5E,8CAA8C,EAC9C,QAAQ,EACR,CAAC,CACF,CAAC;IACF,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,SAAS,EAAE,IAAI,KAAK,QAAQ,EAAE,CAAC;AACpD,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utitilies for generating `PositionSource` IDs
|
|
3
|
+
* (the `options.ID` constructor argument).
|
|
4
|
+
*/
|
|
5
|
+
export declare class IDs {
|
|
6
|
+
private constructor();
|
|
7
|
+
/**
|
|
8
|
+
* Default characters used in IDs: alphanumeric chars.
|
|
9
|
+
*/
|
|
10
|
+
static readonly DEFAULT_CHARS: string;
|
|
11
|
+
/**
|
|
12
|
+
* The default length of an ID, in characters.
|
|
13
|
+
*/
|
|
14
|
+
static readonly DEFAULT_LENGTH: number;
|
|
15
|
+
/**
|
|
16
|
+
* Returns a cryptographically random ID made of alphanumeric characters.
|
|
17
|
+
*
|
|
18
|
+
* @param options.length The length of the ID, in characters.
|
|
19
|
+
* Default: `IDs.DEFAULT_LENGTH`.
|
|
20
|
+
* @param options.chars The characters to draw from. Default: `IDs.DEFAULT_CHARS`.
|
|
21
|
+
*
|
|
22
|
+
* If specified, only the first 256 elements are used, and you achieve
|
|
23
|
+
* about `log_2(chars.length)` bits of entropy per `length`.
|
|
24
|
+
*/
|
|
25
|
+
static random(options?: {
|
|
26
|
+
length?: number;
|
|
27
|
+
chars?: string;
|
|
28
|
+
}): string;
|
|
29
|
+
/**
|
|
30
|
+
* Throws an error if `ID` does not satisfy the
|
|
31
|
+
* following requirements from `PositionSource`'s constructor:
|
|
32
|
+
* - It does not contain `','` or `'.'`.
|
|
33
|
+
* - The first character is lexicographically less than `'~'` (code point 126).
|
|
34
|
+
*/
|
|
35
|
+
static validate(ID: string): void;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=ids.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ids.d.ts","sourceRoot":"","sources":["../../../../src/core/position-strings/ids.ts"],"names":[],"mappings":"AAIA;;;GAGG;AACH,qBAAa,GAAG;IACd,OAAO;IAIP;;OAEG;IACH,MAAM,CAAC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAoE;IAUzG;;OAEG;IACH,MAAM,CAAC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAK;IAE3C;;;;;;;;;OASG;IACH,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM;IA+BpE;;;;;OAKG;IACH,MAAM,CAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;CAKlC"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import * as crypto from 'crypto';
|
|
2
|
+
import { LastInternal, precond } from './util.js';
|
|
3
|
+
/**
|
|
4
|
+
* Utitilies for generating `PositionSource` IDs
|
|
5
|
+
* (the `options.ID` constructor argument).
|
|
6
|
+
*/
|
|
7
|
+
export class IDs {
|
|
8
|
+
constructor() {
|
|
9
|
+
// Not instantiable.
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Default characters used in IDs: alphanumeric chars.
|
|
13
|
+
*/
|
|
14
|
+
static DEFAULT_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
15
|
+
// Rationale for value 8:
|
|
16
|
+
// Each character of the ID gives us ~6 bits of entropy,
|
|
17
|
+
// for a total of ~48 bits. This gives a < 1%
|
|
18
|
+
// probability that two connected `PositionSource`s
|
|
19
|
+
// will ever choose the same IDs, even if we
|
|
20
|
+
// consider the total probability across 100,000,000
|
|
21
|
+
// documents with 1,000 IDs each
|
|
22
|
+
// (= 10 users x 100 days x 1 ID/user/day).
|
|
23
|
+
/**
|
|
24
|
+
* The default length of an ID, in characters.
|
|
25
|
+
*/
|
|
26
|
+
static DEFAULT_LENGTH = 8;
|
|
27
|
+
/**
|
|
28
|
+
* Returns a cryptographically random ID made of alphanumeric characters.
|
|
29
|
+
*
|
|
30
|
+
* @param options.length The length of the ID, in characters.
|
|
31
|
+
* Default: `IDs.DEFAULT_LENGTH`.
|
|
32
|
+
* @param options.chars The characters to draw from. Default: `IDs.DEFAULT_CHARS`.
|
|
33
|
+
*
|
|
34
|
+
* If specified, only the first 256 elements are used, and you achieve
|
|
35
|
+
* about `log_2(chars.length)` bits of entropy per `length`.
|
|
36
|
+
*/
|
|
37
|
+
static random(options) {
|
|
38
|
+
const length = options?.length ?? this.DEFAULT_LENGTH;
|
|
39
|
+
const chars = options?.chars ?? this.DEFAULT_CHARS;
|
|
40
|
+
const arr = new Array(length);
|
|
41
|
+
let randomValues = new Uint8Array(length);
|
|
42
|
+
if (typeof window === 'undefined') {
|
|
43
|
+
// Use Node crypto library.
|
|
44
|
+
// We use eval("require") to prevent Webpack from attempting
|
|
45
|
+
// to bundle the crypto module and complaining.
|
|
46
|
+
// In theory we should also be able to do this by
|
|
47
|
+
// adding "browser": {"crypto": false} to package.json,
|
|
48
|
+
// but that is not working, and besides, every user
|
|
49
|
+
// of this package would have to remember to do so.
|
|
50
|
+
// See https://github.com/webpack/webpack/issues/8826
|
|
51
|
+
const cryptoReal = crypto;
|
|
52
|
+
const randomBuffer = cryptoReal.randomBytes(length);
|
|
53
|
+
randomValues = new Uint8Array(randomBuffer);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
// Use browser crypto library.
|
|
57
|
+
window.crypto.getRandomValues(randomValues);
|
|
58
|
+
}
|
|
59
|
+
for (let i = 0; i < length; i++) {
|
|
60
|
+
// This will be biased if chars.length does not divide 256,
|
|
61
|
+
// but it will still give at least floor(log_2(chars.length))
|
|
62
|
+
// bits of entropy.
|
|
63
|
+
arr[i] = chars[randomValues[i] % chars.length];
|
|
64
|
+
}
|
|
65
|
+
return arr.join('');
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Throws an error if `ID` does not satisfy the
|
|
69
|
+
* following requirements from `PositionSource`'s constructor:
|
|
70
|
+
* - It does not contain `','` or `'.'`.
|
|
71
|
+
* - The first character is lexicographically less than `'~'` (code point 126).
|
|
72
|
+
*/
|
|
73
|
+
static validate(ID) {
|
|
74
|
+
precond(ID < LastInternal, 'ID must be less than', LastInternal, ':', ID);
|
|
75
|
+
precond(!ID.includes(','), "ID must not contain ',':", ID);
|
|
76
|
+
precond(!ID.includes('.'), "ID must not contain '.':", ID);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=ids.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ids.js","sourceRoot":"","sources":["../../../../src/core/position-strings/ids.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAEjC,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAElD;;;GAGG;AACH,MAAM,OAAO,GAAG;IACd;QACE,oBAAoB;IACtB,CAAC;IAED;;OAEG;IACH,MAAM,CAAU,aAAa,GAAW,gEAAgE,CAAC;IAEzG,yBAAyB;IACzB,wDAAwD;IACxD,+CAA+C;IAC/C,mDAAmD;IACnD,4CAA4C;IAC5C,oDAAoD;IACpD,gCAAgC;IAChC,2CAA2C;IAC3C;;OAEG;IACH,MAAM,CAAU,cAAc,GAAW,CAAC,CAAC;IAE3C;;;;;;;;;OASG;IACH,MAAM,CAAC,MAAM,CAAC,OAA6C;QACzD,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,IAAI,IAAI,CAAC,cAAc,CAAC;QACtD,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,IAAI,IAAI,CAAC,aAAa,CAAC;QAEnD,MAAM,GAAG,GAAG,IAAI,KAAK,CAAS,MAAM,CAAC,CAAC;QACtC,IAAI,YAAY,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;QAC1C,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;YAClC,2BAA2B;YAC3B,4DAA4D;YAC5D,+CAA+C;YAC/C,iDAAiD;YACjD,uDAAuD;YACvD,mDAAmD;YACnD,mDAAmD;YACnD,qDAAqD;YACrD,MAAM,UAAU,GAAG,MAAM,CAAC;YAC1B,MAAM,YAAY,GAAG,UAAU,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;YACpD,YAAY,GAAG,IAAI,UAAU,CAAC,YAAY,CAAC,CAAC;QAC9C,CAAC;aAAM,CAAC;YACN,8BAA8B;YAC9B,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC;QAC9C,CAAC;QACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAChC,2DAA2D;YAC3D,6DAA6D;YAC7D,mBAAmB;YACnB,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,CAAE,YAAY,CAAC,CAAC,CAAY,GAAG,KAAK,CAAC,MAAM,CAAW,CAAC;QACvE,CAAC;QACD,OAAO,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACtB,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,QAAQ,CAAC,EAAU;QACxB,OAAO,CAAC,EAAE,GAAG,YAAY,EAAE,sBAAsB,EAAE,YAAY,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;QAC1E,OAAO,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,0BAA0B,EAAE,EAAE,CAAC,CAAC;QAC3D,OAAO,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,0BAA0B,EAAE,EAAE,CAAC,CAAC;IAC7D,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/core/position-strings/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/core/position-strings/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,6BAA6B"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A source of lexicographically-ordered "position strings" for
|
|
3
|
+
* collaborative lists and text.
|
|
4
|
+
*
|
|
5
|
+
* In a collaborative list (or text string), you need a way to refer
|
|
6
|
+
* to "positions" within that list that:
|
|
7
|
+
* 1. Point to a specific list element (or text character).
|
|
8
|
+
* 2. Are global (all users agree on them) and immutable (they do not
|
|
9
|
+
* change over time).
|
|
10
|
+
* 3. Can be sorted.
|
|
11
|
+
* 4. Are unique, even if different users concurrently create positions
|
|
12
|
+
* at the same place.
|
|
13
|
+
*
|
|
14
|
+
* `PositionSource` gives you such positions, in the form
|
|
15
|
+
* of lexicographically-ordered strings. Specifically, `createBetween`
|
|
16
|
+
* returns a new "position string" in between two existing position strings.
|
|
17
|
+
*
|
|
18
|
+
* These strings have the bonus properties:
|
|
19
|
+
* - 5. (Non-Interleaving) If two `PositionSource`s concurrently create a (forward or backward)
|
|
20
|
+
* sequence of positions at the same place,
|
|
21
|
+
* their sequences will not be interleaved.
|
|
22
|
+
* For example, if
|
|
23
|
+
* Alice types "Hello" while Bob types "World" at the same place,
|
|
24
|
+
* and they each use a `PositionSource` to create a position for each
|
|
25
|
+
* character, then
|
|
26
|
+
* the resulting order will be "HelloWorld" or "WorldHello", not
|
|
27
|
+
* "HWeolrllod".
|
|
28
|
+
* - 6. If a `PositionSource` creates positions in a forward (increasing)
|
|
29
|
+
* sequence, their lengths as strings will only grow logarithmically,
|
|
30
|
+
* not linearly.
|
|
31
|
+
*
|
|
32
|
+
* Position strings are printable ASCII. Specifically, they
|
|
33
|
+
* contain alphanumeric characters, `','`, and `'.'`.
|
|
34
|
+
* Also, the special string `PositionSource.LAST` is `'~'`.
|
|
35
|
+
*
|
|
36
|
+
* Further reading:
|
|
37
|
+
* - [Fractional indexing](https://www.figma.com/blog/realtime-editing-of-ordered-sequences/#fractional-indexing),
|
|
38
|
+
* a related scheme that satisfies 1-3 but not 4-6.
|
|
39
|
+
* - [List CRDTs](https://mattweidner.com/2022/10/21/basic-list-crdt.html)
|
|
40
|
+
* and how they map to position strings. `PositionSource` uses an optimized
|
|
41
|
+
* variant of that link's string implementation.
|
|
42
|
+
* - [Paper about interleaving](https://www.repository.cam.ac.uk/handle/1810/290391)
|
|
43
|
+
* in collaborative text editors.
|
|
44
|
+
*/
|
|
45
|
+
export declare class PositionSource {
|
|
46
|
+
/**
|
|
47
|
+
* A string that is less than all positions.
|
|
48
|
+
*
|
|
49
|
+
* Value: `""`.
|
|
50
|
+
*/
|
|
51
|
+
static readonly FIRST: string;
|
|
52
|
+
/**
|
|
53
|
+
* A string that is greater than all positions.
|
|
54
|
+
*
|
|
55
|
+
* Value: `"~"`.
|
|
56
|
+
*/
|
|
57
|
+
static readonly LAST: string;
|
|
58
|
+
/**
|
|
59
|
+
* The unique ID for this `PositionSource`.
|
|
60
|
+
*/
|
|
61
|
+
readonly ID: string;
|
|
62
|
+
/**
|
|
63
|
+
* Our waypoints' long name: `,${ID}.`.
|
|
64
|
+
*/
|
|
65
|
+
private readonly longName;
|
|
66
|
+
/**
|
|
67
|
+
* Variant of longName used for a position's first ID: `${ID}.`.
|
|
68
|
+
* (Otherwise every position would start with a redundant ','.)
|
|
69
|
+
*/
|
|
70
|
+
private readonly firstName;
|
|
71
|
+
/**
|
|
72
|
+
* For each waypoint that we created, maps a prefix (see getPrefix)
|
|
73
|
+
* for that waypoint to its last (most recent) valueSeq.
|
|
74
|
+
* We always store the right-side version (odd valueSeq).
|
|
75
|
+
*/
|
|
76
|
+
private lastValueSeqs;
|
|
77
|
+
/**
|
|
78
|
+
* Constructs a new `PositionSource`.
|
|
79
|
+
*
|
|
80
|
+
* It is okay to share a single `PositionSource` between
|
|
81
|
+
* all documents (lists/text strings) in the same JavaScript runtime.
|
|
82
|
+
*
|
|
83
|
+
* For efficiency (shorter position strings),
|
|
84
|
+
* within each JavaScript runtime, you should not use
|
|
85
|
+
* more than one `PositionSource` for the same document.
|
|
86
|
+
* An exception is if multiple logical users share the same runtime;
|
|
87
|
+
* we then recommend one `PositionSource` per user.
|
|
88
|
+
*
|
|
89
|
+
* @param options.ID A unique ID for this `PositionSource`. Defaults to
|
|
90
|
+
* `IDs.random()`.
|
|
91
|
+
*
|
|
92
|
+
* If provided, `options.ID` must satisfy:
|
|
93
|
+
* - It is unique across the entire collaborative application, i.e.,
|
|
94
|
+
* all `PositionSource`s whose positions may be compared to ours. This
|
|
95
|
+
* includes past `PositionSource`s, even if they correspond to the same
|
|
96
|
+
* user/device.
|
|
97
|
+
* - It does not contain `','` or `'.'`.
|
|
98
|
+
* - The first character is lexicographically less than `'~'` (code point 126).
|
|
99
|
+
*
|
|
100
|
+
* If `options.ID` contains non-alphanumeric characters, then created
|
|
101
|
+
* positions will contain those characters in addition to
|
|
102
|
+
* alphanumeric characters, `','`, and `'.'`.
|
|
103
|
+
*/
|
|
104
|
+
constructor(options?: {
|
|
105
|
+
ID?: string;
|
|
106
|
+
});
|
|
107
|
+
/**
|
|
108
|
+
* Returns a new position between `left` and `right`
|
|
109
|
+
* (`left < new < right`).
|
|
110
|
+
*
|
|
111
|
+
* The new position is unique across the entire collaborative application,
|
|
112
|
+
* even in the face of concurrent calls to this method on other
|
|
113
|
+
* `PositionSource`s.
|
|
114
|
+
*
|
|
115
|
+
* @param left Defaults to `PositionSource.FIRST` (insert at the beginning).
|
|
116
|
+
*
|
|
117
|
+
* @param right Defaults to `PositionSource.LAST` (insert at the end).
|
|
118
|
+
*/
|
|
119
|
+
createBetween(left?: string, right?: string): string;
|
|
120
|
+
/**
|
|
121
|
+
* Appends a wayoint to the given ancestor (= prefix adjusted for
|
|
122
|
+
* side), returning a unique new position using that waypoint.
|
|
123
|
+
*
|
|
124
|
+
* lastValueSeqs is also updated as needed for the waypoint.
|
|
125
|
+
*/
|
|
126
|
+
private appendWaypoint;
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=position-source.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"position-source.d.ts","sourceRoot":"","sources":["../../../../src/core/position-strings/position-source.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,qBAAa,cAAc;IACzB;;;;OAIG;IACH,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAM;IACnC;;;;OAIG;IACH,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAgB;IAE5C;;OAEG;IACH,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IAEnC;;;;OAIG;IACH,OAAO,CAAC,aAAa,CAA6B;IAElD;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;gBACS,OAAO,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE;IASrC;;;;;;;;;;;OAWG;IACH,aAAa,CAAC,IAAI,GAAE,MAA6B,EAAE,KAAK,GAAE,MAA4B,GAAG,MAAM;IA+C/F;;;;;OAKG;IACH,OAAO,CAAC,cAAc;CA2BvB"}
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import { IDs } from './ids.js';
|
|
2
|
+
import { LastInternal, assert, precond } from './util.js';
|
|
3
|
+
/**
|
|
4
|
+
* A source of lexicographically-ordered "position strings" for
|
|
5
|
+
* collaborative lists and text.
|
|
6
|
+
*
|
|
7
|
+
* In a collaborative list (or text string), you need a way to refer
|
|
8
|
+
* to "positions" within that list that:
|
|
9
|
+
* 1. Point to a specific list element (or text character).
|
|
10
|
+
* 2. Are global (all users agree on them) and immutable (they do not
|
|
11
|
+
* change over time).
|
|
12
|
+
* 3. Can be sorted.
|
|
13
|
+
* 4. Are unique, even if different users concurrently create positions
|
|
14
|
+
* at the same place.
|
|
15
|
+
*
|
|
16
|
+
* `PositionSource` gives you such positions, in the form
|
|
17
|
+
* of lexicographically-ordered strings. Specifically, `createBetween`
|
|
18
|
+
* returns a new "position string" in between two existing position strings.
|
|
19
|
+
*
|
|
20
|
+
* These strings have the bonus properties:
|
|
21
|
+
* - 5. (Non-Interleaving) If two `PositionSource`s concurrently create a (forward or backward)
|
|
22
|
+
* sequence of positions at the same place,
|
|
23
|
+
* their sequences will not be interleaved.
|
|
24
|
+
* For example, if
|
|
25
|
+
* Alice types "Hello" while Bob types "World" at the same place,
|
|
26
|
+
* and they each use a `PositionSource` to create a position for each
|
|
27
|
+
* character, then
|
|
28
|
+
* the resulting order will be "HelloWorld" or "WorldHello", not
|
|
29
|
+
* "HWeolrllod".
|
|
30
|
+
* - 6. If a `PositionSource` creates positions in a forward (increasing)
|
|
31
|
+
* sequence, their lengths as strings will only grow logarithmically,
|
|
32
|
+
* not linearly.
|
|
33
|
+
*
|
|
34
|
+
* Position strings are printable ASCII. Specifically, they
|
|
35
|
+
* contain alphanumeric characters, `','`, and `'.'`.
|
|
36
|
+
* Also, the special string `PositionSource.LAST` is `'~'`.
|
|
37
|
+
*
|
|
38
|
+
* Further reading:
|
|
39
|
+
* - [Fractional indexing](https://www.figma.com/blog/realtime-editing-of-ordered-sequences/#fractional-indexing),
|
|
40
|
+
* a related scheme that satisfies 1-3 but not 4-6.
|
|
41
|
+
* - [List CRDTs](https://mattweidner.com/2022/10/21/basic-list-crdt.html)
|
|
42
|
+
* and how they map to position strings. `PositionSource` uses an optimized
|
|
43
|
+
* variant of that link's string implementation.
|
|
44
|
+
* - [Paper about interleaving](https://www.repository.cam.ac.uk/handle/1810/290391)
|
|
45
|
+
* in collaborative text editors.
|
|
46
|
+
*/
|
|
47
|
+
export class PositionSource {
|
|
48
|
+
/**
|
|
49
|
+
* A string that is less than all positions.
|
|
50
|
+
*
|
|
51
|
+
* Value: `""`.
|
|
52
|
+
*/
|
|
53
|
+
static FIRST = '';
|
|
54
|
+
/**
|
|
55
|
+
* A string that is greater than all positions.
|
|
56
|
+
*
|
|
57
|
+
* Value: `"~"`.
|
|
58
|
+
*/
|
|
59
|
+
static LAST = LastInternal;
|
|
60
|
+
/**
|
|
61
|
+
* The unique ID for this `PositionSource`.
|
|
62
|
+
*/
|
|
63
|
+
ID;
|
|
64
|
+
/**
|
|
65
|
+
* Our waypoints' long name: `,${ID}.`.
|
|
66
|
+
*/
|
|
67
|
+
longName;
|
|
68
|
+
/**
|
|
69
|
+
* Variant of longName used for a position's first ID: `${ID}.`.
|
|
70
|
+
* (Otherwise every position would start with a redundant ','.)
|
|
71
|
+
*/
|
|
72
|
+
firstName;
|
|
73
|
+
/**
|
|
74
|
+
* For each waypoint that we created, maps a prefix (see getPrefix)
|
|
75
|
+
* for that waypoint to its last (most recent) valueSeq.
|
|
76
|
+
* We always store the right-side version (odd valueSeq).
|
|
77
|
+
*/
|
|
78
|
+
lastValueSeqs = new Map();
|
|
79
|
+
/**
|
|
80
|
+
* Constructs a new `PositionSource`.
|
|
81
|
+
*
|
|
82
|
+
* It is okay to share a single `PositionSource` between
|
|
83
|
+
* all documents (lists/text strings) in the same JavaScript runtime.
|
|
84
|
+
*
|
|
85
|
+
* For efficiency (shorter position strings),
|
|
86
|
+
* within each JavaScript runtime, you should not use
|
|
87
|
+
* more than one `PositionSource` for the same document.
|
|
88
|
+
* An exception is if multiple logical users share the same runtime;
|
|
89
|
+
* we then recommend one `PositionSource` per user.
|
|
90
|
+
*
|
|
91
|
+
* @param options.ID A unique ID for this `PositionSource`. Defaults to
|
|
92
|
+
* `IDs.random()`.
|
|
93
|
+
*
|
|
94
|
+
* If provided, `options.ID` must satisfy:
|
|
95
|
+
* - It is unique across the entire collaborative application, i.e.,
|
|
96
|
+
* all `PositionSource`s whose positions may be compared to ours. This
|
|
97
|
+
* includes past `PositionSource`s, even if they correspond to the same
|
|
98
|
+
* user/device.
|
|
99
|
+
* - It does not contain `','` or `'.'`.
|
|
100
|
+
* - The first character is lexicographically less than `'~'` (code point 126).
|
|
101
|
+
*
|
|
102
|
+
* If `options.ID` contains non-alphanumeric characters, then created
|
|
103
|
+
* positions will contain those characters in addition to
|
|
104
|
+
* alphanumeric characters, `','`, and `'.'`.
|
|
105
|
+
*/
|
|
106
|
+
constructor(options) {
|
|
107
|
+
if (options?.ID !== undefined) {
|
|
108
|
+
IDs.validate(options.ID);
|
|
109
|
+
}
|
|
110
|
+
this.ID = options?.ID ?? IDs.random();
|
|
111
|
+
this.longName = `,${this.ID}.`;
|
|
112
|
+
this.firstName = `${this.ID}.`;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Returns a new position between `left` and `right`
|
|
116
|
+
* (`left < new < right`).
|
|
117
|
+
*
|
|
118
|
+
* The new position is unique across the entire collaborative application,
|
|
119
|
+
* even in the face of concurrent calls to this method on other
|
|
120
|
+
* `PositionSource`s.
|
|
121
|
+
*
|
|
122
|
+
* @param left Defaults to `PositionSource.FIRST` (insert at the beginning).
|
|
123
|
+
*
|
|
124
|
+
* @param right Defaults to `PositionSource.LAST` (insert at the end).
|
|
125
|
+
*/
|
|
126
|
+
createBetween(left = PositionSource.FIRST, right = PositionSource.LAST) {
|
|
127
|
+
precond(left < right, 'left must be less than right:', left, '!<', right);
|
|
128
|
+
precond(right <= PositionSource.LAST, 'right must be less than or equal to LAST:', right, '!<=', PositionSource.LAST);
|
|
129
|
+
const leftFixed = left === PositionSource.FIRST ? null : left;
|
|
130
|
+
const rightFixed = right === PositionSource.LAST ? null : right;
|
|
131
|
+
let ans;
|
|
132
|
+
if (rightFixed !== null && (leftFixed === null || rightFixed.startsWith(leftFixed))) {
|
|
133
|
+
// Left child of right. This always appends a waypoint.
|
|
134
|
+
const ancestor = leftVersion(rightFixed);
|
|
135
|
+
ans = this.appendWaypoint(ancestor);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
// Right child of left.
|
|
139
|
+
if (leftFixed === null) {
|
|
140
|
+
// ancestor is FIRST.
|
|
141
|
+
ans = this.appendWaypoint('');
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
// Check if we can reuse left's prefix.
|
|
145
|
+
// It needs to be one of ours, and right can't use the same
|
|
146
|
+
// prefix (otherwise we would get ans > right by comparing right's
|
|
147
|
+
// older valueIndex to our new valueIndex).
|
|
148
|
+
const prefix = getPrefix(leftFixed);
|
|
149
|
+
const lastValueSeq = this.lastValueSeqs.get(prefix);
|
|
150
|
+
if (lastValueSeq !== undefined && !(rightFixed !== null && rightFixed.startsWith(prefix))) {
|
|
151
|
+
// Reuse.
|
|
152
|
+
const valueSeq = nextOddValueSeq(lastValueSeq);
|
|
153
|
+
ans = prefix + stringifyBase52(valueSeq);
|
|
154
|
+
this.lastValueSeqs.set(prefix, valueSeq);
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
// Append waypoint.
|
|
158
|
+
ans = this.appendWaypoint(leftFixed);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
assert(left < ans && ans < right, 'Bad position:', left, ans, right);
|
|
163
|
+
return ans;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Appends a wayoint to the given ancestor (= prefix adjusted for
|
|
167
|
+
* side), returning a unique new position using that waypoint.
|
|
168
|
+
*
|
|
169
|
+
* lastValueSeqs is also updated as needed for the waypoint.
|
|
170
|
+
*/
|
|
171
|
+
appendWaypoint(ancestor) {
|
|
172
|
+
let waypointName = ancestor === '' ? this.firstName : this.longName;
|
|
173
|
+
// If our ID already appears in ancestor, instead use a short
|
|
174
|
+
// name for the waypoint.
|
|
175
|
+
// Here we use the uniqueness of ',' and '.' to
|
|
176
|
+
// claim that if this.longName (= `,${ID}.`) appears in ancestor, then it
|
|
177
|
+
// must actually be from a waypoint that we created.
|
|
178
|
+
let existing = ancestor.lastIndexOf(this.longName);
|
|
179
|
+
if (ancestor.startsWith(this.firstName))
|
|
180
|
+
existing = 0;
|
|
181
|
+
if (existing !== -1) {
|
|
182
|
+
// Find the index of existing among the long-name
|
|
183
|
+
// waypoints, in backwards order. Here we use the fact that
|
|
184
|
+
// each longName ends with '.' and that '.' does not appear otherwise.
|
|
185
|
+
let index = -1;
|
|
186
|
+
for (let i = existing; i < ancestor.length; i++) {
|
|
187
|
+
if (ancestor[i] === '.')
|
|
188
|
+
index++;
|
|
189
|
+
}
|
|
190
|
+
waypointName = stringifyShortName(index);
|
|
191
|
+
}
|
|
192
|
+
const prefix = ancestor + waypointName;
|
|
193
|
+
const lastValueSeq = this.lastValueSeqs.get(prefix);
|
|
194
|
+
// Use next odd (right-side) valueSeq (1 if it's a new waypoint).
|
|
195
|
+
const valueSeq = lastValueSeq === undefined ? 1 : nextOddValueSeq(lastValueSeq);
|
|
196
|
+
this.lastValueSeqs.set(prefix, valueSeq);
|
|
197
|
+
return prefix + stringifyBase52(valueSeq);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Returns position's *prefix*: the string through the last waypoint
|
|
202
|
+
* name, or equivalently, without the final valueSeq.
|
|
203
|
+
*/
|
|
204
|
+
function getPrefix(position) {
|
|
205
|
+
// Last waypoint char is the last '.' (for long names) or
|
|
206
|
+
// digit (for short names). Note that neither appear in valueSeq,
|
|
207
|
+
// which is all letters.
|
|
208
|
+
for (let i = position.length - 2; i >= 0; i--) {
|
|
209
|
+
const char = position[i];
|
|
210
|
+
if (char === '.' || ('0' <= char && char <= '9')) {
|
|
211
|
+
// i is the last waypoint char, i.e., the end of the prefix.
|
|
212
|
+
return position.slice(0, i + 1);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
assert(false, 'No last waypoint char found (not a position?)', position);
|
|
216
|
+
return '';
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Returns the variant of position ending with a "left" marker
|
|
220
|
+
* instead of the default "right" marker.
|
|
221
|
+
*
|
|
222
|
+
* I.e., the ancestor for position's left descendants.
|
|
223
|
+
*/
|
|
224
|
+
function leftVersion(position) {
|
|
225
|
+
// We need to subtract one from the (odd) valueSeq, equivalently, from
|
|
226
|
+
// its last base52 digit.
|
|
227
|
+
const last = parseBase52(position[position.length - 1]);
|
|
228
|
+
assert(last % 2 === 1, 'Bad valueSeq (not a position?)', last, position);
|
|
229
|
+
return position.slice(0, -1) + stringifyBase52(last - 1);
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Base 52, except for last digit, which is base 10 using
|
|
233
|
+
* digits. That makes it easy to find the end of a short name
|
|
234
|
+
* in getPrefix: it ends at the last digit.
|
|
235
|
+
*/
|
|
236
|
+
function stringifyShortName(n) {
|
|
237
|
+
if (n < 10)
|
|
238
|
+
return String.fromCharCode(48 + n);
|
|
239
|
+
else
|
|
240
|
+
return stringifyBase52(Math.floor(n / 10)) + String.fromCharCode(48 + (n % 10));
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Base 52 encoding using letters (with "digits" in order by code point).
|
|
244
|
+
*/
|
|
245
|
+
function stringifyBase52(n) {
|
|
246
|
+
if (n === 0)
|
|
247
|
+
return 'A';
|
|
248
|
+
const codes = [];
|
|
249
|
+
while (n > 0) {
|
|
250
|
+
const digit = n % 52;
|
|
251
|
+
codes.unshift((digit >= 26 ? 71 : 65) + digit);
|
|
252
|
+
n = Math.floor(n / 52);
|
|
253
|
+
}
|
|
254
|
+
return String.fromCharCode(...codes);
|
|
255
|
+
}
|
|
256
|
+
function parseBase52(s) {
|
|
257
|
+
let n = 0;
|
|
258
|
+
for (let i = 0; i < s.length; i++) {
|
|
259
|
+
const code = s.charCodeAt(i);
|
|
260
|
+
const digit = code - (code >= 97 ? 71 : 65);
|
|
261
|
+
n = 52 * n + digit;
|
|
262
|
+
}
|
|
263
|
+
return n;
|
|
264
|
+
}
|
|
265
|
+
const log52 = Math.log(52);
|
|
266
|
+
/**
|
|
267
|
+
* Returns the next odd valueSeq in the special sequence.
|
|
268
|
+
* This is equivalent to mapping n to its valueIndex, adding 2,
|
|
269
|
+
* then mapping back.
|
|
270
|
+
*
|
|
271
|
+
* The sequence has the following properties:
|
|
272
|
+
* 1. Each number is a nonnegative integer (however, not all
|
|
273
|
+
* nonnegative integers are enumerated).
|
|
274
|
+
* 2. The numbers' base-52 representations are enumerated in
|
|
275
|
+
* lexicographic order, with no prefixes (i.e., no string
|
|
276
|
+
* representation is a prefix of another).
|
|
277
|
+
* 3. The n-th enumerated number has O(log(n)) base-52 digits.
|
|
278
|
+
*
|
|
279
|
+
* Properties (2) and (3) are analogous to normal counting, except
|
|
280
|
+
* that we order by the (base-52) lexicographic order instead of the
|
|
281
|
+
* usual order by magnitude. It is also the case that
|
|
282
|
+
* the numbers are in order by magnitude, although we do not
|
|
283
|
+
* use this property.
|
|
284
|
+
*
|
|
285
|
+
* The specific sequence is as follows:
|
|
286
|
+
* - Start with 0.
|
|
287
|
+
* - Enumerate 26^1 numbers (A, B, ..., Z).
|
|
288
|
+
* - Add 1, multiply by 52, then enumerate 26^2 numbers
|
|
289
|
+
* (aA, aB, ..., mz).
|
|
290
|
+
* - Add 1, multiply by 52, then enumerate 26^3 numbers
|
|
291
|
+
* (nAA, nAB, ..., tZz).
|
|
292
|
+
* - Repeat this pattern indefinitely, enumerating
|
|
293
|
+
* 26^d d-digit numbers for each d >= 1. Imagining a decimal place
|
|
294
|
+
* in front of each number, each d consumes 2^(-d) of the unit interval,
|
|
295
|
+
* so we never "reach 1" (overflow to d+1 digits when
|
|
296
|
+
* we meant to use d digits).
|
|
297
|
+
*
|
|
298
|
+
* I believe this is related to
|
|
299
|
+
* [Elias gamma coding](https://en.wikipedia.org/wiki/Elias_gamma_coding).
|
|
300
|
+
*/
|
|
301
|
+
function nextOddValueSeq(n) {
|
|
302
|
+
const d = n === 0 ? 1 : Math.floor(Math.log(n) / log52) + 1;
|
|
303
|
+
// You can calculate that the last d-digit number is 52^d - 26^d - 1.
|
|
304
|
+
if (n === Math.pow(52, d) - Math.pow(26, d) - 1) {
|
|
305
|
+
// First step is a new length: n -> (n + 1) * 52.
|
|
306
|
+
// Second step is n -> n + 1.
|
|
307
|
+
return (n + 1) * 52 + 1;
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
// n -> n + 1 twice.
|
|
311
|
+
return n + 2;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
//# sourceMappingURL=position-source.js.map
|