mcard-js 2.1.48 → 2.1.49
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/dist/AbstractSqlEngine-DKka6XjT.d.cts +451 -0
- package/dist/AbstractSqlEngine-DKka6XjT.d.ts +451 -0
- package/dist/CardCollection-ZQ3G3Q3A.js +10 -0
- package/dist/IndexedDBEngine-BWXAB46W.js +12 -0
- package/dist/LLMRuntime-PH3MOQ2Y.js +17 -0
- package/dist/LambdaRuntime-YH74FHIW.js +19 -0
- package/dist/Loader-WZXYG4GE.js +12 -0
- package/dist/NetworkRuntime-S4DZCGVN.js +1598 -0
- package/dist/OllamaProvider-SPGO5Z5E.js +9 -0
- package/dist/chunk-3FFEA2XK.js +149 -0
- package/dist/chunk-7AXRV7NS.js +112 -0
- package/dist/chunk-HIVVDGE5.js +497 -0
- package/dist/chunk-KVZYFZJ5.js +427 -0
- package/dist/chunk-NGTY4P6A.js +275 -0
- package/dist/chunk-OUW2SUGM.js +368 -0
- package/dist/chunk-QKH3N62B.js +2360 -0
- package/dist/chunk-QPVEUPMU.js +299 -0
- package/dist/chunk-VYDZR4ZD.js +364 -0
- package/dist/chunk-XJZOEM5F.js +903 -0
- package/dist/chunk-Z7EFXSTO.js +217 -0
- package/dist/index.browser.cjs +37 -1
- package/dist/index.browser.d.cts +20 -2
- package/dist/index.browser.d.ts +20 -2
- package/dist/index.browser.js +10 -6
- package/dist/index.cjs +618 -146
- package/dist/index.d.cts +723 -4
- package/dist/index.d.ts +723 -4
- package/dist/index.js +527 -89
- package/dist/storage/SqliteNodeEngine.cjs +7 -1
- package/dist/storage/SqliteNodeEngine.d.cts +1 -1
- package/dist/storage/SqliteNodeEngine.d.ts +1 -1
- package/dist/storage/SqliteNodeEngine.js +3 -3
- package/dist/storage/SqliteWasmEngine.cjs +7 -1
- package/dist/storage/SqliteWasmEngine.d.cts +1 -1
- package/dist/storage/SqliteWasmEngine.d.ts +1 -1
- package/dist/storage/SqliteWasmEngine.js +3 -3
- package/package.json +1 -1
|
@@ -0,0 +1,903 @@
|
|
|
1
|
+
// src/model/hash/algorithms/LocalSHA256.ts
|
|
2
|
+
async function computeHash(content) {
|
|
3
|
+
let contentStr;
|
|
4
|
+
if (typeof content === "string") {
|
|
5
|
+
contentStr = content;
|
|
6
|
+
} else {
|
|
7
|
+
contentStr = JSON.stringify(content);
|
|
8
|
+
}
|
|
9
|
+
const encoder = new TextEncoder();
|
|
10
|
+
const data = encoder.encode(contentStr);
|
|
11
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
12
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
13
|
+
const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
14
|
+
return hashHex;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// src/monads/Reader.ts
|
|
18
|
+
var Reader = class _Reader {
|
|
19
|
+
constructor(run) {
|
|
20
|
+
this.run = run;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Lift a pure value into Reader
|
|
24
|
+
*/
|
|
25
|
+
static pure(value) {
|
|
26
|
+
return new _Reader((_) => value);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Get the environment
|
|
30
|
+
*/
|
|
31
|
+
static ask() {
|
|
32
|
+
return new _Reader((env) => env);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Monadic bind (flatMap)
|
|
36
|
+
*/
|
|
37
|
+
bind(fn) {
|
|
38
|
+
return new _Reader((env) => {
|
|
39
|
+
const value = this.run(env);
|
|
40
|
+
return fn(value).run(env);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Map over the result
|
|
45
|
+
*/
|
|
46
|
+
map(fn) {
|
|
47
|
+
return this.bind((value) => _Reader.pure(fn(value)));
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Execute the Reader logic with an environment
|
|
51
|
+
*/
|
|
52
|
+
evaluate(env) {
|
|
53
|
+
return this.run(env);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// src/monads/Writer.ts
|
|
58
|
+
var Writer = class _Writer {
|
|
59
|
+
constructor(run) {
|
|
60
|
+
this.run = run;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Lift a pure value into Writer (empty log)
|
|
64
|
+
*/
|
|
65
|
+
static pure(value) {
|
|
66
|
+
return new _Writer(() => [value, []]);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Write to log
|
|
70
|
+
*/
|
|
71
|
+
static tell(log) {
|
|
72
|
+
return new _Writer(() => [void 0, log]);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Monadic bind
|
|
76
|
+
*/
|
|
77
|
+
bind(fn) {
|
|
78
|
+
return new _Writer(() => {
|
|
79
|
+
const [val1, log1] = this.run();
|
|
80
|
+
const writer2 = fn(val1);
|
|
81
|
+
const [val2, log2] = writer2.evaluate();
|
|
82
|
+
return [val2, [...log1, ...log2]];
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Map
|
|
87
|
+
*/
|
|
88
|
+
map(fn) {
|
|
89
|
+
return this.bind((val) => _Writer.pure(fn(val)));
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Run the writer
|
|
93
|
+
*/
|
|
94
|
+
evaluate() {
|
|
95
|
+
return this.run();
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// src/monads/State.ts
|
|
100
|
+
var State = class _State {
|
|
101
|
+
constructor(run) {
|
|
102
|
+
this.run = run;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Lift pure value
|
|
106
|
+
*/
|
|
107
|
+
static pure(value) {
|
|
108
|
+
return new _State((state) => [value, state]);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Get current state
|
|
112
|
+
*/
|
|
113
|
+
static get() {
|
|
114
|
+
return new _State((state) => [state, state]);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Set new state
|
|
118
|
+
*/
|
|
119
|
+
static put(newState) {
|
|
120
|
+
return new _State((_) => [void 0, newState]);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Monadic bind
|
|
124
|
+
*/
|
|
125
|
+
bind(fn) {
|
|
126
|
+
return new _State((state) => {
|
|
127
|
+
const [val, newState] = this.run(state);
|
|
128
|
+
return fn(val).run(newState);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Map
|
|
133
|
+
*/
|
|
134
|
+
map(fn) {
|
|
135
|
+
return this.bind((val) => _State.pure(fn(val)));
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Execute state transition
|
|
139
|
+
*/
|
|
140
|
+
evaluate(initialState) {
|
|
141
|
+
return this.run(initialState);
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
// src/ptr/LensProtocol.ts
|
|
146
|
+
var LensProtocol = class {
|
|
147
|
+
static idCounter = 0;
|
|
148
|
+
/**
|
|
149
|
+
* Create an execute request
|
|
150
|
+
*/
|
|
151
|
+
static createExecuteRequest(pcard_hash, target_hash, context) {
|
|
152
|
+
return {
|
|
153
|
+
jsonrpc: "2.0",
|
|
154
|
+
id: ++this.idCounter,
|
|
155
|
+
method: "pcard.execute",
|
|
156
|
+
params: { pcard_hash, target_hash, context }
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Create a verify request
|
|
161
|
+
*/
|
|
162
|
+
static createVerifyRequest(pcard_hash, target_hash, context) {
|
|
163
|
+
return {
|
|
164
|
+
jsonrpc: "2.0",
|
|
165
|
+
id: ++this.idCounter,
|
|
166
|
+
method: "pcard.verify",
|
|
167
|
+
params: { pcard_hash, target_hash, context }
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Create a reveal request
|
|
172
|
+
*/
|
|
173
|
+
static createRevealRequest(card_hash, aspect) {
|
|
174
|
+
return {
|
|
175
|
+
jsonrpc: "2.0",
|
|
176
|
+
id: ++this.idCounter,
|
|
177
|
+
method: "lens.reveal",
|
|
178
|
+
params: { card_hash, aspect }
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Create a system status request
|
|
183
|
+
*/
|
|
184
|
+
static createStatusRequest() {
|
|
185
|
+
return {
|
|
186
|
+
jsonrpc: "2.0",
|
|
187
|
+
id: ++this.idCounter,
|
|
188
|
+
method: "system.status"
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Create a system health request
|
|
193
|
+
*/
|
|
194
|
+
static createHealthRequest() {
|
|
195
|
+
return {
|
|
196
|
+
jsonrpc: "2.0",
|
|
197
|
+
id: ++this.idCounter,
|
|
198
|
+
method: "system.health"
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Create a success response
|
|
203
|
+
*/
|
|
204
|
+
static createSuccessResponse(id, result) {
|
|
205
|
+
return { jsonrpc: "2.0", id, result };
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Create an error response
|
|
209
|
+
*/
|
|
210
|
+
static createErrorResponse(id, code, message, data) {
|
|
211
|
+
return { jsonrpc: "2.0", id, error: { code, message, data } };
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Parse a JSON-RPC response
|
|
215
|
+
*/
|
|
216
|
+
static parseResponse(response) {
|
|
217
|
+
if (response.error) {
|
|
218
|
+
throw new Error(`JSON-RPC Error ${response.error.code}: ${response.error.message}`);
|
|
219
|
+
}
|
|
220
|
+
return response.result;
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
var ErrorCodes = {
|
|
224
|
+
PARSE_ERROR: -32700,
|
|
225
|
+
INVALID_REQUEST: -32600,
|
|
226
|
+
METHOD_NOT_FOUND: -32601,
|
|
227
|
+
INVALID_PARAMS: -32602,
|
|
228
|
+
INTERNAL_ERROR: -32603,
|
|
229
|
+
// Custom PTR errors
|
|
230
|
+
EXECUTION_ERROR: -32e3,
|
|
231
|
+
VERIFICATION_FAILED: -32001,
|
|
232
|
+
TIMEOUT: -32002
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
// src/ptr/common_types.ts
|
|
236
|
+
var VerificationStatus = /* @__PURE__ */ ((VerificationStatus2) => {
|
|
237
|
+
VerificationStatus2["PENDING"] = "pending";
|
|
238
|
+
VerificationStatus2["VERIFIED"] = "verified";
|
|
239
|
+
VerificationStatus2["FAILED"] = "failed";
|
|
240
|
+
VerificationStatus2["SKIPPED"] = "skipped";
|
|
241
|
+
return VerificationStatus2;
|
|
242
|
+
})(VerificationStatus || {});
|
|
243
|
+
var PreconditionViolation = class extends Error {
|
|
244
|
+
pcardHash;
|
|
245
|
+
inputHash;
|
|
246
|
+
detail;
|
|
247
|
+
constructor(pcardHash, inputHash, detail = "") {
|
|
248
|
+
const msg = `PreconditionViolation: PCard ${pcardHash.substring(0, 16)} rejected input ${inputHash.substring(0, 16)}${detail ? ` \u2014 ${detail}` : ""}`;
|
|
249
|
+
super(msg);
|
|
250
|
+
this.name = "PreconditionViolation";
|
|
251
|
+
this.pcardHash = pcardHash;
|
|
252
|
+
this.inputHash = inputHash;
|
|
253
|
+
this.detail = detail;
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
var PostconditionViolation = class extends Error {
|
|
257
|
+
pcardHash;
|
|
258
|
+
outputRepr;
|
|
259
|
+
detail;
|
|
260
|
+
constructor(pcardHash, outputRepr, detail = "") {
|
|
261
|
+
const msg = `PostconditionViolation: PCard ${pcardHash.substring(0, 16)} postcondition failed for output ${outputRepr.substring(0, 64)}${detail ? ` \u2014 ${detail}` : ""}`;
|
|
262
|
+
super(msg);
|
|
263
|
+
this.name = "PostconditionViolation";
|
|
264
|
+
this.pcardHash = pcardHash;
|
|
265
|
+
this.outputRepr = outputRepr;
|
|
266
|
+
this.detail = detail;
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
// src/ptr/browser/storage/MCardStore.ts
|
|
271
|
+
import { openDB } from "idb";
|
|
272
|
+
|
|
273
|
+
// src/ptr/browser/constants.ts
|
|
274
|
+
var MCARD_DB_NAME = "mcard-store";
|
|
275
|
+
var MCARD_DB_VERSION = 8;
|
|
276
|
+
|
|
277
|
+
// src/ptr/browser/storage/MCardStore.ts
|
|
278
|
+
var MCardStore = class {
|
|
279
|
+
dbPromise;
|
|
280
|
+
constructor(dbName = MCARD_DB_NAME) {
|
|
281
|
+
this.dbPromise = openDB(dbName, MCARD_DB_VERSION, {
|
|
282
|
+
upgrade(db, oldVersion, newVersion, transaction) {
|
|
283
|
+
const existingStoreNames = Array.from(db.objectStoreNames);
|
|
284
|
+
const rawDb = db;
|
|
285
|
+
if (existingStoreNames.includes("mcards")) rawDb.deleteObjectStore("mcards");
|
|
286
|
+
if (existingStoreNames.includes("handles")) rawDb.deleteObjectStore("handles");
|
|
287
|
+
let cardStore;
|
|
288
|
+
if (!db.objectStoreNames.contains("card")) {
|
|
289
|
+
cardStore = db.createObjectStore("card", { keyPath: "hash" });
|
|
290
|
+
} else {
|
|
291
|
+
cardStore = transaction.objectStore("card");
|
|
292
|
+
}
|
|
293
|
+
if (!cardStore.indexNames.contains("by_g_time")) {
|
|
294
|
+
cardStore.createIndex("by_g_time", "g_time");
|
|
295
|
+
}
|
|
296
|
+
let registryStore;
|
|
297
|
+
if (!db.objectStoreNames.contains("handle_registry")) {
|
|
298
|
+
registryStore = db.createObjectStore("handle_registry", { keyPath: "handle" });
|
|
299
|
+
} else {
|
|
300
|
+
registryStore = transaction.objectStore("handle_registry");
|
|
301
|
+
}
|
|
302
|
+
if (!registryStore.indexNames.contains("by_current_hash")) {
|
|
303
|
+
registryStore.createIndex("by_current_hash", "current_hash");
|
|
304
|
+
}
|
|
305
|
+
if (!registryStore.indexNames.contains("by_updated_at")) {
|
|
306
|
+
registryStore.createIndex("by_updated_at", "updated_at");
|
|
307
|
+
}
|
|
308
|
+
let historyStore;
|
|
309
|
+
if (!db.objectStoreNames.contains("handle_history")) {
|
|
310
|
+
historyStore = db.createObjectStore("handle_history", { keyPath: "id", autoIncrement: true });
|
|
311
|
+
} else {
|
|
312
|
+
historyStore = transaction.objectStore("handle_history");
|
|
313
|
+
}
|
|
314
|
+
if (!historyStore.indexNames.contains("by_handle")) {
|
|
315
|
+
historyStore.createIndex("by_handle", "handle");
|
|
316
|
+
}
|
|
317
|
+
if (!historyStore.indexNames.contains("by_previous_hash")) {
|
|
318
|
+
historyStore.createIndex("by_previous_hash", "previous_hash");
|
|
319
|
+
}
|
|
320
|
+
if (!historyStore.indexNames.contains("by_changed_at")) {
|
|
321
|
+
historyStore.createIndex("by_changed_at", "changed_at");
|
|
322
|
+
}
|
|
323
|
+
if (!db.objectStoreNames.contains("schema_version")) {
|
|
324
|
+
const versionStore = db.createObjectStore("schema_version", { keyPath: "version" });
|
|
325
|
+
versionStore.add({
|
|
326
|
+
version: "3.0.2",
|
|
327
|
+
applied_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
328
|
+
description: "Monadic Core Schema (Strict Compliance)"
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
// ═══════════════════════════════════════════════════════════════
|
|
335
|
+
// CARD OPERATIONS
|
|
336
|
+
// ═══════════════════════════════════════════════════════════════
|
|
337
|
+
async putCard(hash, content) {
|
|
338
|
+
const db = await this.dbPromise;
|
|
339
|
+
const g_time = (/* @__PURE__ */ new Date()).toISOString();
|
|
340
|
+
await db.put("card", {
|
|
341
|
+
hash,
|
|
342
|
+
content,
|
|
343
|
+
g_time
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
async getCard(hash) {
|
|
347
|
+
const db = await this.dbPromise;
|
|
348
|
+
return db.get("card", hash);
|
|
349
|
+
}
|
|
350
|
+
// ═══════════════════════════════════════════════════════════════
|
|
351
|
+
// HANDLE OPERATIONS (Transaction-safe)
|
|
352
|
+
// ═══════════════════════════════════════════════════════════════
|
|
353
|
+
async setHandle(handle, newHash) {
|
|
354
|
+
const db = await this.dbPromise;
|
|
355
|
+
const tx = db.transaction(["handle_registry", "handle_history"], "readwrite");
|
|
356
|
+
const registry = tx.objectStore("handle_registry");
|
|
357
|
+
const history = tx.objectStore("handle_history");
|
|
358
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
359
|
+
const existing = await registry.get(handle);
|
|
360
|
+
if (existing) {
|
|
361
|
+
await history.add({
|
|
362
|
+
handle,
|
|
363
|
+
previous_hash: existing.current_hash,
|
|
364
|
+
changed_at: now
|
|
365
|
+
});
|
|
366
|
+
await registry.put({
|
|
367
|
+
handle,
|
|
368
|
+
current_hash: newHash,
|
|
369
|
+
created_at: existing.created_at,
|
|
370
|
+
updated_at: now
|
|
371
|
+
});
|
|
372
|
+
} else {
|
|
373
|
+
await registry.put({
|
|
374
|
+
handle,
|
|
375
|
+
current_hash: newHash,
|
|
376
|
+
created_at: now,
|
|
377
|
+
updated_at: now
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
await tx.done;
|
|
381
|
+
}
|
|
382
|
+
async resolveHandle(handle) {
|
|
383
|
+
const db = await this.dbPromise;
|
|
384
|
+
const tx = db.transaction(["handle_registry", "card"], "readonly");
|
|
385
|
+
const registry = tx.objectStore("handle_registry");
|
|
386
|
+
const entry = await registry.get(handle);
|
|
387
|
+
if (!entry) return void 0;
|
|
388
|
+
const cardStore = tx.objectStore("card");
|
|
389
|
+
return cardStore.get(entry.current_hash);
|
|
390
|
+
}
|
|
391
|
+
// ═══════════════════════════════════════════════════════════════
|
|
392
|
+
// QUERY HELPERS (using Indices)
|
|
393
|
+
// ═══════════════════════════════════════════════════════════════
|
|
394
|
+
async getHandleHistory(handle) {
|
|
395
|
+
const db = await this.dbPromise;
|
|
396
|
+
return db.getAllFromIndex("handle_history", "by_handle", handle);
|
|
397
|
+
}
|
|
398
|
+
async getHandlesByHash(hash) {
|
|
399
|
+
const db = await this.dbPromise;
|
|
400
|
+
return db.getAllFromIndex("handle_registry", "by_current_hash", hash);
|
|
401
|
+
}
|
|
402
|
+
// ═══════════════════════════════════════════════════════════════
|
|
403
|
+
// UI HELPERS (For Browser Demo - keeping these for compatibility but implementing via new methods where possible)
|
|
404
|
+
// ═══════════════════════════════════════════════════════════════
|
|
405
|
+
async getAllCards() {
|
|
406
|
+
const db = await this.dbPromise;
|
|
407
|
+
return db.getAll("card");
|
|
408
|
+
}
|
|
409
|
+
async getAllHandles() {
|
|
410
|
+
const db = await this.dbPromise;
|
|
411
|
+
return db.getAll("handle_registry");
|
|
412
|
+
}
|
|
413
|
+
async getAllHistory() {
|
|
414
|
+
const db = await this.dbPromise;
|
|
415
|
+
return db.getAll("handle_history");
|
|
416
|
+
}
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
// src/ptr/browser/network/WebSocketClient.ts
|
|
420
|
+
var WebSocketClient = class {
|
|
421
|
+
ws = null;
|
|
422
|
+
url;
|
|
423
|
+
messageHandler = null;
|
|
424
|
+
reconnectInterval = 3e3;
|
|
425
|
+
constructor(url) {
|
|
426
|
+
this.url = url;
|
|
427
|
+
}
|
|
428
|
+
connect() {
|
|
429
|
+
console.log(`[BrowserPTR] Connecting to ${this.url}`);
|
|
430
|
+
this.ws = new WebSocket(this.url);
|
|
431
|
+
this.ws.onopen = () => {
|
|
432
|
+
console.log("[BrowserPTR] WebSocket connected");
|
|
433
|
+
};
|
|
434
|
+
this.ws.onmessage = (event) => {
|
|
435
|
+
try {
|
|
436
|
+
const data = JSON.parse(event.data);
|
|
437
|
+
if (this.messageHandler) {
|
|
438
|
+
this.messageHandler(data);
|
|
439
|
+
}
|
|
440
|
+
} catch (e) {
|
|
441
|
+
console.error("[BrowserPTR] Failed to parse message:", e);
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
this.ws.onclose = () => {
|
|
445
|
+
console.log("[BrowserPTR] WebSocket closed. Reconnecting...");
|
|
446
|
+
this.ws = null;
|
|
447
|
+
setTimeout(() => this.connect(), this.reconnectInterval);
|
|
448
|
+
};
|
|
449
|
+
this.ws.onerror = (error) => {
|
|
450
|
+
console.error("[BrowserPTR] WebSocket error:", error);
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
setMessageHandler(handler) {
|
|
454
|
+
this.messageHandler = handler;
|
|
455
|
+
}
|
|
456
|
+
send(data) {
|
|
457
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
458
|
+
this.ws.send(JSON.stringify(data));
|
|
459
|
+
} else {
|
|
460
|
+
console.warn("[BrowserPTR] WebSocket not ready. Message dropped:", data);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
// src/ptr/browser/worker/ServiceWorkerPTR.ts
|
|
466
|
+
var ServiceWorkerPTR = class {
|
|
467
|
+
store;
|
|
468
|
+
ws;
|
|
469
|
+
constructor(serverUrl, wasmUrl, storeName = MCARD_DB_NAME) {
|
|
470
|
+
this.store = new MCardStore(storeName);
|
|
471
|
+
this.ws = new WebSocketClient(serverUrl);
|
|
472
|
+
this.ws.setMessageHandler(this.onMeshMessage.bind(this));
|
|
473
|
+
}
|
|
474
|
+
start() {
|
|
475
|
+
this.ws.connect();
|
|
476
|
+
self.addEventListener("message", (event) => {
|
|
477
|
+
const typedEvent = event;
|
|
478
|
+
if (typedEvent.data) {
|
|
479
|
+
if (typedEvent.data.type === "clm_execute") {
|
|
480
|
+
this.handleLocalRequest(typedEvent);
|
|
481
|
+
} else if (typedEvent.data.type === "EXECUTE_CLM") {
|
|
482
|
+
this.handleDirectExecution(typedEvent);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
});
|
|
486
|
+
console.log("[ServiceWorkerPTR] Started");
|
|
487
|
+
}
|
|
488
|
+
getStore() {
|
|
489
|
+
return this.store;
|
|
490
|
+
}
|
|
491
|
+
async onMeshMessage(msg) {
|
|
492
|
+
console.log("[ServiceWorkerPTR] Received from mesh:", msg);
|
|
493
|
+
if (!msg || typeof msg !== "object" || Array.isArray(msg)) {
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
const meshMessage = msg;
|
|
497
|
+
if (meshMessage.type === "clm_execute") {
|
|
498
|
+
await this.executeCLM(meshMessage.clm_hash || "", meshMessage.input_hash || "", meshMessage.request_id || "");
|
|
499
|
+
} else if (meshMessage.type === "clm_result") {
|
|
500
|
+
const clients = await self.clients.matchAll();
|
|
501
|
+
for (const client of clients) {
|
|
502
|
+
client.postMessage(meshMessage);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
async handleLocalRequest(event) {
|
|
507
|
+
const { clm_hash, input_hash, request_id } = event.data;
|
|
508
|
+
console.log("[ServiceWorkerPTR] Local request:", request_id);
|
|
509
|
+
this.ws.send({
|
|
510
|
+
type: "clm_execute",
|
|
511
|
+
clm_hash,
|
|
512
|
+
input_hash,
|
|
513
|
+
request_id,
|
|
514
|
+
origin: "browser"
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
async handleDirectExecution(event) {
|
|
518
|
+
const { code } = event.data;
|
|
519
|
+
const port = event.ports[0];
|
|
520
|
+
console.log("[ServiceWorkerPTR] Direct Execution Request");
|
|
521
|
+
try {
|
|
522
|
+
const input = { content: { count: 0 } };
|
|
523
|
+
const result = await this.executeJavaScript(code || "", input, {});
|
|
524
|
+
if (port) {
|
|
525
|
+
port.postMessage({
|
|
526
|
+
result,
|
|
527
|
+
logs: ["[ServiceWorkerPTR] Executed successfully (Client Mode)"]
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
} catch (e) {
|
|
531
|
+
const error = e;
|
|
532
|
+
console.error("[ServiceWorkerPTR] Execution error:", e);
|
|
533
|
+
if (port) {
|
|
534
|
+
port.postMessage({
|
|
535
|
+
logs: [`[ServiceWorkerPTR] Error: ${error.message || String(e)}`]
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
async executeCLM(clmHash, inputHash, requestId) {
|
|
541
|
+
console.log(`[ServiceWorkerPTR] Executing CLM ${clmHash} with input ${inputHash}`);
|
|
542
|
+
const input = await this.store.getCard(inputHash);
|
|
543
|
+
try {
|
|
544
|
+
const code = clmHash.includes("return") ? clmHash : "return input.content.count + 1;";
|
|
545
|
+
const result = await this.executeJavaScript(code, input || { content: { count: 0 } }, {});
|
|
546
|
+
const resultHash = `sha256:result_${requestId}`;
|
|
547
|
+
await this.store.putCard(resultHash, this.toStoredContent(result));
|
|
548
|
+
this.ws.send({
|
|
549
|
+
type: "clm_result",
|
|
550
|
+
request_id: requestId,
|
|
551
|
+
output_hash: resultHash,
|
|
552
|
+
success: true
|
|
553
|
+
});
|
|
554
|
+
} catch (e) {
|
|
555
|
+
const error = e;
|
|
556
|
+
console.error("[ServiceWorkerPTR] Execution error:", e);
|
|
557
|
+
this.ws.send({
|
|
558
|
+
type: "clm_result",
|
|
559
|
+
request_id: requestId,
|
|
560
|
+
output_hash: "",
|
|
561
|
+
success: false,
|
|
562
|
+
error: error.message || String(e)
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
// --- Execution Logic Ported from SandboxWorker ---
|
|
567
|
+
async executeJavaScript(code, input, context) {
|
|
568
|
+
try {
|
|
569
|
+
const fn = new Function("input", "context", code);
|
|
570
|
+
return fn(input, context || {});
|
|
571
|
+
} catch (e) {
|
|
572
|
+
throw e;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
// Pyodide support placeholder
|
|
576
|
+
// Implementing full Pyodide loading requires handling caching and CDN usage
|
|
577
|
+
// similar to SandboxWorker.ts but adapted for ServiceWorker environment.
|
|
578
|
+
async executePython(code, input, context) {
|
|
579
|
+
throw new Error("Python execution not fully implemented yet in ServiceWorkerPTR");
|
|
580
|
+
}
|
|
581
|
+
toStoredContent(value) {
|
|
582
|
+
if (typeof value === "string" || value instanceof Uint8Array) {
|
|
583
|
+
return value;
|
|
584
|
+
}
|
|
585
|
+
if (value && typeof value === "object") {
|
|
586
|
+
return value;
|
|
587
|
+
}
|
|
588
|
+
return { value };
|
|
589
|
+
}
|
|
590
|
+
};
|
|
591
|
+
|
|
592
|
+
// src/ptr/FaroSidecar.ts
|
|
593
|
+
import {
|
|
594
|
+
initializeFaro,
|
|
595
|
+
getWebInstrumentations
|
|
596
|
+
} from "@grafana/faro-web-sdk";
|
|
597
|
+
import { TracingInstrumentation } from "@grafana/faro-web-tracing";
|
|
598
|
+
var FaroSidecar = class _FaroSidecar {
|
|
599
|
+
static instance = null;
|
|
600
|
+
faro = null;
|
|
601
|
+
constructor() {
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* Get the singleton instance of FaroSidecar
|
|
605
|
+
*/
|
|
606
|
+
static getInstance() {
|
|
607
|
+
if (!_FaroSidecar.instance) {
|
|
608
|
+
_FaroSidecar.instance = new _FaroSidecar();
|
|
609
|
+
}
|
|
610
|
+
return _FaroSidecar.instance;
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* Initialize the Faro SDK
|
|
614
|
+
*
|
|
615
|
+
* @param config Configuration options
|
|
616
|
+
* @returns The initialized Faro instance or null if not in a browser environment
|
|
617
|
+
*/
|
|
618
|
+
initialize(config) {
|
|
619
|
+
if (typeof window === "undefined") {
|
|
620
|
+
console.warn("[FaroSidecar] Not initializing Grafana Faro: Non-browser environment detected.");
|
|
621
|
+
return null;
|
|
622
|
+
}
|
|
623
|
+
if (this.faro) {
|
|
624
|
+
console.warn("[FaroSidecar] Grafana Faro is already initialized.");
|
|
625
|
+
return this.faro;
|
|
626
|
+
}
|
|
627
|
+
const {
|
|
628
|
+
url,
|
|
629
|
+
apiKey,
|
|
630
|
+
appName,
|
|
631
|
+
appVersion,
|
|
632
|
+
enableTracing = true,
|
|
633
|
+
namespace = "ptr_runtime",
|
|
634
|
+
additionalInstrumentations = []
|
|
635
|
+
} = config;
|
|
636
|
+
const instrumentations = [
|
|
637
|
+
...getWebInstrumentations(),
|
|
638
|
+
...additionalInstrumentations
|
|
639
|
+
];
|
|
640
|
+
if (enableTracing) {
|
|
641
|
+
instrumentations.push(new TracingInstrumentation());
|
|
642
|
+
}
|
|
643
|
+
const faroOptions = {
|
|
644
|
+
url,
|
|
645
|
+
apiKey,
|
|
646
|
+
app: {
|
|
647
|
+
name: appName,
|
|
648
|
+
version: appVersion,
|
|
649
|
+
namespace
|
|
650
|
+
},
|
|
651
|
+
instrumentations
|
|
652
|
+
};
|
|
653
|
+
try {
|
|
654
|
+
this.faro = initializeFaro(faroOptions);
|
|
655
|
+
console.log(`[FaroSidecar] Grafana Faro initialized for ${appName}@${appVersion}`);
|
|
656
|
+
this.faro.api.pushLog([`PTR Observability Sidecar started for ${appName}`]);
|
|
657
|
+
} catch (error) {
|
|
658
|
+
console.error("[FaroSidecar] Failed to initialize Grafana Faro:", error);
|
|
659
|
+
}
|
|
660
|
+
return this.faro;
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* Get the underlying Faro instance
|
|
664
|
+
*/
|
|
665
|
+
getFaro() {
|
|
666
|
+
return this.faro;
|
|
667
|
+
}
|
|
668
|
+
/**
|
|
669
|
+
* Manually push an error to Faro
|
|
670
|
+
*/
|
|
671
|
+
pushError(error, context) {
|
|
672
|
+
if (this.faro) {
|
|
673
|
+
this.faro.api.pushError(error, { context });
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Manually push a log message to Faro
|
|
678
|
+
*/
|
|
679
|
+
pushLog(message, context) {
|
|
680
|
+
if (this.faro) {
|
|
681
|
+
this.faro.api.pushLog([message], { context });
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
/**
|
|
685
|
+
* Push a custom event
|
|
686
|
+
*/
|
|
687
|
+
pushEvent(name, attributes) {
|
|
688
|
+
if (this.faro) {
|
|
689
|
+
this.faro.api.pushEvent(name, attributes);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
};
|
|
693
|
+
|
|
694
|
+
// src/model/validators/BaseValidator.ts
|
|
695
|
+
var ValidationError = class extends Error {
|
|
696
|
+
constructor(message) {
|
|
697
|
+
super(message);
|
|
698
|
+
this.name = "ValidationError";
|
|
699
|
+
}
|
|
700
|
+
};
|
|
701
|
+
var BaseValidator = class {
|
|
702
|
+
};
|
|
703
|
+
|
|
704
|
+
// src/model/validators/TextValidator.ts
|
|
705
|
+
var TextValidator = class _TextValidator extends BaseValidator {
|
|
706
|
+
static TEXT_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
707
|
+
"text/plain",
|
|
708
|
+
"application/json",
|
|
709
|
+
"application/xml",
|
|
710
|
+
"text/xml",
|
|
711
|
+
"image/svg+xml",
|
|
712
|
+
"text/html",
|
|
713
|
+
"text/markdown"
|
|
714
|
+
]);
|
|
715
|
+
canValidate(mimeType) {
|
|
716
|
+
return _TextValidator.TEXT_MIME_TYPES.has(mimeType);
|
|
717
|
+
}
|
|
718
|
+
validate(content, mimeType) {
|
|
719
|
+
const textContent = this.ensureString(content);
|
|
720
|
+
if (mimeType === "text/plain") {
|
|
721
|
+
this.validatePlainText(textContent);
|
|
722
|
+
} else if (mimeType === "application/json") {
|
|
723
|
+
this.validateJson(textContent);
|
|
724
|
+
} else if (["application/xml", "text/xml", "image/svg+xml"].includes(mimeType)) {
|
|
725
|
+
this.validateXml(textContent);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
ensureString(content) {
|
|
729
|
+
if (typeof content === "string") {
|
|
730
|
+
return content;
|
|
731
|
+
}
|
|
732
|
+
return new TextDecoder().decode(content);
|
|
733
|
+
}
|
|
734
|
+
validatePlainText(content) {
|
|
735
|
+
const trimmed = content.trim();
|
|
736
|
+
if (!trimmed) {
|
|
737
|
+
throw new ValidationError("Invalid content: empty text");
|
|
738
|
+
}
|
|
739
|
+
if (trimmed.length < 3) {
|
|
740
|
+
throw new ValidationError("Invalid content: too short");
|
|
741
|
+
}
|
|
742
|
+
if (!/\s/.test(content)) {
|
|
743
|
+
if (content.split(/\s+/).length === 1 && content.length > 20) {
|
|
744
|
+
throw new ValidationError("Invalid content: likely not plain text");
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
validateJson(content) {
|
|
749
|
+
try {
|
|
750
|
+
const lines = content.split("\n");
|
|
751
|
+
if (lines.some((line) => line.trim().startsWith("//"))) {
|
|
752
|
+
throw new ValidationError("Invalid JSON content: contains comments");
|
|
753
|
+
}
|
|
754
|
+
JSON.parse(content);
|
|
755
|
+
} catch (e) {
|
|
756
|
+
if (e instanceof ValidationError) throw e;
|
|
757
|
+
throw new ValidationError("Invalid JSON content");
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
validateXml(content) {
|
|
761
|
+
const trimmed = content.trim();
|
|
762
|
+
if (!trimmed.startsWith("<") || !trimmed.endsWith(">")) {
|
|
763
|
+
throw new ValidationError("Invalid XML content");
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
};
|
|
767
|
+
|
|
768
|
+
// src/model/validators/BinaryValidator.ts
|
|
769
|
+
var BinaryValidator = class _BinaryValidator extends BaseValidator {
|
|
770
|
+
static BINARY_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
771
|
+
"image/png",
|
|
772
|
+
"image/jpeg",
|
|
773
|
+
"image/gif",
|
|
774
|
+
"image/bmp",
|
|
775
|
+
"application/pdf",
|
|
776
|
+
"application/zip",
|
|
777
|
+
"video/mp4",
|
|
778
|
+
"audio/wav",
|
|
779
|
+
"application/octet-stream"
|
|
780
|
+
]);
|
|
781
|
+
canValidate(mimeType) {
|
|
782
|
+
return _BinaryValidator.BINARY_MIME_TYPES.has(mimeType) || mimeType.startsWith("image/") || mimeType.startsWith("audio/") || mimeType.startsWith("video/");
|
|
783
|
+
}
|
|
784
|
+
validate(content, mimeType) {
|
|
785
|
+
const contentBytes = this.ensureBytes(content);
|
|
786
|
+
if (mimeType.startsWith("image/")) {
|
|
787
|
+
this.validateImage(contentBytes, mimeType);
|
|
788
|
+
} else if (mimeType === "application/pdf") {
|
|
789
|
+
this.validatePdf(contentBytes);
|
|
790
|
+
} else if (mimeType === "application/zip") {
|
|
791
|
+
this.validateZip(contentBytes);
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
ensureBytes(content) {
|
|
795
|
+
if (content instanceof Uint8Array) {
|
|
796
|
+
return content;
|
|
797
|
+
}
|
|
798
|
+
return new TextEncoder().encode(content);
|
|
799
|
+
}
|
|
800
|
+
validateImage(content, mimeType) {
|
|
801
|
+
if (mimeType === "image/png" && content.length <= 8) {
|
|
802
|
+
throw new ValidationError("Invalid PNG content: truncated file");
|
|
803
|
+
} else if (mimeType === "image/jpeg" && content.length <= 3) {
|
|
804
|
+
throw new ValidationError("Invalid JPEG content: truncated file");
|
|
805
|
+
} else if (mimeType === "image/gif" && content.length <= 6) {
|
|
806
|
+
throw new ValidationError("Invalid GIF content: truncated file");
|
|
807
|
+
}
|
|
808
|
+
const signatures = {
|
|
809
|
+
"image/png": "89504e470d0a1a0a",
|
|
810
|
+
// \x89PNG\r\n\x1a\n
|
|
811
|
+
"image/jpeg": "ffd8ff",
|
|
812
|
+
// \xff\xd8\xff
|
|
813
|
+
"image/gif": "47494638"
|
|
814
|
+
// GIF8
|
|
815
|
+
};
|
|
816
|
+
if (signatures[mimeType]) {
|
|
817
|
+
const expectedSig = signatures[mimeType];
|
|
818
|
+
const currentHex = this.toHex(content.slice(0, expectedSig.length / 2));
|
|
819
|
+
if (!currentHex.startsWith(expectedSig) && currentHex !== expectedSig) {
|
|
820
|
+
throw new ValidationError(`Invalid ${mimeType} content: missing proper header`);
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
validatePdf(content) {
|
|
825
|
+
if (!this.startsWithAscii(content, "%PDF-")) {
|
|
826
|
+
throw new ValidationError("Invalid PDF content");
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
validateZip(content) {
|
|
830
|
+
if (content.length <= 4) {
|
|
831
|
+
throw new ValidationError("Invalid ZIP content");
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
// Helpers
|
|
835
|
+
toHex(content) {
|
|
836
|
+
return Array.from(content).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
837
|
+
}
|
|
838
|
+
startsWithAscii(content, str) {
|
|
839
|
+
if (content.length < str.length) return false;
|
|
840
|
+
for (let i = 0; i < str.length; i++) {
|
|
841
|
+
if (content[i] !== str.charCodeAt(i)) return false;
|
|
842
|
+
}
|
|
843
|
+
return true;
|
|
844
|
+
}
|
|
845
|
+
};
|
|
846
|
+
|
|
847
|
+
// src/model/validators/ValidationRegistry.ts
|
|
848
|
+
var ValidationRegistry = class {
|
|
849
|
+
validators;
|
|
850
|
+
constructor() {
|
|
851
|
+
this.validators = [
|
|
852
|
+
new TextValidator(),
|
|
853
|
+
new BinaryValidator()
|
|
854
|
+
];
|
|
855
|
+
}
|
|
856
|
+
/**
|
|
857
|
+
* Validate content using appropriate validator.
|
|
858
|
+
*
|
|
859
|
+
* @param content The content to validate
|
|
860
|
+
* @param mimeType The detected MIME type
|
|
861
|
+
* @throws ValidationError If content is invalid
|
|
862
|
+
*/
|
|
863
|
+
validate(content, mimeType) {
|
|
864
|
+
if (!content || content instanceof Uint8Array && content.length === 0) {
|
|
865
|
+
throw new ValidationError("Empty content");
|
|
866
|
+
}
|
|
867
|
+
if (typeof content === "string" && !content) {
|
|
868
|
+
throw new ValidationError("Empty content");
|
|
869
|
+
}
|
|
870
|
+
for (const validator of this.validators) {
|
|
871
|
+
if (validator.canValidate(mimeType)) {
|
|
872
|
+
validator.validate(content, mimeType);
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
this.basicValidation(content);
|
|
877
|
+
}
|
|
878
|
+
basicValidation(content) {
|
|
879
|
+
if (content instanceof Uint8Array) {
|
|
880
|
+
if (content.length === 0) {
|
|
881
|
+
throw new ValidationError("Invalid content: empty byte array");
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
};
|
|
886
|
+
var validationRegistry = new ValidationRegistry();
|
|
887
|
+
|
|
888
|
+
export {
|
|
889
|
+
computeHash,
|
|
890
|
+
Reader,
|
|
891
|
+
Writer,
|
|
892
|
+
State,
|
|
893
|
+
LensProtocol,
|
|
894
|
+
ErrorCodes,
|
|
895
|
+
VerificationStatus,
|
|
896
|
+
PreconditionViolation,
|
|
897
|
+
PostconditionViolation,
|
|
898
|
+
MCardStore,
|
|
899
|
+
ServiceWorkerPTR,
|
|
900
|
+
FaroSidecar,
|
|
901
|
+
ValidationRegistry,
|
|
902
|
+
validationRegistry
|
|
903
|
+
};
|