@amalgm/live 0.1.1 → 0.2.1
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/PURPOSE.md +16 -0
- package/README.md +1 -0
- package/dist/detection/enrollment.d.ts +20 -0
- package/dist/detection/enrollment.d.ts.map +1 -1
- package/dist/detection/enrollment.js +102 -1
- package/dist/detection/enrollment.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/registration/index.d.ts +3 -0
- package/dist/registration/index.d.ts.map +1 -0
- package/dist/registration/index.js +3 -0
- package/dist/registration/index.js.map +1 -0
- package/dist/registration/user-ground.d.ts +83 -0
- package/dist/registration/user-ground.d.ts.map +1 -0
- package/dist/registration/user-ground.js +143 -0
- package/dist/registration/user-ground.js.map +1 -0
- package/package.json +5 -1
package/PURPOSE.md
CHANGED
|
@@ -58,6 +58,20 @@ its cutover.
|
|
|
58
58
|
survive renames and rediscovery; local paths locate it but never become a
|
|
59
59
|
second identity, and every registry change emits both legacy `projects`
|
|
60
60
|
and canonical `workspaces` resource events.
|
|
61
|
+
12. **User ground is portable; coordination state is not.** Everything beneath
|
|
62
|
+
an authenticated `users/<email>/` home is registrable except the reserved
|
|
63
|
+
`.amalgm/` directory, transient SQLite journal/lock sidecars, and paths
|
|
64
|
+
excluded by its tracked `.amalgmignore`; the durable SQLite database itself
|
|
65
|
+
remains portable ground.
|
|
66
|
+
13. **The graph, not SQLite, travels.** R2 receives canonical entity snapshots
|
|
67
|
+
and immutable content; a machine derives its SQLite rows, paths, physical
|
|
68
|
+
identities, observer evidence, and watcher bindings locally.
|
|
69
|
+
14. **Login convergence has one authority.** A first machine conditionally
|
|
70
|
+
publishes the declared home; every later machine materializes that exact
|
|
71
|
+
cloud graph, and a simultaneous first-login loser adopts the winning graph.
|
|
72
|
+
15. **Cloud precedes Watch.** Login becomes ready only after local records equal
|
|
73
|
+
the active cloud snapshot and watcher coverage is established over the
|
|
74
|
+
resulting user ground.
|
|
61
75
|
|
|
62
76
|
## Five surfaces, one behavior
|
|
63
77
|
|
|
@@ -88,6 +102,8 @@ src/entities/ the entity model's rule surface: the six-field record,
|
|
|
88
102
|
src/detection/ the Detect/Watch pure cores: the adapter contract, the
|
|
89
103
|
three-channel compare (diff) core, coverage, continuity
|
|
90
104
|
verdicts, classification, refusals, enrollment policy.
|
|
105
|
+
src/registration/ the login-time user-ground convergence operation and its
|
|
106
|
+
injected local/cloud effect ports.
|
|
91
107
|
src/adapters/ the ports a host injects: transport, journal store,
|
|
92
108
|
content store, clock, crypto, logger.
|
|
93
109
|
tests/ unit tests per module, in the engine wall's style: each
|
package/README.md
CHANGED
|
@@ -22,6 +22,7 @@ normative specifications live in [docs/](docs/):
|
|
|
22
22
|
| [cloud-mutation-discipline.md](docs/cloud-mutation-discipline.md) | the journal-first write discipline and conformance rules |
|
|
23
23
|
| [shared-realtime-wave-3.md](docs/shared-realtime-wave-3.md) | the multi-user cloud-authority design |
|
|
24
24
|
| [extraction-boundary.md](docs/extraction-boundary.md) | what moves here from the engine runtime, and in what order |
|
|
25
|
+
| [user-ground-login.md](docs/user-ground-login.md) | first-login publication, later-device materialization, and Watch readiness |
|
|
25
26
|
|
|
26
27
|
## Surfaces
|
|
27
28
|
|
|
@@ -11,6 +11,26 @@
|
|
|
11
11
|
*/
|
|
12
12
|
/** The injected policy shape: relative path in, verdict out. */
|
|
13
13
|
export type EnrollmentPolicy = (relPath: string) => boolean;
|
|
14
|
+
/** The Git-like names at the user-ground boundary. Structural coordination
|
|
15
|
+
* state is local and cannot be negated by user policy; the ignore file is
|
|
16
|
+
* portable ground so every machine evaluates the same user rules. */
|
|
17
|
+
export declare const AMALGM_CONTROL_DIRECTORY = ".amalgm";
|
|
18
|
+
export declare const AMALGM_IGNORE_FILE = ".amalgmignore";
|
|
19
|
+
export declare const DEFAULT_AMALGM_IGNORE = "/.amalgm/\n";
|
|
14
20
|
/** The default policy. Both separators are path separators; '' never enrolls. */
|
|
15
21
|
export declare const defaultShouldEnroll: EnrollmentPolicy;
|
|
22
|
+
/** One normalized spelling for enrollment and ignore matching. */
|
|
23
|
+
export declare function normalizedRelativePath(relPath: string): string;
|
|
24
|
+
/** `.amalgm/` is local control state wherever a registered tree is rooted. */
|
|
25
|
+
export declare function isAmalgmControlPath(relPath: string): boolean;
|
|
26
|
+
/** SQLite's WAL, shared-memory, and rollback-journal files coordinate one
|
|
27
|
+
* local database connection. They are not a portable copy of the database. */
|
|
28
|
+
export declare function isSqliteCoordinationPath(relPath: string): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Compile the root `.amalgmignore` for a user ground. Rules are ordered and
|
|
31
|
+
* `!` negates a prior user rule. Reserved control state and transient SQLite
|
|
32
|
+
* coordination files are structural exclusions; every other path is ground
|
|
33
|
+
* unless this tracked file excludes it. The policy file itself always travels.
|
|
34
|
+
*/
|
|
35
|
+
export declare function createUserGroundEnrollmentPolicy(ignoreText?: string): EnrollmentPolicy;
|
|
16
36
|
//# sourceMappingURL=enrollment.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"enrollment.d.ts","sourceRoot":"","sources":["../../src/detection/enrollment.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,gEAAgE;AAChE,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"enrollment.d.ts","sourceRoot":"","sources":["../../src/detection/enrollment.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,gEAAgE;AAChE,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;AAE5D;;qEAEqE;AACrE,eAAO,MAAM,wBAAwB,YAAY,CAAC;AAClD,eAAO,MAAM,kBAAkB,kBAAkB,CAAC;AAClD,eAAO,MAAM,qBAAqB,gBAAoC,CAAC;AAgDvE,iFAAiF;AACjF,eAAO,MAAM,mBAAmB,EAAE,gBAMjC,CAAC;AAEF,kEAAkE;AAClE,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAM9D;AAED,8EAA8E;AAC9E,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAE5D;AAED;8EAC8E;AAC9E,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAGjE;AAsDD;;;;;GAKG;AACH,wBAAgB,gCAAgC,CAAC,UAAU,SAAK,GAAG,gBAAgB,CAalF"}
|
|
@@ -9,6 +9,12 @@
|
|
|
9
9
|
* Pure string policy: a host may inject its own `EnrollmentPolicy`
|
|
10
10
|
* everywhere this default is accepted.
|
|
11
11
|
*/
|
|
12
|
+
/** The Git-like names at the user-ground boundary. Structural coordination
|
|
13
|
+
* state is local and cannot be negated by user policy; the ignore file is
|
|
14
|
+
* portable ground so every machine evaluates the same user rules. */
|
|
15
|
+
export const AMALGM_CONTROL_DIRECTORY = '.amalgm';
|
|
16
|
+
export const AMALGM_IGNORE_FILE = '.amalgmignore';
|
|
17
|
+
export const DEFAULT_AMALGM_IGNORE = `/${AMALGM_CONTROL_DIRECTORY}/\n`;
|
|
12
18
|
const EXCLUDED_DIRECTORIES = new Set([
|
|
13
19
|
'.amalgm',
|
|
14
20
|
'.aws',
|
|
@@ -59,7 +65,7 @@ function isPrivateFile(name) {
|
|
|
59
65
|
}
|
|
60
66
|
/** The default policy. Both separators are path separators; '' never enrolls. */
|
|
61
67
|
export const defaultShouldEnroll = (relPath) => {
|
|
62
|
-
const parts = relPath.split(/
|
|
68
|
+
const parts = normalizedRelativePath(relPath).split('/').filter(Boolean);
|
|
63
69
|
const name = parts.at(-1);
|
|
64
70
|
if (name === undefined || parts.some((part) => EXCLUDED_DIRECTORIES.has(part)))
|
|
65
71
|
return false;
|
|
@@ -67,4 +73,99 @@ export const defaultShouldEnroll = (relPath) => {
|
|
|
67
73
|
return false;
|
|
68
74
|
return !name.startsWith('.#') && !name.endsWith('~') && !/\.(?:swp|swo)$/i.test(name);
|
|
69
75
|
};
|
|
76
|
+
/** One normalized spelling for enrollment and ignore matching. */
|
|
77
|
+
export function normalizedRelativePath(relPath) {
|
|
78
|
+
return String(relPath || '')
|
|
79
|
+
.replace(/\\/g, '/')
|
|
80
|
+
.replace(/^\.\//, '')
|
|
81
|
+
.replace(/^\/+|\/+$/g, '')
|
|
82
|
+
.replace(/\/{2,}/g, '/');
|
|
83
|
+
}
|
|
84
|
+
/** `.amalgm/` is local control state wherever a registered tree is rooted. */
|
|
85
|
+
export function isAmalgmControlPath(relPath) {
|
|
86
|
+
return normalizedRelativePath(relPath).split('/').includes(AMALGM_CONTROL_DIRECTORY);
|
|
87
|
+
}
|
|
88
|
+
/** SQLite's WAL, shared-memory, and rollback-journal files coordinate one
|
|
89
|
+
* local database connection. They are not a portable copy of the database. */
|
|
90
|
+
export function isSqliteCoordinationPath(relPath) {
|
|
91
|
+
const name = normalizedRelativePath(relPath).split('/').at(-1) ?? '';
|
|
92
|
+
return /\.(?:db|sqlite|sqlite3)-(?:wal|shm|journal)$/i.test(name);
|
|
93
|
+
}
|
|
94
|
+
function escapeRegex(character) {
|
|
95
|
+
return /[\\^$.*+?()[\]{}|]/.test(character) ? `\\${character}` : character;
|
|
96
|
+
}
|
|
97
|
+
function globExpression(pattern) {
|
|
98
|
+
let expression = '';
|
|
99
|
+
for (let index = 0; index < pattern.length; index += 1) {
|
|
100
|
+
const character = pattern[index];
|
|
101
|
+
if (character === '*') {
|
|
102
|
+
if (pattern[index + 1] === '*') {
|
|
103
|
+
while (pattern[index + 1] === '*')
|
|
104
|
+
index += 1;
|
|
105
|
+
if (pattern[index + 1] === '/') {
|
|
106
|
+
index += 1;
|
|
107
|
+
expression += '(?:.*/)?';
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
expression += '.*';
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
expression += '[^/]*';
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else if (character === '?') {
|
|
118
|
+
expression += '[^/]';
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
expression += escapeRegex(character);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return expression;
|
|
125
|
+
}
|
|
126
|
+
function ignoreRule(line) {
|
|
127
|
+
let value = line.trim();
|
|
128
|
+
if (!value || value.startsWith('#'))
|
|
129
|
+
return null;
|
|
130
|
+
let negated = false;
|
|
131
|
+
if (value.startsWith('!')) {
|
|
132
|
+
negated = true;
|
|
133
|
+
value = value.slice(1);
|
|
134
|
+
}
|
|
135
|
+
if (!value)
|
|
136
|
+
return null;
|
|
137
|
+
const anchored = value.startsWith('/');
|
|
138
|
+
if (anchored)
|
|
139
|
+
value = value.slice(1);
|
|
140
|
+
if (value.endsWith('/'))
|
|
141
|
+
value = value.slice(0, -1);
|
|
142
|
+
if (!value)
|
|
143
|
+
return null;
|
|
144
|
+
const body = globExpression(value);
|
|
145
|
+
const prefix = anchored || value.includes('/') ? '^' : '(?:^|/)';
|
|
146
|
+
return { negated, expression: new RegExp(`${prefix}${body}(?:$|/)`) };
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Compile the root `.amalgmignore` for a user ground. Rules are ordered and
|
|
150
|
+
* `!` negates a prior user rule. Reserved control state and transient SQLite
|
|
151
|
+
* coordination files are structural exclusions; every other path is ground
|
|
152
|
+
* unless this tracked file excludes it. The policy file itself always travels.
|
|
153
|
+
*/
|
|
154
|
+
export function createUserGroundEnrollmentPolicy(ignoreText = '') {
|
|
155
|
+
const rules = String(ignoreText).split(/\r?\n/).map(ignoreRule)
|
|
156
|
+
.filter((rule) => rule !== null);
|
|
157
|
+
return (input) => {
|
|
158
|
+
const relPath = normalizedRelativePath(input);
|
|
159
|
+
if (!relPath || isAmalgmControlPath(relPath) || isSqliteCoordinationPath(relPath))
|
|
160
|
+
return false;
|
|
161
|
+
if (relPath === AMALGM_IGNORE_FILE)
|
|
162
|
+
return true;
|
|
163
|
+
let excluded = false;
|
|
164
|
+
for (const rule of rules) {
|
|
165
|
+
if (rule.expression.test(relPath))
|
|
166
|
+
excluded = !rule.negated;
|
|
167
|
+
}
|
|
168
|
+
return !excluded;
|
|
169
|
+
};
|
|
170
|
+
}
|
|
70
171
|
//# sourceMappingURL=enrollment.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"enrollment.js","sourceRoot":"","sources":["../../src/detection/enrollment.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,MAAM,oBAAoB,GAAG,IAAI,GAAG,CAAC;IACnC,SAAS;IACT,MAAM;IACN,QAAQ;IACR,SAAS;IACT,MAAM;IACN,QAAQ;IACR,SAAS;IACT,KAAK;IACL,OAAO;IACP,aAAa;IACb,OAAO;IACP,aAAa;IACb,eAAe;IACf,eAAe;IACf,aAAa;IACb,MAAM;IACN,MAAM;IACN,aAAa;IACb,QAAQ;IACR,OAAO;IACP,OAAO;IACP,aAAa;IACb,UAAU;IACV,cAAc;CACf,CAAC,CAAC;AAEH,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC;IAC7B,WAAW;IACX,WAAW;IACX,aAAa;IACb,eAAe;IACf,gBAAgB;IAChB,gBAAgB;IAChB,gBAAgB;CACjB,CAAC,CAAC;AAEH,SAAS,aAAa,CAAC,IAAY;IACjC,IAAI,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7E,IAAI,4DAA4D,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACzF,IAAI,iCAAiC,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9D,IAAI,yEAAyE,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACtG,IAAI,0DAA0D,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACvF,OAAO,gEAAgE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACrF,CAAC;AAED,iFAAiF;AACjF,MAAM,CAAC,MAAM,mBAAmB,GAAqB,CAAC,OAAO,EAAE,EAAE;IAC/D,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,
|
|
1
|
+
{"version":3,"file":"enrollment.js","sourceRoot":"","sources":["../../src/detection/enrollment.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export * from './contracts/index.js';
|
|
|
13
13
|
export * from './machines/index.js';
|
|
14
14
|
export * from './entities/index.js';
|
|
15
15
|
export * from './detection/index.js';
|
|
16
|
+
export * from './registration/index.js';
|
|
16
17
|
export * from './merge/index.js';
|
|
17
18
|
export * from './workspace/index.js';
|
|
18
19
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,sBAAsB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,sBAAsB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -13,6 +13,7 @@ export * from './contracts/index.js';
|
|
|
13
13
|
export * from './machines/index.js';
|
|
14
14
|
export * from './entities/index.js';
|
|
15
15
|
export * from './detection/index.js';
|
|
16
|
+
export * from './registration/index.js';
|
|
16
17
|
export * from './merge/index.js';
|
|
17
18
|
export * from './workspace/index.js';
|
|
18
19
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,sBAAsB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,sBAAsB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/registration/index.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,cAAc,kBAAkB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/registration/index.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,cAAc,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Login-time user-ground convergence.
|
|
3
|
+
*
|
|
4
|
+
* Core owns when this operation runs. Live owns what it means: either create
|
|
5
|
+
* one initial cloud graph or install the active graph, prove the local
|
|
6
|
+
* materialization carries the same portable records, then start Watch. Every
|
|
7
|
+
* filesystem, SQLite, content, and R2 effect is injected by the host.
|
|
8
|
+
*/
|
|
9
|
+
import { type CloudEntityRecord, type CloudSnapshot, type Sha256Hex } from '../entities/index.js';
|
|
10
|
+
export interface UserGroundIdentity {
|
|
11
|
+
readonly userId: string;
|
|
12
|
+
readonly userEmail: string;
|
|
13
|
+
readonly deviceId: string;
|
|
14
|
+
}
|
|
15
|
+
/** The active immutable R2 snapshot selected by the cloud authority. */
|
|
16
|
+
export interface CloudUserGround {
|
|
17
|
+
readonly resourceId: string;
|
|
18
|
+
readonly version: number;
|
|
19
|
+
readonly checksum: string;
|
|
20
|
+
readonly snapshot: CloudSnapshot;
|
|
21
|
+
}
|
|
22
|
+
/** The local projection: portable records plus machine-only placement. */
|
|
23
|
+
export interface LocalUserGround {
|
|
24
|
+
readonly userId: string;
|
|
25
|
+
readonly userEmail: string;
|
|
26
|
+
readonly deviceId: string;
|
|
27
|
+
readonly localRoot: string;
|
|
28
|
+
readonly sqlitePath: string;
|
|
29
|
+
readonly records: readonly CloudEntityRecord[];
|
|
30
|
+
}
|
|
31
|
+
export interface UserGroundWatchEvidence {
|
|
32
|
+
readonly active: true;
|
|
33
|
+
readonly roots: number;
|
|
34
|
+
}
|
|
35
|
+
export interface UserGroundCloudPort {
|
|
36
|
+
/** Resolve the active immutable snapshot, or null when this user has none. */
|
|
37
|
+
lookup(resourceId: string): Promise<unknown>;
|
|
38
|
+
/** Compare-and-create. A racing loser receives created=false + the winner. */
|
|
39
|
+
publishInitial(input: {
|
|
40
|
+
readonly resourceId: string;
|
|
41
|
+
readonly checksum: string;
|
|
42
|
+
readonly snapshot: CloudSnapshot;
|
|
43
|
+
}): Promise<{
|
|
44
|
+
readonly created: boolean;
|
|
45
|
+
readonly current: unknown;
|
|
46
|
+
}>;
|
|
47
|
+
}
|
|
48
|
+
export interface UserGroundLocalPort {
|
|
49
|
+
/** Inspect an existing local projection without creating or binding it. */
|
|
50
|
+
inspect(identity: UserGroundIdentity): Promise<unknown>;
|
|
51
|
+
/** Create the declared new-user home and register its complete graph. */
|
|
52
|
+
initialize(identity: UserGroundIdentity): Promise<unknown>;
|
|
53
|
+
/** Materialize/install the supplied cloud graph and immutable content. */
|
|
54
|
+
install(identity: UserGroundIdentity, cloud: CloudUserGround): Promise<unknown>;
|
|
55
|
+
/** Begin or prove coverage only after cloud/local identity agrees. */
|
|
56
|
+
watch(identity: UserGroundIdentity, cloud: CloudUserGround): Promise<unknown>;
|
|
57
|
+
}
|
|
58
|
+
export interface UserGroundConvergencePorts {
|
|
59
|
+
readonly sha256Hex: Sha256Hex;
|
|
60
|
+
readonly cloud: UserGroundCloudPort;
|
|
61
|
+
readonly local: UserGroundLocalPort;
|
|
62
|
+
}
|
|
63
|
+
export interface UserGroundConvergence {
|
|
64
|
+
readonly userId: string;
|
|
65
|
+
readonly userEmail: string;
|
|
66
|
+
readonly deviceId: string;
|
|
67
|
+
readonly localRoot: string;
|
|
68
|
+
readonly sqlitePath: string;
|
|
69
|
+
readonly cloudResourceId: string;
|
|
70
|
+
readonly cloudVersion: number;
|
|
71
|
+
readonly cloudHead: string;
|
|
72
|
+
readonly mode: 'initialized' | 'materialized' | 'current';
|
|
73
|
+
readonly entityCount: number;
|
|
74
|
+
readonly watcherRoots: number;
|
|
75
|
+
readonly watching: true;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Converge one login. No caller selects "new" versus "existing": the active
|
|
79
|
+
* cloud head decides. Initial publication is conditional, so two first
|
|
80
|
+
* machines cannot create two authorities.
|
|
81
|
+
*/
|
|
82
|
+
export declare function convergeUserGround(input: UserGroundIdentity, ports: UserGroundConvergencePorts): Promise<UserGroundConvergence>;
|
|
83
|
+
//# sourceMappingURL=user-ground.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-ground.d.ts","sourceRoot":"","sources":["../../src/registration/user-ground.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Login-time user-ground convergence.
|
|
3
|
+
*
|
|
4
|
+
* Core owns when this operation runs. Live owns what it means: either create
|
|
5
|
+
* one initial cloud graph or install the active graph, prove the local
|
|
6
|
+
* materialization carries the same portable records, then start Watch. Every
|
|
7
|
+
* filesystem, SQLite, content, and R2 effect is injected by the host.
|
|
8
|
+
*/
|
|
9
|
+
import { privateEntityResourceId, sameRecords, snapshotFromRecords, stableJson, } from '../entities/index.js';
|
|
10
|
+
const HASH = /^[0-9a-f]{64}$/;
|
|
11
|
+
function required(value, name) {
|
|
12
|
+
const text = typeof value === 'string' ? value.trim() : '';
|
|
13
|
+
if (!text)
|
|
14
|
+
throw new Error(`user-ground ${name} is required`);
|
|
15
|
+
return text;
|
|
16
|
+
}
|
|
17
|
+
function normalizeIdentity(input) {
|
|
18
|
+
return {
|
|
19
|
+
userId: required(input?.userId, 'user id'),
|
|
20
|
+
userEmail: required(input?.userEmail, 'user email').toLowerCase(),
|
|
21
|
+
deviceId: required(input?.deviceId, 'device id'),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function assertCloud(value, resourceId, sha256Hex) {
|
|
25
|
+
const cloud = value;
|
|
26
|
+
if (!cloud || cloud.resourceId !== resourceId
|
|
27
|
+
|| !Number.isSafeInteger(cloud.version) || Number(cloud.version) < 0
|
|
28
|
+
|| typeof cloud.checksum !== 'string' || !HASH.test(cloud.checksum)) {
|
|
29
|
+
throw new Error('cloud user-ground head is invalid');
|
|
30
|
+
}
|
|
31
|
+
const snapshot = snapshotFromRecords(cloud.snapshot?.records || []);
|
|
32
|
+
if (snapshot.records.length === 0)
|
|
33
|
+
throw new Error('cloud user-ground graph is empty');
|
|
34
|
+
const checksum = sha256Hex(stableJson(snapshot));
|
|
35
|
+
if (checksum !== cloud.checksum)
|
|
36
|
+
throw new Error('cloud user-ground snapshot checksum mismatch');
|
|
37
|
+
return {
|
|
38
|
+
resourceId,
|
|
39
|
+
version: Number(cloud.version),
|
|
40
|
+
checksum,
|
|
41
|
+
snapshot,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function controlPath(path) {
|
|
45
|
+
return path.replace(/\\/g, '/').split('/').includes('.amalgm');
|
|
46
|
+
}
|
|
47
|
+
function assertLocal(value, identity) {
|
|
48
|
+
const local = value;
|
|
49
|
+
if (!local
|
|
50
|
+
|| local.userId !== identity.userId
|
|
51
|
+
|| String(local.userEmail || '').toLowerCase() !== identity.userEmail
|
|
52
|
+
|| local.deviceId !== identity.deviceId) {
|
|
53
|
+
throw new Error('local user-ground identity does not match login');
|
|
54
|
+
}
|
|
55
|
+
const localRoot = required(local.localRoot, 'local root');
|
|
56
|
+
const sqlitePath = required(local.sqlitePath, 'SQLite path');
|
|
57
|
+
if (!controlPath(sqlitePath)) {
|
|
58
|
+
throw new Error('local user-ground SQLite must live inside the reserved .amalgm control directory');
|
|
59
|
+
}
|
|
60
|
+
const snapshot = snapshotFromRecords(local.records || []);
|
|
61
|
+
if (snapshot.records.length === 0)
|
|
62
|
+
throw new Error('local user-ground graph is empty');
|
|
63
|
+
return { ...identity, localRoot, sqlitePath, records: snapshot.records };
|
|
64
|
+
}
|
|
65
|
+
function assertSameGraph(local, cloud) {
|
|
66
|
+
if (!sameRecords(local.records, cloud.snapshot.records)) {
|
|
67
|
+
throw new Error('local user-ground graph does not match the active cloud snapshot');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function assertWatch(value) {
|
|
71
|
+
const watch = value;
|
|
72
|
+
if (!watch || watch.active !== true || !Number.isSafeInteger(watch.roots) || Number(watch.roots) < 1) {
|
|
73
|
+
throw new Error('user-ground watcher coverage is not active');
|
|
74
|
+
}
|
|
75
|
+
return { active: true, roots: Number(watch.roots) };
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Converge one login. No caller selects "new" versus "existing": the active
|
|
79
|
+
* cloud head decides. Initial publication is conditional, so two first
|
|
80
|
+
* machines cannot create two authorities.
|
|
81
|
+
*/
|
|
82
|
+
export async function convergeUserGround(input, ports) {
|
|
83
|
+
const identity = normalizeIdentity(input);
|
|
84
|
+
const resourceId = privateEntityResourceId(identity.userId, ports.sha256Hex);
|
|
85
|
+
const found = await ports.cloud.lookup(resourceId);
|
|
86
|
+
let cloud;
|
|
87
|
+
let local;
|
|
88
|
+
let mode;
|
|
89
|
+
if (found === null || found === undefined) {
|
|
90
|
+
const initialized = assertLocal(await ports.local.initialize(identity), identity);
|
|
91
|
+
const snapshot = snapshotFromRecords(initialized.records);
|
|
92
|
+
const checksum = ports.sha256Hex(stableJson(snapshot));
|
|
93
|
+
const published = await ports.cloud.publishInitial({ resourceId, checksum, snapshot });
|
|
94
|
+
cloud = assertCloud(published.current, resourceId, ports.sha256Hex);
|
|
95
|
+
if (published.created) {
|
|
96
|
+
assertSameGraph(initialized, cloud);
|
|
97
|
+
local = initialized;
|
|
98
|
+
mode = 'initialized';
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
local = assertLocal(await ports.local.install(identity, cloud), identity);
|
|
102
|
+
assertSameGraph(local, cloud);
|
|
103
|
+
mode = 'materialized';
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
cloud = assertCloud(found, resourceId, ports.sha256Hex);
|
|
108
|
+
const currentValue = await ports.local.inspect(identity);
|
|
109
|
+
if (currentValue !== null && currentValue !== undefined) {
|
|
110
|
+
const current = assertLocal(currentValue, identity);
|
|
111
|
+
if (sameRecords(current.records, cloud.snapshot.records)) {
|
|
112
|
+
local = current;
|
|
113
|
+
mode = 'current';
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
local = assertLocal(await ports.local.install(identity, cloud), identity);
|
|
117
|
+
assertSameGraph(local, cloud);
|
|
118
|
+
mode = 'materialized';
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
local = assertLocal(await ports.local.install(identity, cloud), identity);
|
|
123
|
+
assertSameGraph(local, cloud);
|
|
124
|
+
mode = 'materialized';
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
const watch = assertWatch(await ports.local.watch(identity, cloud));
|
|
128
|
+
const finalLocal = assertLocal(await ports.local.inspect(identity), identity);
|
|
129
|
+
assertSameGraph(finalLocal, cloud);
|
|
130
|
+
return {
|
|
131
|
+
...identity,
|
|
132
|
+
localRoot: finalLocal.localRoot,
|
|
133
|
+
sqlitePath: finalLocal.sqlitePath,
|
|
134
|
+
cloudResourceId: cloud.resourceId,
|
|
135
|
+
cloudVersion: cloud.version,
|
|
136
|
+
cloudHead: cloud.checksum,
|
|
137
|
+
mode,
|
|
138
|
+
entityCount: cloud.snapshot.records.length,
|
|
139
|
+
watcherRoots: watch.roots,
|
|
140
|
+
watching: true,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=user-ground.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-ground.js","sourceRoot":"","sources":["../../src/registration/user-ground.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EACL,uBAAuB,EACvB,WAAW,EACX,mBAAmB,EACnB,UAAU,GAIX,MAAM,sBAAsB,CAAC;AA0E9B,MAAM,IAAI,GAAG,gBAAgB,CAAC;AAE9B,SAAS,QAAQ,CAAC,KAAc,EAAE,IAAY;IAC5C,MAAM,IAAI,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3D,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,eAAe,IAAI,cAAc,CAAC,CAAC;IAC9D,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAyB;IAClD,OAAO;QACL,MAAM,EAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC;QAC1C,SAAS,EAAE,QAAQ,CAAC,KAAK,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC,WAAW,EAAE;QACjE,QAAQ,EAAE,QAAQ,CAAC,KAAK,EAAE,QAAQ,EAAE,WAAW,CAAC;KACjD,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAClB,KAAc,EACd,UAAkB,EAClB,SAAoB;IAEpB,MAAM,KAAK,GAAG,KAAwC,CAAC;IACvD,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,UAAU,KAAK,UAAU;WACxC,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;WACjE,OAAO,KAAK,CAAC,QAAQ,KAAK,QAAQ,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;QACtE,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IACvD,CAAC;IACD,MAAM,QAAQ,GAAG,mBAAmB,CAAC,KAAK,CAAC,QAAQ,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC;IACpE,IAAI,QAAQ,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IACvF,MAAM,QAAQ,GAAG,SAAS,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC;IACjD,IAAI,QAAQ,KAAK,KAAK,CAAC,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IACjG,OAAO;QACL,UAAU;QACV,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC;QAC9B,QAAQ;QACR,QAAQ;KACT,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,IAAY;IAC/B,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;AACjE,CAAC;AAED,SAAS,WAAW,CAAC,KAAc,EAAE,QAA4B;IAC/D,MAAM,KAAK,GAAG,KAAwC,CAAC;IACvD,IAAI,CAAC,KAAK;WACL,KAAK,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM;WAChC,MAAM,CAAC,KAAK,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,KAAK,QAAQ,CAAC,SAAS;WAClE,KAAK,CAAC,QAAQ,KAAK,QAAQ,CAAC,QAAQ,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACrE,CAAC;IACD,MAAM,SAAS,GAAG,QAAQ,CAAC,KAAK,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;IAC1D,MAAM,UAAU,GAAG,QAAQ,CAAC,KAAK,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;IAC7D,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,kFAAkF,CAAC,CAAC;IACtG,CAAC;IACD,MAAM,QAAQ,GAAG,mBAAmB,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;IAC1D,IAAI,QAAQ,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IACvF,OAAO,EAAE,GAAG,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,CAAC;AAC3E,CAAC;AAED,SAAS,eAAe,CAAC,KAAsB,EAAE,KAAsB;IACrE,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QACxD,MAAM,IAAI,KAAK,CAAC,kEAAkE,CAAC,CAAC;IACtF,CAAC;AACH,CAAC;AAED,SAAS,WAAW,CAAC,KAAc;IACjC,MAAM,KAAK,GAAG,KAAgD,CAAC;IAC/D,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACrG,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;AACtD,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,KAAyB,EACzB,KAAiC;IAEjC,MAAM,QAAQ,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;IAC1C,MAAM,UAAU,GAAG,uBAAuB,CAAC,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAC7E,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IACnD,IAAI,KAAsB,CAAC;IAC3B,IAAI,KAAsB,CAAC;IAC3B,IAAI,IAAmC,CAAC;IAExC,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC1C,MAAM,WAAW,GAAG,WAAW,CAAC,MAAM,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,CAAC;QAClF,MAAM,QAAQ,GAAG,mBAAmB,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QAC1D,MAAM,QAAQ,GAAG,KAAK,CAAC,SAAS,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC;QACvD,MAAM,SAAS,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,cAAc,CAAC,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;QACvF,KAAK,GAAG,WAAW,CAAC,SAAS,CAAC,OAAO,EAAE,UAAU,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;QACpE,IAAI,SAAS,CAAC,OAAO,EAAE,CAAC;YACtB,eAAe,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;YACpC,KAAK,GAAG,WAAW,CAAC;YACpB,IAAI,GAAG,aAAa,CAAC;QACvB,CAAC;aAAM,CAAC;YACN,KAAK,GAAG,WAAW,CAAC,MAAM,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,KAAK,CAAC,EAAE,QAAQ,CAAC,CAAC;YAC1E,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YAC9B,IAAI,GAAG,cAAc,CAAC;QACxB,CAAC;IACH,CAAC;SAAM,CAAC;QACN,KAAK,GAAG,WAAW,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;QACxD,MAAM,YAAY,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACzD,IAAI,YAAY,KAAK,IAAI,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;YACxD,MAAM,OAAO,GAAG,WAAW,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;YACpD,IAAI,WAAW,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;gBACzD,KAAK,GAAG,OAAO,CAAC;gBAChB,IAAI,GAAG,SAAS,CAAC;YACnB,CAAC;iBAAM,CAAC;gBACN,KAAK,GAAG,WAAW,CAAC,MAAM,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,KAAK,CAAC,EAAE,QAAQ,CAAC,CAAC;gBAC1E,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;gBAC9B,IAAI,GAAG,cAAc,CAAC;YACxB,CAAC;QACH,CAAC;aAAM,CAAC;YACN,KAAK,GAAG,WAAW,CAAC,MAAM,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,KAAK,CAAC,EAAE,QAAQ,CAAC,CAAC;YAC1E,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YAC9B,IAAI,GAAG,cAAc,CAAC;QACxB,CAAC;IACH,CAAC;IAED,MAAM,KAAK,GAAG,WAAW,CAAC,MAAM,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC;IACpE,MAAM,UAAU,GAAG,WAAW,CAAC,MAAM,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,CAAC;IAC9E,eAAe,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IACnC,OAAO;QACL,GAAG,QAAQ;QACX,SAAS,EAAE,UAAU,CAAC,SAAS;QAC/B,UAAU,EAAE,UAAU,CAAC,UAAU;QACjC,eAAe,EAAE,KAAK,CAAC,UAAU;QACjC,YAAY,EAAE,KAAK,CAAC,OAAO;QAC3B,SAAS,EAAE,KAAK,CAAC,QAAQ;QACzB,IAAI;QACJ,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM;QAC1C,YAAY,EAAE,KAAK,CAAC,KAAK;QACzB,QAAQ,EAAE,IAAI;KACf,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amalgm/live",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Portable realtime SDK for Amalgm — wire contracts, mutation discipline, and delivery state machines behind host-injected adapters.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -31,6 +31,10 @@
|
|
|
31
31
|
"./workspace": {
|
|
32
32
|
"types": "./dist/workspace/index.d.ts",
|
|
33
33
|
"default": "./dist/workspace/index.js"
|
|
34
|
+
},
|
|
35
|
+
"./registration": {
|
|
36
|
+
"types": "./dist/registration/index.d.ts",
|
|
37
|
+
"default": "./dist/registration/index.js"
|
|
34
38
|
}
|
|
35
39
|
},
|
|
36
40
|
"files": [
|