@interop/was-react 0.1.9 → 0.1.10
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 +80 -0
- package/dist/auth/loginFlow.d.ts +4 -2
- package/dist/auth/loginFlow.d.ts.map +1 -1
- package/dist/auth/loginFlow.js +1 -1
- package/dist/auth/loginFlow.js.map +1 -1
- package/dist/auth/loginRequest.d.ts +29 -3
- package/dist/auth/loginRequest.d.ts.map +1 -1
- package/dist/auth/loginRequest.js +12 -4
- package/dist/auth/loginRequest.js.map +1 -1
- package/dist/config.d.ts +15 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +0 -0
- package/dist/config.js.map +1 -1
- package/dist/identity/documentLoader.d.ts +2 -8
- package/dist/identity/documentLoader.d.ts.map +1 -1
- package/dist/identity/documentLoader.js +7 -88
- package/dist/identity/documentLoader.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/session/authStore.d.ts.map +1 -1
- package/dist/session/authStore.js +20 -9
- package/dist/session/authStore.js.map +1 -1
- package/dist/storage/entityStore.d.ts +20 -0
- package/dist/storage/entityStore.d.ts.map +1 -1
- package/dist/storage/entityStore.js +24 -1
- package/dist/storage/entityStore.js.map +1 -1
- package/dist/storage/localStore.d.ts +10 -0
- package/dist/storage/localStore.d.ts.map +1 -1
- package/dist/storage/localStore.js +24 -2
- package/dist/storage/localStore.js.map +1 -1
- package/dist/storage/publicUrl.d.ts +20 -0
- package/dist/storage/publicUrl.d.ts.map +1 -0
- package/dist/storage/publicUrl.js +33 -0
- package/dist/storage/publicUrl.js.map +1 -0
- package/dist/storage/storageManager.d.ts +28 -0
- package/dist/storage/storageManager.d.ts.map +1 -1
- package/dist/storage/storageManager.js +42 -2
- package/dist/storage/storageManager.js.map +1 -1
- package/dist/storage/wasRemoteStore.d.ts +91 -2
- package/dist/storage/wasRemoteStore.d.ts.map +1 -1
- package/dist/storage/wasRemoteStore.js +159 -9
- package/dist/storage/wasRemoteStore.js.map +1 -1
- package/dist/storage/wasSync.d.ts +2 -1
- package/dist/storage/wasSync.d.ts.map +1 -1
- package/dist/storage/wasSync.js +13 -6
- package/dist/storage/wasSync.js.map +1 -1
- package/package.json +8 -8
|
@@ -16,7 +16,12 @@
|
|
|
16
16
|
* RW zcap authorizes writing the collection description). It is non-fatal
|
|
17
17
|
* either way -- envelopes replicate into an unmarked (plaintext) collection
|
|
18
18
|
* just the same. A PUBLIC collection is never marked: public implies
|
|
19
|
-
* plaintext, so the marker PUT is skipped outright
|
|
19
|
+
* plaintext, so the marker PUT is skipped outright;
|
|
20
|
+
* - the sibling best-effort `indexes` declaration PUT for public collections
|
|
21
|
+
* that declare equality-indexed attributes, plus the equality query verb
|
|
22
|
+
* itself ({@link WasRemoteStore.queryCollectionByEquality}): the canonical
|
|
23
|
+
* sorted `filter[attr]=value` GET on the collection list endpoint, parsed
|
|
24
|
+
* into the `{ documents, hasMore, cursor? }` page shape.
|
|
20
25
|
*/
|
|
21
26
|
import type { ZcapClient } from '@interop/ezcap';
|
|
22
27
|
import type { IZcap } from '@interop/data-integrity-core';
|
|
@@ -37,12 +42,28 @@ export interface MarkerResult {
|
|
|
37
42
|
*/
|
|
38
43
|
skipped?: boolean;
|
|
39
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* One page of equality-query results: the shared shape of the GET
|
|
47
|
+
* `filter[attr]=value` filter and the POST `equality` query profile. `data` is
|
|
48
|
+
* the stored JSON content (absent for a blob resource); `custom` is the
|
|
49
|
+
* resource's custom metadata object, present when it has one. The opaque
|
|
50
|
+
* `cursor` continues the page walk when `hasMore` is true.
|
|
51
|
+
*/
|
|
52
|
+
export interface EqualityQueryPage {
|
|
53
|
+
documents: Array<{
|
|
54
|
+
id: string;
|
|
55
|
+
data?: unknown;
|
|
56
|
+
custom?: unknown;
|
|
57
|
+
}>;
|
|
58
|
+
hasMore: boolean;
|
|
59
|
+
cursor?: string;
|
|
60
|
+
}
|
|
40
61
|
export declare class WasRemoteStore {
|
|
41
62
|
readonly was: WasClient;
|
|
42
63
|
readonly serverUrl: string;
|
|
43
64
|
readonly spaceId: string;
|
|
44
65
|
private readonly _byCollectionId;
|
|
45
|
-
private readonly
|
|
66
|
+
private readonly _configById;
|
|
46
67
|
private constructor();
|
|
47
68
|
/**
|
|
48
69
|
* Builds a delegated remote store from a parsed grant set and the app's
|
|
@@ -84,5 +105,73 @@ export declare class WasRemoteStore {
|
|
|
84
105
|
* @returns {Promise<MarkerResult>}
|
|
85
106
|
*/
|
|
86
107
|
markCollectionEncrypted(collectionId: string): Promise<MarkerResult>;
|
|
108
|
+
/**
|
|
109
|
+
* Best-effort declaration of a public collection's equality-indexed
|
|
110
|
+
* attributes (`{ indexes: [...] }`) on its collection description, invoked
|
|
111
|
+
* with that collection's delegated RW zcap. The server rejects
|
|
112
|
+
* `filter[attr]=value` queries on undeclared attributes fail-closed, so a
|
|
113
|
+
* public collection that wants `store.query()` must announce its `indexes`
|
|
114
|
+
* here. Non-fatal like the encryption marker: returns the outcome rather
|
|
115
|
+
* than throwing. Skipped (reported `ok` + `skipped`) for a private
|
|
116
|
+
* collection or one that declares no indexes.
|
|
117
|
+
*
|
|
118
|
+
* @param collectionId {string} the WAS collection id
|
|
119
|
+
* @returns {Promise<MarkerResult>}
|
|
120
|
+
*/
|
|
121
|
+
declareCollectionIndexes(collectionId: string): Promise<MarkerResult>;
|
|
122
|
+
/**
|
|
123
|
+
* Runs one equality query against a public collection: the canonical GET
|
|
124
|
+
* `filter[attr]=value` form of the server's `equality` profile, invoked with
|
|
125
|
+
* the collection's delegated zcap (an anonymous reader would issue the same
|
|
126
|
+
* URL unsigned against a `PublicCanRead` collection). Filter attributes are
|
|
127
|
+
* emitted in sorted order so identical queries produce identical URLs
|
|
128
|
+
* (cache-friendly); values are string equality only. Fails closed before any
|
|
129
|
+
* network round trip on a non-public collection (the encrypted
|
|
130
|
+
* `blinded-index` path is not yet supported), an empty term set, or an
|
|
131
|
+
* attribute missing from the collection's declared `indexes`.
|
|
132
|
+
*
|
|
133
|
+
* @param options {object}
|
|
134
|
+
* @param options.collectionId {string} the WAS collection id
|
|
135
|
+
* @param options.equals {Record<string, string>} equality terms; multiple
|
|
136
|
+
* attributes AND together
|
|
137
|
+
* @param [options.limit] {number} page size (server default when omitted)
|
|
138
|
+
* @param [options.cursor] {string} opaque continuation cursor from the
|
|
139
|
+
* prior page
|
|
140
|
+
* @returns {Promise<EqualityQueryPage>}
|
|
141
|
+
*/
|
|
142
|
+
queryCollectionByEquality({ collectionId, equals, limit, cursor }: {
|
|
143
|
+
collectionId: string;
|
|
144
|
+
equals: Record<string, string>;
|
|
145
|
+
limit?: number;
|
|
146
|
+
cursor?: string;
|
|
147
|
+
}): Promise<EqualityQueryPage>;
|
|
148
|
+
/**
|
|
149
|
+
* The world-readable share URL for one document in a public (plaintext)
|
|
150
|
+
* collection: the exact URL an unauthenticated reader fetches (e.g. via
|
|
151
|
+
* `WasClient.publicRead`) to consume a share link. Because a public
|
|
152
|
+
* collection stores the payload under its own logical `id`, this URL is
|
|
153
|
+
* stable across edits of the document. Fails closed before composing
|
|
154
|
+
* anything on a non-public collection (the encrypted path stores under a
|
|
155
|
+
* random envelope id, so no stable public URL exists), an empty id, or a
|
|
156
|
+
* collection no delegated capability covers (catching typo'd or
|
|
157
|
+
* unprovisioned collection ids). The URL resolves publicly only once the
|
|
158
|
+
* document has replicated to the server -- a locally-inserted doc shares
|
|
159
|
+
* after the next sync push.
|
|
160
|
+
*
|
|
161
|
+
* @param options {object}
|
|
162
|
+
* @param options.collectionId {string} the WAS collection id
|
|
163
|
+
* @param options.id {string} the document's logical uuid
|
|
164
|
+
* @returns {string}
|
|
165
|
+
*/
|
|
166
|
+
publicUrlFor({ collectionId, id }: {
|
|
167
|
+
collectionId: string;
|
|
168
|
+
id: string;
|
|
169
|
+
}): string;
|
|
170
|
+
/**
|
|
171
|
+
* The shared best-effort collection-description PUT behind the encryption
|
|
172
|
+
* marker and the indexes declaration: invokes the collection's delegated RW
|
|
173
|
+
* zcap and reports the outcome rather than throwing.
|
|
174
|
+
*/
|
|
175
|
+
private _putDescription;
|
|
87
176
|
}
|
|
88
177
|
//# sourceMappingURL=wasRemoteStore.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wasRemoteStore.d.ts","sourceRoot":"","sources":["../../src/storage/wasRemoteStore.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH
|
|
1
|
+
{"version":3,"file":"wasRemoteStore.d.ts","sourceRoot":"","sources":["../../src/storage/wasRemoteStore.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAChD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,8BAA8B,CAAA;AACzD,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAG/C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAA;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAEhD;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,CAAA;IACpB,EAAE,EAAE,OAAO,CAAA;IACX,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAA;IAClE,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,qBAAa,cAAc;IACzB,SAAgB,GAAG,EAAE,SAAS,CAAA;IAC9B,SAAgB,SAAS,EAAE,MAAM,CAAA;IACjC,SAAgB,OAAO,EAAE,MAAM,CAAA;IAC/B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAuB;IAGvD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAG3B;IAED,OAAO;IAmBP;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,UAAU,CAAC,EAChB,MAAM,EACN,UAAU,EACV,WAAgB,EACjB,EAAE;QACD,MAAM,EAAE,YAAY,CAAA;QACpB,UAAU,EAAE,UAAU,CAAA;QACtB,WAAW,CAAC,EAAE,mBAAmB,EAAE,CAAA;KACpC,GAAG,cAAc;IAqBlB;;;;;;;OAOG;IACH,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS;IAI7D;;;;;;;;;;;OAWG;IACG,uBAAuB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAU1E;;;;;;;;;;;;OAYG;IACG,wBAAwB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAe3E;;;;;;;;;;;;;;;;;;;OAmBG;IACG,yBAAyB,CAAC,EAC9B,YAAY,EACZ,MAAM,EACN,KAAK,EACL,MAAM,EACP,EAAE;QACD,YAAY,EAAE,MAAM,CAAA;QACpB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QAC9B,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,MAAM,CAAC,EAAE,MAAM,CAAA;KAChB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAgE9B;;;;;;;;;;;;;;;;;OAiBG;IACH,YAAY,CAAC,EACX,YAAY,EACZ,EAAE,EACH,EAAE;QACD,YAAY,EAAE,MAAM,CAAA;QACpB,EAAE,EAAE,MAAM,CAAA;KACX,GAAG,MAAM;IAyBV;;;;OAIG;YACW,eAAe;CAyB9B"}
|
|
@@ -6,13 +6,15 @@ export class WasRemoteStore {
|
|
|
6
6
|
serverUrl;
|
|
7
7
|
spaceId;
|
|
8
8
|
_byCollectionId;
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
// Per WAS collection id: the effective visibility + declared equality
|
|
10
|
+
// indexes (the registry guarantees one declaration per id).
|
|
11
|
+
_configById;
|
|
12
|
+
constructor({ was, parsed, configById }) {
|
|
11
13
|
this.was = was;
|
|
12
14
|
this.serverUrl = parsed.serverUrl;
|
|
13
15
|
this.spaceId = parsed.spaceId;
|
|
14
16
|
this._byCollectionId = parsed.byCollectionId;
|
|
15
|
-
this.
|
|
17
|
+
this._configById = configById;
|
|
16
18
|
}
|
|
17
19
|
/**
|
|
18
20
|
* Builds a delegated remote store from a parsed grant set and the app's
|
|
@@ -33,10 +35,14 @@ export class WasRemoteStore {
|
|
|
33
35
|
zcapClient,
|
|
34
36
|
encryption: createEdvEncryption({ resolveKeys: async () => null })
|
|
35
37
|
});
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
const configById = new Map(collections.map(entry => [
|
|
39
|
+
entry.id,
|
|
40
|
+
{
|
|
41
|
+
visibility: entry.visibility ?? 'private',
|
|
42
|
+
...(entry.indexes && { indexes: entry.indexes })
|
|
43
|
+
}
|
|
44
|
+
]));
|
|
45
|
+
return new WasRemoteStore({ was, parsed, configById });
|
|
40
46
|
}
|
|
41
47
|
/**
|
|
42
48
|
* The delegated capability for one WAS collection, or `undefined` when no
|
|
@@ -62,9 +68,153 @@ export class WasRemoteStore {
|
|
|
62
68
|
* @returns {Promise<MarkerResult>}
|
|
63
69
|
*/
|
|
64
70
|
async markCollectionEncrypted(collectionId) {
|
|
65
|
-
if (this.
|
|
71
|
+
if (this._configById.get(collectionId)?.visibility === 'public') {
|
|
66
72
|
return { collectionId, ok: true, skipped: true };
|
|
67
73
|
}
|
|
74
|
+
return this._putDescription({
|
|
75
|
+
collectionId,
|
|
76
|
+
description: { id: collectionId, encryption: { scheme: 'edv' } }
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Best-effort declaration of a public collection's equality-indexed
|
|
81
|
+
* attributes (`{ indexes: [...] }`) on its collection description, invoked
|
|
82
|
+
* with that collection's delegated RW zcap. The server rejects
|
|
83
|
+
* `filter[attr]=value` queries on undeclared attributes fail-closed, so a
|
|
84
|
+
* public collection that wants `store.query()` must announce its `indexes`
|
|
85
|
+
* here. Non-fatal like the encryption marker: returns the outcome rather
|
|
86
|
+
* than throwing. Skipped (reported `ok` + `skipped`) for a private
|
|
87
|
+
* collection or one that declares no indexes.
|
|
88
|
+
*
|
|
89
|
+
* @param collectionId {string} the WAS collection id
|
|
90
|
+
* @returns {Promise<MarkerResult>}
|
|
91
|
+
*/
|
|
92
|
+
async declareCollectionIndexes(collectionId) {
|
|
93
|
+
const config = this._configById.get(collectionId);
|
|
94
|
+
if (config?.visibility !== 'public' ||
|
|
95
|
+
!config.indexes ||
|
|
96
|
+
config.indexes.length === 0) {
|
|
97
|
+
return { collectionId, ok: true, skipped: true };
|
|
98
|
+
}
|
|
99
|
+
return this._putDescription({
|
|
100
|
+
collectionId,
|
|
101
|
+
description: { id: collectionId, indexes: config.indexes }
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Runs one equality query against a public collection: the canonical GET
|
|
106
|
+
* `filter[attr]=value` form of the server's `equality` profile, invoked with
|
|
107
|
+
* the collection's delegated zcap (an anonymous reader would issue the same
|
|
108
|
+
* URL unsigned against a `PublicCanRead` collection). Filter attributes are
|
|
109
|
+
* emitted in sorted order so identical queries produce identical URLs
|
|
110
|
+
* (cache-friendly); values are string equality only. Fails closed before any
|
|
111
|
+
* network round trip on a non-public collection (the encrypted
|
|
112
|
+
* `blinded-index` path is not yet supported), an empty term set, or an
|
|
113
|
+
* attribute missing from the collection's declared `indexes`.
|
|
114
|
+
*
|
|
115
|
+
* @param options {object}
|
|
116
|
+
* @param options.collectionId {string} the WAS collection id
|
|
117
|
+
* @param options.equals {Record<string, string>} equality terms; multiple
|
|
118
|
+
* attributes AND together
|
|
119
|
+
* @param [options.limit] {number} page size (server default when omitted)
|
|
120
|
+
* @param [options.cursor] {string} opaque continuation cursor from the
|
|
121
|
+
* prior page
|
|
122
|
+
* @returns {Promise<EqualityQueryPage>}
|
|
123
|
+
*/
|
|
124
|
+
async queryCollectionByEquality({ collectionId, equals, limit, cursor }) {
|
|
125
|
+
const config = this._configById.get(collectionId);
|
|
126
|
+
if (config?.visibility !== 'public') {
|
|
127
|
+
throw new Error(`Equality queries require a public (plaintext) collection; ` +
|
|
128
|
+
`"${collectionId}" is not registered as public (the encrypted ` +
|
|
129
|
+
`blinded-index query path is not yet supported).`);
|
|
130
|
+
}
|
|
131
|
+
const attributes = Object.keys(equals);
|
|
132
|
+
if (attributes.length === 0) {
|
|
133
|
+
throw new Error('An equality query needs at least one term.');
|
|
134
|
+
}
|
|
135
|
+
const declared = new Set(config.indexes ?? []);
|
|
136
|
+
for (const name of attributes) {
|
|
137
|
+
if (!declared.has(name)) {
|
|
138
|
+
throw new Error(`Attribute "${name}" is not declared in collection ` +
|
|
139
|
+
`"${collectionId}" indexes; declare it in the collection config.`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const capability = this.collectionCapability(collectionId);
|
|
143
|
+
if (!capability) {
|
|
144
|
+
throw new Error(`No delegated capability covers collection "${collectionId}".`);
|
|
145
|
+
}
|
|
146
|
+
// Canonical query string: sorted filter attributes first, then the
|
|
147
|
+
// reserved pagination params. Literal brackets around a percent-encoded
|
|
148
|
+
// attribute name; the server decodes either spelling identically.
|
|
149
|
+
const params = attributes
|
|
150
|
+
.sort()
|
|
151
|
+
.map(name => `filter[${encodeURIComponent(name)}]=` +
|
|
152
|
+
encodeURIComponent(equals[name]));
|
|
153
|
+
if (limit !== undefined) {
|
|
154
|
+
params.push(`limit=${encodeURIComponent(String(limit))}`);
|
|
155
|
+
}
|
|
156
|
+
if (cursor !== undefined) {
|
|
157
|
+
params.push(`cursor=${encodeURIComponent(cursor)}`);
|
|
158
|
+
}
|
|
159
|
+
// The list endpoint is the trailing-slash collection items URL.
|
|
160
|
+
const response = await this.was.request({
|
|
161
|
+
capability,
|
|
162
|
+
path: `/space/${this.spaceId}/${collectionId}/?${params.join('&')}`,
|
|
163
|
+
method: 'GET'
|
|
164
|
+
});
|
|
165
|
+
const page = response.data;
|
|
166
|
+
if (!page || !Array.isArray(page.documents)) {
|
|
167
|
+
throw new Error(`Malformed equality query response for "${collectionId}": expected a ` +
|
|
168
|
+
`{ documents, hasMore } page.`);
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
documents: page.documents,
|
|
172
|
+
hasMore: page.hasMore === true,
|
|
173
|
+
...(typeof page.cursor === 'string' && { cursor: page.cursor })
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* The world-readable share URL for one document in a public (plaintext)
|
|
178
|
+
* collection: the exact URL an unauthenticated reader fetches (e.g. via
|
|
179
|
+
* `WasClient.publicRead`) to consume a share link. Because a public
|
|
180
|
+
* collection stores the payload under its own logical `id`, this URL is
|
|
181
|
+
* stable across edits of the document. Fails closed before composing
|
|
182
|
+
* anything on a non-public collection (the encrypted path stores under a
|
|
183
|
+
* random envelope id, so no stable public URL exists), an empty id, or a
|
|
184
|
+
* collection no delegated capability covers (catching typo'd or
|
|
185
|
+
* unprovisioned collection ids). The URL resolves publicly only once the
|
|
186
|
+
* document has replicated to the server -- a locally-inserted doc shares
|
|
187
|
+
* after the next sync push.
|
|
188
|
+
*
|
|
189
|
+
* @param options {object}
|
|
190
|
+
* @param options.collectionId {string} the WAS collection id
|
|
191
|
+
* @param options.id {string} the document's logical uuid
|
|
192
|
+
* @returns {string}
|
|
193
|
+
*/
|
|
194
|
+
publicUrlFor({ collectionId, id }) {
|
|
195
|
+
const config = this._configById.get(collectionId);
|
|
196
|
+
if (config?.visibility !== 'public') {
|
|
197
|
+
throw new Error(`Public share URLs require a public (plaintext) collection; ` +
|
|
198
|
+
`"${collectionId}" is not registered as public (a private ` +
|
|
199
|
+
`collection stores documents under a random envelope id, so no ` +
|
|
200
|
+
`stable public URL exists).`);
|
|
201
|
+
}
|
|
202
|
+
if (typeof id !== 'string' || id.length === 0) {
|
|
203
|
+
throw new Error('A public share URL needs a non-empty document id.');
|
|
204
|
+
}
|
|
205
|
+
const capability = this.collectionCapability(collectionId);
|
|
206
|
+
if (!capability) {
|
|
207
|
+
throw new Error(`No delegated capability covers collection "${collectionId}".`);
|
|
208
|
+
}
|
|
209
|
+
return (`${this.serverUrl}/space/${this.spaceId}/${collectionId}/` +
|
|
210
|
+
`${encodeURIComponent(id)}`);
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* The shared best-effort collection-description PUT behind the encryption
|
|
214
|
+
* marker and the indexes declaration: invokes the collection's delegated RW
|
|
215
|
+
* zcap and reports the outcome rather than throwing.
|
|
216
|
+
*/
|
|
217
|
+
async _putDescription({ collectionId, description }) {
|
|
68
218
|
const capability = this.collectionCapability(collectionId);
|
|
69
219
|
if (!capability) {
|
|
70
220
|
return { collectionId, ok: false, error: 'no capability' };
|
|
@@ -74,7 +224,7 @@ export class WasRemoteStore {
|
|
|
74
224
|
capability,
|
|
75
225
|
path: `/space/${this.spaceId}/${collectionId}`,
|
|
76
226
|
method: 'PUT',
|
|
77
|
-
json:
|
|
227
|
+
json: description
|
|
78
228
|
});
|
|
79
229
|
return { collectionId, ok: true, status: response.status };
|
|
80
230
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wasRemoteStore.js","sourceRoot":"","sources":["../../src/storage/wasRemoteStore.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"wasRemoteStore.js","sourceRoot":"","sources":["../../src/storage/wasRemoteStore.ts"],"names":[],"mappings":"AA2BA,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAC/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAA;AAC7D,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAgC5D,MAAM,OAAO,cAAc;IACT,GAAG,CAAW;IACd,SAAS,CAAQ;IACjB,OAAO,CAAQ;IACd,eAAe,CAAuB;IACvD,sEAAsE;IACtE,4DAA4D;IAC3C,WAAW,CAG3B;IAED,YAAoB,EAClB,GAAG,EACH,MAAM,EACN,UAAU,EAQX;QACC,IAAI,CAAC,GAAG,GAAG,GAAG,CAAA;QACd,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAA;QACjC,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,CAAA;QAC7B,IAAI,CAAC,eAAe,GAAG,MAAM,CAAC,cAAc,CAAA;QAC5C,IAAI,CAAC,WAAW,GAAG,UAAU,CAAA;IAC/B,CAAC;IAED;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,UAAU,CAAC,EAChB,MAAM,EACN,UAAU,EACV,WAAW,GAAG,EAAE,EAKjB;QACC,MAAM,GAAG,GAAG,IAAI,SAAS,CAAC;YACxB,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,UAAU;YACV,UAAU,EAAE,mBAAmB,CAAC,EAAE,WAAW,EAAE,KAAK,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC;SACnE,CAAC,CAAA;QACF,MAAM,UAAU,GAAG,IAAI,GAAG,CAIxB,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YACvB,KAAK,CAAC,EAAE;YACR;gBACE,UAAU,EAAE,KAAK,CAAC,UAAU,IAAI,SAAS;gBACzC,GAAG,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;aACjD;SACF,CAAC,CACH,CAAA;QACD,OAAO,IAAI,cAAc,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAA;IACxD,CAAC;IAED;;;;;;;OAOG;IACH,oBAAoB,CAAC,YAAoB;QACvC,OAAO,IAAI,CAAC,eAAe,CAAC,YAAY,CAAC,CAAA;IAC3C,CAAC;IAED;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,uBAAuB,CAAC,YAAoB;QAChD,IAAI,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,UAAU,KAAK,QAAQ,EAAE,CAAC;YAChE,OAAO,EAAE,YAAY,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;QAClD,CAAC;QACD,OAAO,IAAI,CAAC,eAAe,CAAC;YAC1B,YAAY;YACZ,WAAW,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE,UAAU,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE;SACjE,CAAC,CAAA;IACJ,CAAC;IAED;;;;;;;;;;;;OAYG;IACH,KAAK,CAAC,wBAAwB,CAAC,YAAoB;QACjD,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,YAAY,CAAC,CAAA;QACjD,IACE,MAAM,EAAE,UAAU,KAAK,QAAQ;YAC/B,CAAC,MAAM,CAAC,OAAO;YACf,MAAM,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAC3B,CAAC;YACD,OAAO,EAAE,YAAY,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;QAClD,CAAC;QACD,OAAO,IAAI,CAAC,eAAe,CAAC;YAC1B,YAAY;YACZ,WAAW,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE;SAC3D,CAAC,CAAA;IACJ,CAAC;IAED;;;;;;;;;;;;;;;;;;;OAmBG;IACH,KAAK,CAAC,yBAAyB,CAAC,EAC9B,YAAY,EACZ,MAAM,EACN,KAAK,EACL,MAAM,EAMP;QACC,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,YAAY,CAAC,CAAA;QACjD,IAAI,MAAM,EAAE,UAAU,KAAK,QAAQ,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CACb,4DAA4D;gBAC1D,IAAI,YAAY,+CAA+C;gBAC/D,iDAAiD,CACpD,CAAA;QACH,CAAC;QACD,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACtC,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC5B,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAA;QAC/D,CAAC;QACD,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAA;QAC9C,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;YAC9B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBACxB,MAAM,IAAI,KAAK,CACb,cAAc,IAAI,kCAAkC;oBAClD,IAAI,YAAY,iDAAiD,CACpE,CAAA;YACH,CAAC;QACH,CAAC;QACD,MAAM,UAAU,GAAG,IAAI,CAAC,oBAAoB,CAAC,YAAY,CAAC,CAAA;QAC1D,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CACb,8CAA8C,YAAY,IAAI,CAC/D,CAAA;QACH,CAAC;QACD,mEAAmE;QACnE,wEAAwE;QACxE,kEAAkE;QAClE,MAAM,MAAM,GAAG,UAAU;aACtB,IAAI,EAAE;aACN,GAAG,CACF,IAAI,CAAC,EAAE,CACL,UAAU,kBAAkB,CAAC,IAAI,CAAC,IAAI;YACtC,kBAAkB,CAAC,MAAM,CAAC,IAAI,CAAW,CAAC,CAC7C,CAAA;QACH,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,MAAM,CAAC,IAAI,CAAC,SAAS,kBAAkB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAA;QAC3D,CAAC;QACD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,MAAM,CAAC,IAAI,CAAC,UAAU,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;QACrD,CAAC;QACD,gEAAgE;QAChE,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC;YACtC,UAAU;YACV,IAAI,EAAE,UAAU,IAAI,CAAC,OAAO,IAAI,YAAY,KAAK,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;YACnE,MAAM,EAAE,KAAK;SACd,CAAC,CAAA;QACF,MAAM,IAAI,GAAG,QAAQ,CAAC,IAA8C,CAAA;QACpE,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;YAC5C,MAAM,IAAI,KAAK,CACb,0CAA0C,YAAY,gBAAgB;gBACpE,8BAA8B,CACjC,CAAA;QACH,CAAC;QACD,OAAO;YACL,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,OAAO,EAAE,IAAI,CAAC,OAAO,KAAK,IAAI;YAC9B,GAAG,CAAC,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;SAChE,CAAA;IACH,CAAC;IAED;;;;;;;;;;;;;;;;;OAiBG;IACH,YAAY,CAAC,EACX,YAAY,EACZ,EAAE,EAIH;QACC,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,YAAY,CAAC,CAAA;QACjD,IAAI,MAAM,EAAE,UAAU,KAAK,QAAQ,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CACb,6DAA6D;gBAC3D,IAAI,YAAY,2CAA2C;gBAC3D,gEAAgE;gBAChE,4BAA4B,CAC/B,CAAA;QACH,CAAC;QACD,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC9C,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAA;QACtE,CAAC;QACD,MAAM,UAAU,GAAG,IAAI,CAAC,oBAAoB,CAAC,YAAY,CAAC,CAAA;QAC1D,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CACb,8CAA8C,YAAY,IAAI,CAC/D,CAAA;QACH,CAAC;QACD,OAAO,CACL,GAAG,IAAI,CAAC,SAAS,UAAU,IAAI,CAAC,OAAO,IAAI,YAAY,GAAG;YAC1D,GAAG,kBAAkB,CAAC,EAAE,CAAC,EAAE,CAC5B,CAAA;IACH,CAAC;IAED;;;;OAIG;IACK,KAAK,CAAC,eAAe,CAAC,EAC5B,YAAY,EACZ,WAAW,EAIZ;QACC,MAAM,UAAU,GAAG,IAAI,CAAC,oBAAoB,CAAC,YAAY,CAAC,CAAA;QAC1D,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,OAAO,EAAE,YAAY,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,eAAe,EAAE,CAAA;QAC5D,CAAC;QACD,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC;gBACtC,UAAU;gBACV,IAAI,EAAE,UAAU,IAAI,CAAC,OAAO,IAAI,YAAY,EAAE;gBAC9C,MAAM,EAAE,KAAK;gBACb,IAAI,EAAE,WAAW;aAClB,CAAC,CAAA;YACF,OAAO,EAAE,YAAY,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAA;QAC5D,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,CAAA;YAC/B,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,CAAA;YACjC,OAAO,EAAE,YAAY,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,CAAA;QAC5D,CAAC;IACH,CAAC;CACF"}
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
/**
|
|
5
5
|
* Shared WAS replication bootstrap: given a parsed grant set and the invoking
|
|
6
6
|
* ZcapClient, builds the delegated {@link WasRemoteStore}, best-effort marks
|
|
7
|
-
* each collection encrypted
|
|
7
|
+
* each private collection encrypted and declares each public collection's
|
|
8
|
+
* equality `indexes`, and starts the supplied {@link SyncController} with
|
|
8
9
|
* reactive store patching. The caller injects the opened localStore, the
|
|
9
10
|
* controller, and the per-doc `onRemoteChange` patcher (typically wired to the
|
|
10
11
|
* rehydrate mechanism over the app's store registry) rather than this module
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wasSync.d.ts","sourceRoot":"","sources":["../../src/storage/wasSync.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH
|
|
1
|
+
{"version":3,"file":"wasSync.d.ts","sourceRoot":"","sources":["../../src/storage/wasSync.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAChD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACjD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAA;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAEzD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,YAAY,CAAC,EACjC,MAAM,EACN,UAAU,EACV,WAAW,EACX,UAAU,EACV,cAAc,EACd,cAAc,EACd,WAAW,EACZ,EAAE;IACD,MAAM,EAAE,YAAY,CAAA;IACpB,UAAU,EAAE,UAAU,CAAA;IACtB,WAAW,EAAE,mBAAmB,EAAE,CAAA;IAClC,UAAU,EAAE,UAAU,CAAA;IACtB,cAAc,EAAE,cAAc,CAAA;IAC9B,cAAc,EAAE,CACd,aAAa,EAAE,MAAM,EACrB,KAAK,EAAE,aAAa,CAAC,SAAS,CAAC,KAC5B,IAAI,CAAA;IACT,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;CACzB,GAAG,OAAO,CAAC,cAAc,CAAC,CAqC1B"}
|
package/dist/storage/wasSync.js
CHANGED
|
@@ -21,13 +21,20 @@ export async function startWasSync({ parsed, zcapClient, collections, localStore
|
|
|
21
21
|
zcapClient,
|
|
22
22
|
collections
|
|
23
23
|
});
|
|
24
|
-
// Best-effort
|
|
25
|
-
// into an unmarked collection just the same
|
|
26
|
-
//
|
|
24
|
+
// Best-effort collection-description PUTs; non-fatal either way (envelopes
|
|
25
|
+
// replicate into an unmarked collection just the same, and a query against
|
|
26
|
+
// undeclared indexes fails with a descriptive 400). Each helper skips the
|
|
27
|
+
// collections it does not apply to (reported ok + skipped): the encryption
|
|
28
|
+
// marker skips public collections, the indexes declaration skips private
|
|
29
|
+
// ones and public ones with no declared indexes.
|
|
27
30
|
await Promise.all(Object.keys(parsed.byCollectionId).map(async (collectionId) => {
|
|
28
|
-
const
|
|
29
|
-
if (!
|
|
30
|
-
console.warn(`Encryption marker PUT not authorized for "${collectionId}" (status ${
|
|
31
|
+
const marker = await remoteStore.markCollectionEncrypted(collectionId);
|
|
32
|
+
if (!marker.ok) {
|
|
33
|
+
console.warn(`Encryption marker PUT not authorized for "${collectionId}" (status ${marker.status ?? 'n/a'}).`);
|
|
34
|
+
}
|
|
35
|
+
const indexes = await remoteStore.declareCollectionIndexes(collectionId);
|
|
36
|
+
if (!indexes.ok) {
|
|
37
|
+
console.warn(`Indexes declaration PUT not authorized for "${collectionId}" (status ${indexes.status ?? 'n/a'}).`);
|
|
31
38
|
}
|
|
32
39
|
}));
|
|
33
40
|
await syncController.start({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wasSync.js","sourceRoot":"","sources":["../../src/storage/wasSync.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"wasSync.js","sourceRoot":"","sources":["../../src/storage/wasSync.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAIpD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,EACjC,MAAM,EACN,UAAU,EACV,WAAW,EACX,UAAU,EACV,cAAc,EACd,cAAc,EACd,WAAW,EAYZ;IACC,MAAM,WAAW,GAAG,cAAc,CAAC,UAAU,CAAC;QAC5C,MAAM;QACN,UAAU;QACV,WAAW;KACZ,CAAC,CAAA;IAEF,2EAA2E;IAC3E,2EAA2E;IAC3E,0EAA0E;IAC1E,2EAA2E;IAC3E,yEAAyE;IACzE,iDAAiD;IACjD,MAAM,OAAO,CAAC,GAAG,CACf,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,GAAG,CAAC,KAAK,EAAC,YAAY,EAAC,EAAE;QAC1D,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,uBAAuB,CAAC,YAAY,CAAC,CAAA;QACtE,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,CACV,6CAA6C,YAAY,aAAa,MAAM,CAAC,MAAM,IAAI,KAAK,IAAI,CACjG,CAAA;QACH,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,wBAAwB,CAAC,YAAY,CAAC,CAAA;QACxE,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC;YAChB,OAAO,CAAC,IAAI,CACV,+CAA+C,YAAY,aAAa,OAAO,CAAC,MAAM,IAAI,KAAK,IAAI,CACpG,CAAA;QACH,CAAC;IACH,CAAC,CAAC,CACH,CAAA;IAED,MAAM,cAAc,CAAC,KAAK,CAAC;QACzB,WAAW;QACX,UAAU;QACV,cAAc;QACd,GAAG,CAAC,WAAW,IAAI,EAAE,WAAW,EAAE,CAAC;KACpC,CAAC,CAAA;IACF,OAAO,WAAW,CAAA;AACpB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@interop/was-react",
|
|
3
3
|
"description": "React library for building 'Bring Your Own Everything' (BYOE) apps on Wallet Attached Storage: DID Auth login via CHAPI wallet, local-first encrypted storage, and background sync to a WAS server.",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.10",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"build": "pnpm run clear && tsc",
|
|
@@ -49,15 +49,15 @@
|
|
|
49
49
|
"types": "dist/index.d.ts",
|
|
50
50
|
"sideEffects": false,
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@interop/data-integrity-core": "^8.
|
|
53
|
-
"@interop/ed25519-signature": "^7.1.
|
|
54
|
-
"@interop/ezcap": "^7.
|
|
55
|
-
"@interop/security-document-loader": "^9.4.
|
|
52
|
+
"@interop/data-integrity-core": "^8.3.0",
|
|
53
|
+
"@interop/ed25519-signature": "^7.1.4",
|
|
54
|
+
"@interop/ezcap": "^7.4.1",
|
|
55
|
+
"@interop/security-document-loader": "^9.4.4",
|
|
56
56
|
"@interop/vc": "^11.0.5",
|
|
57
57
|
"@interop/verifier-core": "^3.3.1",
|
|
58
|
-
"@interop/was-client": "^0.
|
|
58
|
+
"@interop/was-client": "^0.16.0",
|
|
59
59
|
"@interop/webkms-client": "^14.7.1",
|
|
60
|
-
"@interop/x25519-key-agreement-key": "^5.2.
|
|
60
|
+
"@interop/x25519-key-agreement-key": "^5.2.1",
|
|
61
61
|
"@scure/base": "^2.2.0",
|
|
62
62
|
"credential-handler-polyfill": "^4.1.0",
|
|
63
63
|
"uuidv7": "^1.2.1"
|
|
@@ -111,7 +111,7 @@
|
|
|
111
111
|
"typescript-eslint": "^8.59.4",
|
|
112
112
|
"vite": "^8.0.14",
|
|
113
113
|
"vitest": "^4.1.7",
|
|
114
|
-
"was-teaching-server": "0.
|
|
114
|
+
"was-teaching-server": "^0.11.0",
|
|
115
115
|
"zustand": "^5.0.12"
|
|
116
116
|
},
|
|
117
117
|
"publishConfig": {
|