jazz-tools 0.18.25 → 0.18.27
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/.turbo/turbo-build.log +41 -41
- package/CHANGELOG.md +21 -0
- package/dist/{chunk-DOCEAUVD.js → chunk-ZIAN4UY5.js} +338 -9
- package/dist/chunk-ZIAN4UY5.js.map +1 -0
- package/dist/index.js +97 -5
- package/dist/index.js.map +1 -1
- package/dist/media/{chunk-W3S526L3.js → chunk-K6GCHLQU.js} +1 -1
- package/dist/media/chunk-K6GCHLQU.js.map +1 -0
- package/dist/media/create-image/browser.d.ts +1 -1
- package/dist/media/create-image/react-native.d.ts +1 -1
- package/dist/media/create-image/react-native.d.ts.map +1 -1
- package/dist/media/create-image/server.d.ts +1 -1
- package/dist/media/create-image-factory.d.ts +5 -2
- package/dist/media/create-image-factory.d.ts.map +1 -1
- package/dist/media/index.browser.js +1 -1
- package/dist/media/index.d.ts +3 -4
- package/dist/media/index.d.ts.map +1 -1
- package/dist/media/index.js +1 -1
- package/dist/media/index.native.js +63 -28
- package/dist/media/index.native.js.map +1 -1
- package/dist/media/index.server.js +1 -1
- package/dist/react/index.js.map +1 -1
- package/dist/testing.js +1 -1
- package/dist/tools/coValues/coVector.d.ts +127 -0
- package/dist/tools/coValues/coVector.d.ts.map +1 -0
- package/dist/tools/coValues/request.d.ts +70 -0
- package/dist/tools/coValues/request.d.ts.map +1 -1
- package/dist/tools/exports.d.ts +1 -1
- package/dist/tools/exports.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/coExport.d.ts +2 -1
- package/dist/tools/implementation/zodSchema/coExport.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/runtimeConverters/coValueSchemaTransformation.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/schemaTypes/CoVectorSchema.d.ts +47 -0
- package/dist/tools/implementation/zodSchema/schemaTypes/CoVectorSchema.d.ts.map +1 -0
- package/dist/tools/implementation/zodSchema/typeConverters/InstanceOfSchema.d.ts +2 -2
- package/dist/tools/implementation/zodSchema/typeConverters/InstanceOfSchema.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/typeConverters/InstanceOfSchemaCoValuesNullable.d.ts +2 -2
- package/dist/tools/implementation/zodSchema/typeConverters/InstanceOfSchemaCoValuesNullable.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/zodCo.d.ts +2 -1
- package/dist/tools/implementation/zodSchema/zodCo.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/zodSchema.d.ts +3 -2
- package/dist/tools/implementation/zodSchema/zodSchema.d.ts.map +1 -1
- package/dist/tools/internal.d.ts +2 -0
- package/dist/tools/internal.d.ts.map +1 -1
- package/dist/tools/tests/authenticate-request.test.d.ts +2 -0
- package/dist/tools/tests/authenticate-request.test.d.ts.map +1 -0
- package/dist/tools/tests/coVector.test-d.d.ts +2 -0
- package/dist/tools/tests/coVector.test-d.d.ts.map +1 -0
- package/dist/tools/tests/coVector.test.d.ts +2 -0
- package/dist/tools/tests/coVector.test.d.ts.map +1 -0
- package/package.json +8 -4
- package/src/media/create-image/react-native.ts +75 -30
- package/src/media/create-image-factory.test.ts +18 -0
- package/src/media/create-image-factory.ts +6 -1
- package/src/media/index.ts +7 -4
- package/src/tools/coValues/coVector.ts +432 -0
- package/src/tools/coValues/request.ts +188 -4
- package/src/tools/exports.ts +3 -0
- package/src/tools/implementation/zodSchema/coExport.ts +2 -0
- package/src/tools/implementation/zodSchema/runtimeConverters/coValueSchemaTransformation.ts +13 -0
- package/src/tools/implementation/zodSchema/schemaTypes/CoVectorSchema.ts +105 -0
- package/src/tools/implementation/zodSchema/typeConverters/InstanceOfSchema.ts +9 -5
- package/src/tools/implementation/zodSchema/typeConverters/InstanceOfSchemaCoValuesNullable.ts +15 -9
- package/src/tools/implementation/zodSchema/zodCo.ts +15 -0
- package/src/tools/implementation/zodSchema/zodSchema.ts +15 -6
- package/src/tools/internal.ts +2 -0
- package/src/tools/tests/authenticate-request.test.ts +194 -0
- package/src/tools/tests/coVector.test-d.ts +40 -0
- package/src/tools/tests/coVector.test.ts +891 -0
- package/dist/chunk-DOCEAUVD.js.map +0 -1
- package/dist/media/chunk-W3S526L3.js.map +0 -1
package/src/media/index.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import type { ImageDefinition } from "jazz-tools";
|
|
2
|
-
import {
|
|
1
|
+
import type { ImageDefinition, Loaded } from "jazz-tools";
|
|
2
|
+
import type {
|
|
3
|
+
CreateImageOptions,
|
|
4
|
+
CreateImageReturnType,
|
|
5
|
+
} from "./create-image-factory";
|
|
3
6
|
|
|
4
7
|
export * from "./exports";
|
|
5
8
|
|
|
@@ -38,7 +41,7 @@ export * from "./exports";
|
|
|
38
41
|
export declare function createImage(
|
|
39
42
|
imageBlobOrFile: Blob | File,
|
|
40
43
|
options?: CreateImageOptions,
|
|
41
|
-
): Promise<
|
|
44
|
+
): Promise<CreateImageReturnType>;
|
|
42
45
|
|
|
43
46
|
/**
|
|
44
47
|
* Creates an ImageDefinition from an image file path with built-in UX features.
|
|
@@ -66,4 +69,4 @@ export declare function createImage(
|
|
|
66
69
|
export declare function createImage(
|
|
67
70
|
filePath: string,
|
|
68
71
|
options?: CreateImageOptions,
|
|
69
|
-
): Promise<
|
|
72
|
+
): Promise<CreateImageReturnType>;
|
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
import type { RawBinaryCoStream } from "cojson";
|
|
2
|
+
import { cojsonInternals } from "cojson";
|
|
3
|
+
import {
|
|
4
|
+
AnonymousJazzAgent,
|
|
5
|
+
CoValue,
|
|
6
|
+
CoValueClass,
|
|
7
|
+
getCoValueOwner,
|
|
8
|
+
Group,
|
|
9
|
+
ID,
|
|
10
|
+
Resolved,
|
|
11
|
+
SubscribeListenerOptions,
|
|
12
|
+
SubscribeRestArgs,
|
|
13
|
+
TypeSym,
|
|
14
|
+
} from "../internal.js";
|
|
15
|
+
import {
|
|
16
|
+
Account,
|
|
17
|
+
CoValueJazzApi,
|
|
18
|
+
inspect,
|
|
19
|
+
loadCoValueWithoutMe,
|
|
20
|
+
parseCoValueCreateOptions,
|
|
21
|
+
parseSubscribeRestArgs,
|
|
22
|
+
subscribeToCoValueWithoutMe,
|
|
23
|
+
subscribeToExistingCoValue,
|
|
24
|
+
} from "../internal.js";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* CoVectors are collaborative storages of vectors (floating point arrays).
|
|
28
|
+
*
|
|
29
|
+
* @category CoValues
|
|
30
|
+
*/
|
|
31
|
+
export class CoVector
|
|
32
|
+
extends Float32Array
|
|
33
|
+
implements Readonly<Float32Array>, CoValue
|
|
34
|
+
{
|
|
35
|
+
declare $jazz: CoVectorJazzApi<this>;
|
|
36
|
+
|
|
37
|
+
/** @category Type Helpers */
|
|
38
|
+
declare [TypeSym]: "BinaryCoStream";
|
|
39
|
+
|
|
40
|
+
static get [Symbol.species]() {
|
|
41
|
+
return Float32Array;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
protected static requiredDimensionsCount: number | undefined = undefined;
|
|
45
|
+
private declare _isVectorLoaded: boolean;
|
|
46
|
+
private declare _requiredDimensionsCount: number;
|
|
47
|
+
|
|
48
|
+
constructor(
|
|
49
|
+
options:
|
|
50
|
+
| {
|
|
51
|
+
owner: Account | Group;
|
|
52
|
+
}
|
|
53
|
+
| {
|
|
54
|
+
fromRaw: RawBinaryCoStream;
|
|
55
|
+
},
|
|
56
|
+
) {
|
|
57
|
+
const dimensionsCount = (new.target as typeof CoVector)
|
|
58
|
+
.requiredDimensionsCount;
|
|
59
|
+
|
|
60
|
+
if (dimensionsCount === undefined) {
|
|
61
|
+
throw new Error(
|
|
62
|
+
"Instantiating CoVector without a dimensions count is not allowed. Use co.vector(...).create() instead.",
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Initialize empty Float32Array buffer with the expected vector length
|
|
67
|
+
// to be filled with the vector data later
|
|
68
|
+
super(dimensionsCount);
|
|
69
|
+
|
|
70
|
+
const isFromRaw = "fromRaw" in options;
|
|
71
|
+
|
|
72
|
+
const raw: RawBinaryCoStream = isFromRaw
|
|
73
|
+
? options.fromRaw
|
|
74
|
+
: options.owner.$jazz.raw.createBinaryStream();
|
|
75
|
+
|
|
76
|
+
Object.defineProperties(this, {
|
|
77
|
+
[TypeSym]: { value: "BinaryCoStream", enumerable: false },
|
|
78
|
+
$jazz: {
|
|
79
|
+
value: new CoVectorJazzApi(this, raw),
|
|
80
|
+
enumerable: false,
|
|
81
|
+
},
|
|
82
|
+
_isVectorLoaded: { value: false, enumerable: false, writable: true },
|
|
83
|
+
_requiredDimensionsCount: {
|
|
84
|
+
value: dimensionsCount,
|
|
85
|
+
enumerable: false,
|
|
86
|
+
writable: false,
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
if (isFromRaw) {
|
|
91
|
+
this.loadVectorData();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** @category Internals */
|
|
96
|
+
static fromRaw<V extends CoVector>(
|
|
97
|
+
this: CoValueClass<V> & typeof CoVector,
|
|
98
|
+
raw: RawBinaryCoStream,
|
|
99
|
+
) {
|
|
100
|
+
return new this({ fromRaw: raw });
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Create a new `CoVector` instance with the given vector.
|
|
105
|
+
*
|
|
106
|
+
* @category Creation
|
|
107
|
+
* @deprecated Use `co.vector(...).create` instead.
|
|
108
|
+
*/
|
|
109
|
+
static create<S extends CoVector>(
|
|
110
|
+
this: CoValueClass<S> & typeof CoVector,
|
|
111
|
+
vector: number[] | Float32Array,
|
|
112
|
+
options?: { owner?: Account | Group } | Account | Group,
|
|
113
|
+
) {
|
|
114
|
+
const vectorAsFloat32Array =
|
|
115
|
+
vector instanceof Float32Array ? vector : new Float32Array(vector);
|
|
116
|
+
|
|
117
|
+
const givenVectorDimensions =
|
|
118
|
+
vectorAsFloat32Array.byteLength / vectorAsFloat32Array.BYTES_PER_ELEMENT;
|
|
119
|
+
|
|
120
|
+
if (
|
|
121
|
+
this.requiredDimensionsCount !== undefined &&
|
|
122
|
+
givenVectorDimensions !== this.requiredDimensionsCount
|
|
123
|
+
) {
|
|
124
|
+
throw new Error(
|
|
125
|
+
`Vector dimension mismatch! Expected ${this.requiredDimensionsCount} dimensions, got ${
|
|
126
|
+
givenVectorDimensions
|
|
127
|
+
}`,
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const coVector = new this(parseCoValueCreateOptions(options));
|
|
132
|
+
coVector.setVectorData(vectorAsFloat32Array);
|
|
133
|
+
|
|
134
|
+
const byteArray = CoVector.toByteArray(vectorAsFloat32Array);
|
|
135
|
+
|
|
136
|
+
coVector.$jazz.raw.startBinaryStream({
|
|
137
|
+
mimeType: "application/vector+octet-stream",
|
|
138
|
+
totalSizeBytes: byteArray.byteLength,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
const chunkSize =
|
|
142
|
+
cojsonInternals.TRANSACTION_CONFIG.MAX_RECOMMENDED_TX_SIZE;
|
|
143
|
+
|
|
144
|
+
// Although most embedding vectors are small
|
|
145
|
+
// (3072-dimensional vector is only 12,288 bytes),
|
|
146
|
+
// we should still chunk the data to avoid transaction size limits
|
|
147
|
+
for (let idx = 0; idx < byteArray.length; idx += chunkSize) {
|
|
148
|
+
coVector.$jazz.raw.pushBinaryStreamChunk(
|
|
149
|
+
byteArray.slice(idx, idx + chunkSize),
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
coVector.$jazz.raw.endBinaryStream();
|
|
153
|
+
|
|
154
|
+
return coVector;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
private static toByteArray(vector: Float32Array): Uint8Array {
|
|
158
|
+
// zero copy view of the vector bytes
|
|
159
|
+
return new Uint8Array(vector.buffer, vector.byteOffset, vector.byteLength);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
private static fromByteArray(bytesChunks: Uint8Array[]): Float32Array {
|
|
163
|
+
const total = bytesChunks.reduce((acc, c) => acc + c.byteLength, 0);
|
|
164
|
+
|
|
165
|
+
if (total % 4 !== 0)
|
|
166
|
+
throw new Error("[INTERNAL] Total byte length must be multiple of 4");
|
|
167
|
+
|
|
168
|
+
const u8 = new Uint8Array(total);
|
|
169
|
+
let off = 0;
|
|
170
|
+
|
|
171
|
+
for (const c of bytesChunks) {
|
|
172
|
+
u8.set(c, off);
|
|
173
|
+
off += c.byteLength;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return new Float32Array(u8.buffer, u8.byteOffset, total / 4);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
private loadVectorData(): void {
|
|
180
|
+
if (this._isVectorLoaded === true) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const chunks = this.$jazz.raw.getBinaryChunks();
|
|
185
|
+
|
|
186
|
+
if (!chunks) {
|
|
187
|
+
// This should never happen
|
|
188
|
+
throw new Error(`CoVector '${this.$jazz.raw.id}' is not loaded`);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const vector = CoVector.fromByteArray(chunks.chunks);
|
|
192
|
+
|
|
193
|
+
if (vector.length !== this._requiredDimensionsCount) {
|
|
194
|
+
throw new Error(
|
|
195
|
+
`Vector dimension mismatch! CoVector '${this.$jazz.raw.id}' loaded with ${vector.length} dimensions, but the schema requires ${this._requiredDimensionsCount} dimensions`,
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
this.setVectorData(vector);
|
|
200
|
+
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
private setVectorData(vector: Float32Array): void {
|
|
205
|
+
super.set(vector, 0);
|
|
206
|
+
this._isVectorLoaded = true;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Get a JSON representation of the `CoVector`
|
|
211
|
+
* @category Content
|
|
212
|
+
*/
|
|
213
|
+
toJSON(): Array<number> {
|
|
214
|
+
return Array.from(this);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** @internal */
|
|
218
|
+
[inspect]() {
|
|
219
|
+
return this.toJSON();
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Load a `CoVector`
|
|
224
|
+
*
|
|
225
|
+
* @category Subscription & Loading
|
|
226
|
+
* @deprecated Use `co.vector(...).load` instead.
|
|
227
|
+
*/
|
|
228
|
+
static async load<C extends CoVector>(
|
|
229
|
+
this: CoValueClass<C>,
|
|
230
|
+
id: ID<C>,
|
|
231
|
+
options?: {
|
|
232
|
+
loadAs?: Account | AnonymousJazzAgent;
|
|
233
|
+
},
|
|
234
|
+
): Promise<C | null> {
|
|
235
|
+
const coVector = await loadCoValueWithoutMe(this, id, options);
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* We are only interested in the entire vector. Since most vectors are small (<15kB),
|
|
239
|
+
* we can wait for the stream to be complete before returning the vector
|
|
240
|
+
*/
|
|
241
|
+
if (!coVector?.$jazz.raw.isBinaryStreamEnded()) {
|
|
242
|
+
return new Promise<C | null>((resolve) => {
|
|
243
|
+
subscribeToCoValueWithoutMe(
|
|
244
|
+
this,
|
|
245
|
+
id,
|
|
246
|
+
options || {},
|
|
247
|
+
(value, unsubscribe) => {
|
|
248
|
+
if (value.$jazz.raw.isBinaryStreamEnded()) {
|
|
249
|
+
unsubscribe();
|
|
250
|
+
resolve(value);
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
);
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
coVector.loadVectorData();
|
|
258
|
+
return coVector;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Subscribe to a `CoVector`, when you have an ID but don't have a `CoVector` instance yet
|
|
263
|
+
* @category Subscription & Loading
|
|
264
|
+
* @deprecated Use `co.vector(...).subscribe` instead.
|
|
265
|
+
*/
|
|
266
|
+
static subscribe<V extends CoVector>(
|
|
267
|
+
this: CoValueClass<V>,
|
|
268
|
+
id: ID<V>,
|
|
269
|
+
listener: (value: Resolved<V, true>, unsubscribe: () => void) => void,
|
|
270
|
+
): () => void;
|
|
271
|
+
static subscribe<V extends CoVector>(
|
|
272
|
+
this: CoValueClass<V>,
|
|
273
|
+
id: ID<V>,
|
|
274
|
+
options: SubscribeListenerOptions<V, true>,
|
|
275
|
+
listener: (value: Resolved<V, true>, unsubscribe: () => void) => void,
|
|
276
|
+
): () => void;
|
|
277
|
+
static subscribe<V extends CoVector>(
|
|
278
|
+
this: CoValueClass<V>,
|
|
279
|
+
id: ID<V>,
|
|
280
|
+
...args: SubscribeRestArgs<V, true>
|
|
281
|
+
): () => void {
|
|
282
|
+
const { options, listener } = parseSubscribeRestArgs(args);
|
|
283
|
+
return subscribeToCoValueWithoutMe<V, true>(this, id, options, listener);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// CoVector mutation method overrides, as CoVectors aren't meant to be mutated
|
|
287
|
+
/**
|
|
288
|
+
* Calling `copyWithin` on a CoVector is forbidden. CoVectors are immutable.
|
|
289
|
+
* @deprecated If you want to change the vector, replace the former instance of CoVector with a new one.
|
|
290
|
+
*/
|
|
291
|
+
override copyWithin(target: number, start: number, end?: number): never {
|
|
292
|
+
throw new Error("Cannot mutate a CoVector using `copyWithin`");
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Calling `fill` on a CoVector is forbidden. CoVectors are immutable.
|
|
296
|
+
* @deprecated If you want to change the vector, replace the former instance of CoVector with a new one.
|
|
297
|
+
*/
|
|
298
|
+
override fill(value: number, start?: number, end?: number): never {
|
|
299
|
+
throw new Error("Cannot mutate a CoVector using `fill`");
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Calling `reverse` on a CoVector is forbidden. CoVectors are immutable.
|
|
303
|
+
* @deprecated If you want to change the vector, replace the former instance of CoVector with a new one.
|
|
304
|
+
*/
|
|
305
|
+
override reverse(): never {
|
|
306
|
+
throw new Error("Cannot mutate a CoVector using `reverse`");
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Calling `set` on a CoVector is forbidden. CoVectors are immutable.
|
|
310
|
+
* @deprecated If you want to change the vector, replace the former instance of CoVector with a new one.
|
|
311
|
+
// */
|
|
312
|
+
override set(array: ArrayLike<number>, offset?: number): never {
|
|
313
|
+
throw new Error("Cannot mutate a CoVector using `set`");
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Calling `sort` on a CoVector is forbidden. CoVectors are immutable.
|
|
317
|
+
* @deprecated If you want to change the vector, replace the former instance of CoVector with a new one.
|
|
318
|
+
*/
|
|
319
|
+
override sort(compareFn?: (a: number, b: number) => number): never {
|
|
320
|
+
throw new Error("Cannot mutate a CoVector using `sort`");
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
export class CoVectorJazzApi<V extends CoVector> extends CoValueJazzApi<V> {
|
|
325
|
+
constructor(
|
|
326
|
+
private coVector: V,
|
|
327
|
+
public raw: RawBinaryCoStream,
|
|
328
|
+
) {
|
|
329
|
+
super(coVector);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
get owner(): Group {
|
|
333
|
+
return getCoValueOwner(this.coVector);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* An instance method to subscribe to an existing `CoVector`
|
|
338
|
+
* @category Subscription & Loading
|
|
339
|
+
*/
|
|
340
|
+
subscribe<B extends CoVector>(
|
|
341
|
+
this: CoVectorJazzApi<B>,
|
|
342
|
+
listener: (value: Resolved<B, true>) => void,
|
|
343
|
+
): () => void {
|
|
344
|
+
return subscribeToExistingCoValue(this.coVector, {}, listener);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Wait for the `CoVector` to be uploaded to the other peers.
|
|
349
|
+
*
|
|
350
|
+
* @category Subscription & Loading
|
|
351
|
+
*/
|
|
352
|
+
waitForSync(options?: { timeout?: number }) {
|
|
353
|
+
return this.raw.core.waitForSync(options);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Vector operations
|
|
357
|
+
/**
|
|
358
|
+
* Calculate the magnitude of this vector.
|
|
359
|
+
*/
|
|
360
|
+
magnitude(): number {
|
|
361
|
+
return VectorCalculation.magnitude(this.coVector);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Normalize this vector.
|
|
366
|
+
* @returns A new instance of a normalized vector.
|
|
367
|
+
*/
|
|
368
|
+
normalize(): Float32Array {
|
|
369
|
+
return VectorCalculation.normalize(this.coVector);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Calculate the dot product of this vector and another vector.
|
|
374
|
+
*/
|
|
375
|
+
dotProduct(otherVector: CoVector | Float32Array | number[]): number {
|
|
376
|
+
return VectorCalculation.dotProduct(this.coVector, otherVector);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Calculate the cosine similarity between this vector and another vector.
|
|
381
|
+
*
|
|
382
|
+
* @returns A value between `-1` and `1`:
|
|
383
|
+
* - `1` means the vectors are identical
|
|
384
|
+
* - `0` means the vectors are orthogonal (i.e. no similarity)
|
|
385
|
+
* - `-1` means the vectors are opposite direction (perfectly dissimilar)
|
|
386
|
+
*/
|
|
387
|
+
cosineSimilarity(otherVector: CoVector | Float32Array | number[]): number {
|
|
388
|
+
return VectorCalculation.cosineSimilarity(this.coVector, otherVector);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const VectorCalculation = {
|
|
393
|
+
magnitude: (vector: Float32Array | number[]) => {
|
|
394
|
+
let sum = 0;
|
|
395
|
+
for (const v of vector) {
|
|
396
|
+
sum += v * v;
|
|
397
|
+
}
|
|
398
|
+
return Math.sqrt(sum);
|
|
399
|
+
},
|
|
400
|
+
normalize: (vector: Float32Array) => {
|
|
401
|
+
const mag = VectorCalculation.magnitude(vector);
|
|
402
|
+
|
|
403
|
+
if (mag === 0) {
|
|
404
|
+
return new Float32Array(vector.length).fill(0);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
return vector.map((v) => v / mag);
|
|
408
|
+
},
|
|
409
|
+
dotProduct: (vectorA: Float32Array, vectorB: Float32Array | number[]) => {
|
|
410
|
+
if (vectorA.length !== vectorB.length) {
|
|
411
|
+
throw new Error(
|
|
412
|
+
`Vector dimensions don't match: ${vectorA.length} vs ${vectorB.length}`,
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return vectorA.reduce((sum, a, i) => sum + a * vectorB[i]!, 0);
|
|
417
|
+
},
|
|
418
|
+
cosineSimilarity: (
|
|
419
|
+
vectorA: Float32Array,
|
|
420
|
+
vectorB: Float32Array | number[],
|
|
421
|
+
) => {
|
|
422
|
+
const magnitudeA = VectorCalculation.magnitude(vectorA);
|
|
423
|
+
const magnitudeB = VectorCalculation.magnitude(vectorB);
|
|
424
|
+
|
|
425
|
+
if (magnitudeA === 0 || magnitudeB === 0) {
|
|
426
|
+
return 0;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const dotProductAB = VectorCalculation.dotProduct(vectorA, vectorB);
|
|
430
|
+
return dotProductAB / (magnitudeA * magnitudeB);
|
|
431
|
+
},
|
|
432
|
+
};
|
|
@@ -172,13 +172,16 @@ async function serializeMessagePayload({
|
|
|
172
172
|
};
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
+
const coIdSchema = z.custom<`co_z${string}`>(isCoValueId);
|
|
176
|
+
const signatureSchema = z.custom<`signature_z${string}`>(
|
|
177
|
+
(value) => typeof value === "string" && value.startsWith("signature_z"),
|
|
178
|
+
);
|
|
179
|
+
|
|
175
180
|
const requestSchema = z.object({
|
|
176
181
|
contentPieces: z.array(z.json()),
|
|
177
|
-
id:
|
|
182
|
+
id: coIdSchema,
|
|
178
183
|
createdAt: z.number(),
|
|
179
|
-
authToken:
|
|
180
|
-
(value) => typeof value === "string" && value.startsWith("signature_z"),
|
|
181
|
-
),
|
|
184
|
+
authToken: signatureSchema,
|
|
182
185
|
signerID: z.custom<`signer_z${string}`>(
|
|
183
186
|
(value) => typeof value === "string" && value.startsWith("signer_z"),
|
|
184
187
|
),
|
|
@@ -631,3 +634,184 @@ async function loadWorkerAccountOrGroup(id: string, loadAs: Account) {
|
|
|
631
634
|
loadAs,
|
|
632
635
|
});
|
|
633
636
|
}
|
|
637
|
+
|
|
638
|
+
function defaultGetToken(request: Request) {
|
|
639
|
+
const headerValue = request.headers.get("Authorization");
|
|
640
|
+
if (headerValue?.startsWith("Jazz ")) {
|
|
641
|
+
return headerValue.replace("Jazz ", "");
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
if (headerValue) {
|
|
645
|
+
console.warn(
|
|
646
|
+
"An Authorization header was found, but it did not start with 'Jazz '. If this is intentional, you can specify the location of the token using the `getToken` option.",
|
|
647
|
+
);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
return undefined;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* Authenticates a Request by verifying a signed authentication token.
|
|
655
|
+
*
|
|
656
|
+
* - If a token is not provided, the returned account is `undefined` and no error is returned.
|
|
657
|
+
* - If a valid token is provided, the signer account is returned.
|
|
658
|
+
* - If an invalid token is provided, an error is returned detailing the validation error, and the returned account is `undefined`.
|
|
659
|
+
*
|
|
660
|
+
* @see {@link generateAuthToken} for generating a token.
|
|
661
|
+
*
|
|
662
|
+
* Note: This function does not perform any authorization checks, it only verifies if - **when provided** - a token is valid. It is up to the caller to perform any additional authorization checks, if needed.
|
|
663
|
+
*
|
|
664
|
+
* @param request - The request to authenticate.
|
|
665
|
+
* @param options - The options for the authentication.
|
|
666
|
+
* @param options.expiration - The expiration time of the token in milliseconds, defaults to 1 minute.
|
|
667
|
+
* @param options.loadAs - The account to load the token from, defaults to the current active account.
|
|
668
|
+
* @param options.getToken - If specified, this function will be used to get the token from the request. By default the token is expected to be in the `Authorization` header in the form of `Jazz <token>`.
|
|
669
|
+
* @returns The account if it is valid, otherwise an error.
|
|
670
|
+
*
|
|
671
|
+
* @example
|
|
672
|
+
* ```ts
|
|
673
|
+
* const { account, error } = await authenticateRequest(request);
|
|
674
|
+
* if (error) {
|
|
675
|
+
* return new Response(JSON.stringify(error), { status: 401 });
|
|
676
|
+
* }
|
|
677
|
+
* ```
|
|
678
|
+
*/
|
|
679
|
+
export async function authenticateRequest(
|
|
680
|
+
request: Request,
|
|
681
|
+
options?: {
|
|
682
|
+
expiration?: number;
|
|
683
|
+
loadAs?: Account;
|
|
684
|
+
getToken?: (request: Request) => string | undefined | null;
|
|
685
|
+
},
|
|
686
|
+
): Promise<
|
|
687
|
+
| {
|
|
688
|
+
account?: Account;
|
|
689
|
+
error?: never;
|
|
690
|
+
}
|
|
691
|
+
| {
|
|
692
|
+
account?: never;
|
|
693
|
+
error: { message: string; details?: unknown };
|
|
694
|
+
}
|
|
695
|
+
> {
|
|
696
|
+
const token = options?.getToken?.(request) ?? defaultGetToken(request);
|
|
697
|
+
|
|
698
|
+
if (!token) {
|
|
699
|
+
return {};
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
const { account, error } = await parseAuthToken(token, {
|
|
703
|
+
loadAs: options?.loadAs,
|
|
704
|
+
expiration: options?.expiration ?? 1000 * 60,
|
|
705
|
+
});
|
|
706
|
+
|
|
707
|
+
if (error) {
|
|
708
|
+
return { error };
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
return { account, error };
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
/**
|
|
715
|
+
* Generates an authentication token for a given account. This token can be used to authenticate a request. See {@link authenticateRequest} for more details.
|
|
716
|
+
*
|
|
717
|
+
* @param as - The account to generate the token for, defaults to the current active account.
|
|
718
|
+
* @returns The authentication token.
|
|
719
|
+
*
|
|
720
|
+
* @example Make a fetch request with the token
|
|
721
|
+
* ```ts
|
|
722
|
+
* const token = generateAuthToken();
|
|
723
|
+
* const response = await fetch(url, {
|
|
724
|
+
* headers: {
|
|
725
|
+
* Authorization: `Jazz ${token}`,
|
|
726
|
+
* },
|
|
727
|
+
* });
|
|
728
|
+
* ```
|
|
729
|
+
*/
|
|
730
|
+
|
|
731
|
+
export function generateAuthToken(as?: Account) {
|
|
732
|
+
const account = as ?? Account.getMe();
|
|
733
|
+
const node = account.$jazz.localNode;
|
|
734
|
+
const crypto = node.crypto;
|
|
735
|
+
|
|
736
|
+
const agent = node.getCurrentAgent();
|
|
737
|
+
const signerSecret = agent.currentSignerSecret();
|
|
738
|
+
|
|
739
|
+
const createdAt = Date.now();
|
|
740
|
+
|
|
741
|
+
const signPayload = crypto.secureHash({
|
|
742
|
+
id: account.$jazz.id,
|
|
743
|
+
createdAt,
|
|
744
|
+
});
|
|
745
|
+
|
|
746
|
+
const authToken = crypto.sign(signerSecret, signPayload);
|
|
747
|
+
|
|
748
|
+
return `${authToken}~${account.$jazz.id}~${createdAt}`;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
export async function parseAuthToken(
|
|
752
|
+
authToken: string,
|
|
753
|
+
options?: { loadAs?: Account; expiration?: number },
|
|
754
|
+
): Promise<
|
|
755
|
+
| { account: Account; error?: never }
|
|
756
|
+
| { account?: never; error: { message: string; details?: unknown } }
|
|
757
|
+
> {
|
|
758
|
+
const expiration = options?.expiration ?? 1_000 * 60; // 1 minute
|
|
759
|
+
|
|
760
|
+
const parsed = z
|
|
761
|
+
.tuple([signatureSchema, coIdSchema, z.string().transform(Number)])
|
|
762
|
+
.safeParse(authToken.split("~"));
|
|
763
|
+
|
|
764
|
+
if (!parsed.success) {
|
|
765
|
+
return {
|
|
766
|
+
error: {
|
|
767
|
+
message: "Invalid token",
|
|
768
|
+
details: parsed.error,
|
|
769
|
+
},
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
const [signature, id, createdAt] = parsed.data;
|
|
774
|
+
|
|
775
|
+
if (createdAt + expiration < Date.now()) {
|
|
776
|
+
return {
|
|
777
|
+
error: {
|
|
778
|
+
message: "Token expired",
|
|
779
|
+
},
|
|
780
|
+
};
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
const account = await Account.load(id, { loadAs: options?.loadAs });
|
|
784
|
+
|
|
785
|
+
if (!account) {
|
|
786
|
+
return {
|
|
787
|
+
error: {
|
|
788
|
+
message: "Failed to load account",
|
|
789
|
+
details: { id },
|
|
790
|
+
},
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
const node = account.$jazz.localNode;
|
|
795
|
+
const crypto = node.crypto;
|
|
796
|
+
|
|
797
|
+
// Verify the signature of the message to prevent tampering
|
|
798
|
+
const signPayload = crypto.secureHash({
|
|
799
|
+
id: account.$jazz.id,
|
|
800
|
+
createdAt: Number(createdAt),
|
|
801
|
+
});
|
|
802
|
+
|
|
803
|
+
const agentID = account.$jazz.raw.currentAgentID();
|
|
804
|
+
const signerID = crypto.getAgentSignerID(agentID);
|
|
805
|
+
|
|
806
|
+
if (!crypto.verify(signature, signPayload, signerID)) {
|
|
807
|
+
return {
|
|
808
|
+
error: {
|
|
809
|
+
message: "Invalid signature",
|
|
810
|
+
},
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
return {
|
|
815
|
+
account,
|
|
816
|
+
};
|
|
817
|
+
}
|
package/src/tools/exports.ts
CHANGED
|
@@ -7,6 +7,7 @@ export { CoFeedSchema as Feed } from "./schemaTypes/CoFeedSchema.js";
|
|
|
7
7
|
export { PlainTextSchema as PlainText } from "./schemaTypes/PlainTextSchema.js";
|
|
8
8
|
export { RichTextSchema as RichText } from "./schemaTypes/RichTextSchema.js";
|
|
9
9
|
export { FileStreamSchema as FileStream } from "./schemaTypes/FileStreamSchema.js";
|
|
10
|
+
export { CoVectorSchema as Vector } from "./schemaTypes/CoVectorSchema.js";
|
|
10
11
|
export { CoInput as input } from "./typeConverters/CoFieldSchemaInit.js";
|
|
11
12
|
export {
|
|
12
13
|
AccountSchema as Account,
|
|
@@ -23,6 +24,7 @@ export {
|
|
|
23
24
|
coPlainTextDefiner as plainText,
|
|
24
25
|
coRichTextDefiner as richText,
|
|
25
26
|
coFileStreamDefiner as fileStream,
|
|
27
|
+
coVectorDefiner as vector,
|
|
26
28
|
coImageDefiner as image,
|
|
27
29
|
coAccountDefiner as account,
|
|
28
30
|
coGroupDefiner as group,
|