@bandeira-tech/b3nd-web 0.2.5 → 0.2.6
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/{chunk-S7NJA6B6.js → chunk-FVLXYKYS.js} +372 -1
- package/dist/chunk-GIMIWVPX.js +202 -0
- package/dist/client-B0Ekm99R.d.ts +354 -0
- package/dist/clients/memory/mod.d.ts +1 -1
- package/dist/clients/memory/mod.js +3 -199
- package/dist/src/mod.web.d.ts +1 -1
- package/dist/src/mod.web.js +5 -3
- package/dist/wallet/mod.d.ts +73 -308
- package/dist/wallet/mod.js +11 -3
- package/package.json +1 -1
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
import {
|
|
2
|
+
WalletServerCore
|
|
3
|
+
} from "./chunk-F3W5GZU6.js";
|
|
4
|
+
import {
|
|
5
|
+
exportPrivateKeyPem,
|
|
6
|
+
generateEncryptionKeyPair,
|
|
7
|
+
generateSigningKeyPair
|
|
8
|
+
} from "./chunk-JN75UL5C.js";
|
|
9
|
+
import {
|
|
10
|
+
MemoryClient
|
|
11
|
+
} from "./chunk-GIMIWVPX.js";
|
|
12
|
+
|
|
1
13
|
// wallet/client.ts
|
|
2
14
|
var WalletClient = class {
|
|
3
15
|
constructor(config) {
|
|
@@ -381,6 +393,365 @@ var WalletClient = class {
|
|
|
381
393
|
}
|
|
382
394
|
};
|
|
383
395
|
|
|
396
|
+
// wallet/memory-client.ts
|
|
397
|
+
async function generateTestServerKeys() {
|
|
398
|
+
const identityKeyPair = await generateSigningKeyPair();
|
|
399
|
+
const encryptionKeyPair = await generateEncryptionKeyPair();
|
|
400
|
+
const identityPrivateKeyPem = await exportPrivateKeyPem(
|
|
401
|
+
identityKeyPair.privateKey,
|
|
402
|
+
"PRIVATE KEY"
|
|
403
|
+
);
|
|
404
|
+
const encryptionPrivateKeyBytes = new Uint8Array(
|
|
405
|
+
await crypto.subtle.exportKey("pkcs8", encryptionKeyPair.privateKey)
|
|
406
|
+
);
|
|
407
|
+
const encryptionBase64 = btoa(
|
|
408
|
+
String.fromCharCode(...encryptionPrivateKeyBytes)
|
|
409
|
+
);
|
|
410
|
+
const encryptionPrivateKeyPem = `-----BEGIN PRIVATE KEY-----
|
|
411
|
+
${encryptionBase64.match(/.{1,64}/g)?.join("\n")}
|
|
412
|
+
-----END PRIVATE KEY-----`;
|
|
413
|
+
return {
|
|
414
|
+
identityKey: {
|
|
415
|
+
privateKeyPem: identityPrivateKeyPem,
|
|
416
|
+
publicKeyHex: identityKeyPair.publicKeyHex
|
|
417
|
+
},
|
|
418
|
+
encryptionKey: {
|
|
419
|
+
privateKeyPem: encryptionPrivateKeyPem,
|
|
420
|
+
publicKeyHex: encryptionKeyPair.publicKeyHex
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
function createWalletMemoryClient() {
|
|
425
|
+
return new MemoryClient({
|
|
426
|
+
schema: {
|
|
427
|
+
"immutable://accounts": async () => ({ valid: true }),
|
|
428
|
+
"mutable://accounts": async () => ({ valid: true })
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
var MemoryWalletClient = class _MemoryWalletClient {
|
|
433
|
+
constructor(server) {
|
|
434
|
+
this.currentSession = null;
|
|
435
|
+
this.apiBasePath = "/api/v1";
|
|
436
|
+
this.server = server;
|
|
437
|
+
this.handler = server.getFetchHandler();
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Create a new MemoryWalletClient
|
|
441
|
+
* Use this factory method instead of constructor (async key generation)
|
|
442
|
+
*/
|
|
443
|
+
static async create(config = {}) {
|
|
444
|
+
const serverKeys = config.serverKeys || await generateTestServerKeys();
|
|
445
|
+
const storageClient = config.storageClient || createWalletMemoryClient();
|
|
446
|
+
const serverConfig = {
|
|
447
|
+
serverKeys,
|
|
448
|
+
jwtSecret: config.jwtSecret || "test-jwt-secret-for-memory-wallet-client!!",
|
|
449
|
+
jwtExpirationSeconds: config.jwtExpirationSeconds || 3600,
|
|
450
|
+
deps: {
|
|
451
|
+
credentialClient: storageClient,
|
|
452
|
+
proxyClient: storageClient,
|
|
453
|
+
logger: {
|
|
454
|
+
log: () => {
|
|
455
|
+
},
|
|
456
|
+
warn: () => {
|
|
457
|
+
},
|
|
458
|
+
error: () => {
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
const server = new WalletServerCore(serverConfig);
|
|
464
|
+
return new _MemoryWalletClient(server);
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Make a request to the embedded server
|
|
468
|
+
*/
|
|
469
|
+
async request(method, path, body, headers) {
|
|
470
|
+
const url = `http://memory-wallet${this.apiBasePath}${path}`;
|
|
471
|
+
const requestHeaders = {
|
|
472
|
+
...headers
|
|
473
|
+
};
|
|
474
|
+
if (body) {
|
|
475
|
+
requestHeaders["Content-Type"] = "application/json";
|
|
476
|
+
}
|
|
477
|
+
const request = new Request(url, {
|
|
478
|
+
method,
|
|
479
|
+
headers: requestHeaders,
|
|
480
|
+
body: body ? JSON.stringify(body) : void 0
|
|
481
|
+
});
|
|
482
|
+
return await this.handler(request);
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Make an authenticated request
|
|
486
|
+
*/
|
|
487
|
+
async authRequest(method, path, body) {
|
|
488
|
+
if (!this.currentSession) {
|
|
489
|
+
throw new Error("Not authenticated. Please login first.");
|
|
490
|
+
}
|
|
491
|
+
return this.request(method, path, body, {
|
|
492
|
+
Authorization: `Bearer ${this.currentSession.token}`
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
// ============================================================
|
|
496
|
+
// Session Management (same as WalletClient)
|
|
497
|
+
// ============================================================
|
|
498
|
+
getSession() {
|
|
499
|
+
return this.currentSession;
|
|
500
|
+
}
|
|
501
|
+
setSession(session) {
|
|
502
|
+
this.currentSession = session;
|
|
503
|
+
}
|
|
504
|
+
isAuthenticated() {
|
|
505
|
+
return this.currentSession !== null;
|
|
506
|
+
}
|
|
507
|
+
getUsername() {
|
|
508
|
+
return this.currentSession?.username || null;
|
|
509
|
+
}
|
|
510
|
+
getToken() {
|
|
511
|
+
return this.currentSession?.token || null;
|
|
512
|
+
}
|
|
513
|
+
logout() {
|
|
514
|
+
this.currentSession = null;
|
|
515
|
+
}
|
|
516
|
+
// ============================================================
|
|
517
|
+
// Health & Server Info
|
|
518
|
+
// ============================================================
|
|
519
|
+
async health() {
|
|
520
|
+
const response = await this.request("GET", "/health");
|
|
521
|
+
if (!response.ok) {
|
|
522
|
+
throw new Error(`Health check failed: ${response.statusText}`);
|
|
523
|
+
}
|
|
524
|
+
return await response.json();
|
|
525
|
+
}
|
|
526
|
+
async getServerKeys() {
|
|
527
|
+
const response = await this.request("GET", "/server-keys");
|
|
528
|
+
if (!response.ok) {
|
|
529
|
+
throw new Error(`Failed to get server keys: ${response.statusText}`);
|
|
530
|
+
}
|
|
531
|
+
const data = await response.json();
|
|
532
|
+
if (!data.success) {
|
|
533
|
+
throw new Error(data.error || "Failed to get server keys");
|
|
534
|
+
}
|
|
535
|
+
return {
|
|
536
|
+
identityPublicKeyHex: data.identityPublicKeyHex,
|
|
537
|
+
encryptionPublicKeyHex: data.encryptionPublicKeyHex
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
// ============================================================
|
|
541
|
+
// Authentication
|
|
542
|
+
// ============================================================
|
|
543
|
+
async signup(_credentials) {
|
|
544
|
+
throw new Error("Use signupWithToken(appKey, credentials) \u2014 app token required");
|
|
545
|
+
}
|
|
546
|
+
async login(_credentials) {
|
|
547
|
+
throw new Error("Use loginWithTokenSession(appKey, session, credentials) \u2014 app token + session required");
|
|
548
|
+
}
|
|
549
|
+
async signupWithToken(appKey, tokenOrCredentials, maybeCredentials) {
|
|
550
|
+
const credentials = typeof tokenOrCredentials === "string" ? maybeCredentials : tokenOrCredentials;
|
|
551
|
+
if (!credentials) throw new Error("credentials are required");
|
|
552
|
+
const response = await this.request("POST", `/auth/signup/${appKey}`, {
|
|
553
|
+
token: typeof tokenOrCredentials === "string" ? tokenOrCredentials : void 0,
|
|
554
|
+
type: "password",
|
|
555
|
+
username: credentials.username,
|
|
556
|
+
password: credentials.password
|
|
557
|
+
});
|
|
558
|
+
const data = await response.json();
|
|
559
|
+
if (!response.ok || !data.success) {
|
|
560
|
+
throw new Error(data.error || `Signup failed: ${response.statusText}`);
|
|
561
|
+
}
|
|
562
|
+
return {
|
|
563
|
+
username: data.username,
|
|
564
|
+
token: data.token,
|
|
565
|
+
expiresIn: data.expiresIn
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
async loginWithTokenSession(appKey, tokenOrSession, sessionOrCredentials, maybeCredentials) {
|
|
569
|
+
const session = typeof sessionOrCredentials === "string" && maybeCredentials ? sessionOrCredentials : tokenOrSession;
|
|
570
|
+
const credentials = maybeCredentials || sessionOrCredentials;
|
|
571
|
+
if (!session || typeof session !== "string") throw new Error("session is required");
|
|
572
|
+
const response = await this.request("POST", `/auth/login/${appKey}`, {
|
|
573
|
+
token: typeof tokenOrSession === "string" && maybeCredentials ? tokenOrSession : void 0,
|
|
574
|
+
session,
|
|
575
|
+
type: "password",
|
|
576
|
+
username: credentials.username,
|
|
577
|
+
password: credentials.password
|
|
578
|
+
});
|
|
579
|
+
const data = await response.json();
|
|
580
|
+
if (!response.ok || !data.success) {
|
|
581
|
+
throw new Error(data.error || `Login failed: ${response.statusText}`);
|
|
582
|
+
}
|
|
583
|
+
return {
|
|
584
|
+
username: data.username,
|
|
585
|
+
token: data.token,
|
|
586
|
+
expiresIn: data.expiresIn
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
// ============================================================
|
|
590
|
+
// Password Management
|
|
591
|
+
// ============================================================
|
|
592
|
+
async changePassword(appKey, oldPassword, newPassword) {
|
|
593
|
+
const response = await this.authRequest(
|
|
594
|
+
"POST",
|
|
595
|
+
`/auth/credentials/change-password/${appKey}`,
|
|
596
|
+
{ oldPassword, newPassword }
|
|
597
|
+
);
|
|
598
|
+
const data = await response.json();
|
|
599
|
+
if (!response.ok || !data.success) {
|
|
600
|
+
throw new Error(data.error || `Change password failed: ${response.statusText}`);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
async requestPasswordReset(_username) {
|
|
604
|
+
throw new Error("Use requestPasswordResetWithToken(appKey, username)");
|
|
605
|
+
}
|
|
606
|
+
async resetPassword(_username, _resetToken, _newPassword) {
|
|
607
|
+
throw new Error("Use resetPasswordWithToken(appKey, username, resetToken, newPassword)");
|
|
608
|
+
}
|
|
609
|
+
async requestPasswordResetWithToken(appKey, tokenOrUsername, maybeUsername) {
|
|
610
|
+
const username = maybeUsername || tokenOrUsername;
|
|
611
|
+
const response = await this.request(
|
|
612
|
+
"POST",
|
|
613
|
+
`/auth/credentials/request-password-reset/${appKey}`,
|
|
614
|
+
{ username }
|
|
615
|
+
);
|
|
616
|
+
const data = await response.json();
|
|
617
|
+
if (!response.ok || !data.success) {
|
|
618
|
+
throw new Error(data.error || `Request password reset failed: ${response.statusText}`);
|
|
619
|
+
}
|
|
620
|
+
return {
|
|
621
|
+
resetToken: data.resetToken,
|
|
622
|
+
expiresIn: data.expiresIn
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
async resetPasswordWithToken(appKey, _tokenOrUsername, usernameOrReset, resetToken, newPassword) {
|
|
626
|
+
const username = usernameOrReset;
|
|
627
|
+
if (!resetToken || !newPassword) {
|
|
628
|
+
throw new Error("resetToken and newPassword are required");
|
|
629
|
+
}
|
|
630
|
+
const response = await this.request(
|
|
631
|
+
"POST",
|
|
632
|
+
`/auth/credentials/reset-password/${appKey}`,
|
|
633
|
+
{ username, resetToken, newPassword }
|
|
634
|
+
);
|
|
635
|
+
const data = await response.json();
|
|
636
|
+
if (!response.ok || !data.success) {
|
|
637
|
+
throw new Error(data.error || `Reset password failed: ${response.statusText}`);
|
|
638
|
+
}
|
|
639
|
+
return {
|
|
640
|
+
username: data.username,
|
|
641
|
+
token: data.token,
|
|
642
|
+
expiresIn: data.expiresIn
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
// ============================================================
|
|
646
|
+
// Public Keys
|
|
647
|
+
// ============================================================
|
|
648
|
+
async getPublicKeys(appKey) {
|
|
649
|
+
if (!appKey || typeof appKey !== "string") {
|
|
650
|
+
throw new Error("appKey is required");
|
|
651
|
+
}
|
|
652
|
+
const response = await this.authRequest("GET", `/auth/public-keys/${appKey}`);
|
|
653
|
+
const data = await response.json();
|
|
654
|
+
if (!response.ok || !data.success) {
|
|
655
|
+
throw new Error(data.error || `Get public keys failed: ${response.statusText}`);
|
|
656
|
+
}
|
|
657
|
+
return {
|
|
658
|
+
accountPublicKeyHex: data.accountPublicKeyHex,
|
|
659
|
+
encryptionPublicKeyHex: data.encryptionPublicKeyHex
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
async getMyPublicKeys(appKey) {
|
|
663
|
+
return this.getPublicKeys(appKey);
|
|
664
|
+
}
|
|
665
|
+
// ============================================================
|
|
666
|
+
// Proxy Operations
|
|
667
|
+
// ============================================================
|
|
668
|
+
async proxyWrite(request) {
|
|
669
|
+
const response = await this.authRequest("POST", "/proxy/write", {
|
|
670
|
+
uri: request.uri,
|
|
671
|
+
data: request.data,
|
|
672
|
+
encrypt: request.encrypt
|
|
673
|
+
});
|
|
674
|
+
const data = await response.json();
|
|
675
|
+
if (!response.ok || !data.success) {
|
|
676
|
+
throw new Error(data.error || `Proxy write failed: ${response.statusText}`);
|
|
677
|
+
}
|
|
678
|
+
return data;
|
|
679
|
+
}
|
|
680
|
+
async proxyRead(request) {
|
|
681
|
+
const url = `/proxy/read?uri=${encodeURIComponent(request.uri)}`;
|
|
682
|
+
const response = await this.authRequest("GET", url);
|
|
683
|
+
const data = await response.json();
|
|
684
|
+
if (!response.ok || !data.success) {
|
|
685
|
+
throw new Error(data.error || `Proxy read failed: ${response.statusText}`);
|
|
686
|
+
}
|
|
687
|
+
return data;
|
|
688
|
+
}
|
|
689
|
+
// ============================================================
|
|
690
|
+
// Google OAuth (for completeness - may not work without real Google)
|
|
691
|
+
// ============================================================
|
|
692
|
+
async signupWithGoogle(appKey, token, googleIdToken) {
|
|
693
|
+
if (!token) throw new Error("token is required");
|
|
694
|
+
if (!googleIdToken) throw new Error("googleIdToken is required");
|
|
695
|
+
const response = await this.request("POST", `/auth/signup/${appKey}`, {
|
|
696
|
+
token,
|
|
697
|
+
type: "google",
|
|
698
|
+
googleIdToken
|
|
699
|
+
});
|
|
700
|
+
const data = await response.json();
|
|
701
|
+
if (!response.ok || !data.success) {
|
|
702
|
+
throw new Error(data.error || `Google signup failed: ${response.statusText}`);
|
|
703
|
+
}
|
|
704
|
+
return {
|
|
705
|
+
username: data.username,
|
|
706
|
+
token: data.token,
|
|
707
|
+
expiresIn: data.expiresIn,
|
|
708
|
+
email: data.email,
|
|
709
|
+
name: data.name,
|
|
710
|
+
picture: data.picture
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
async loginWithGoogle(appKey, token, session, googleIdToken) {
|
|
714
|
+
if (!token) throw new Error("token is required");
|
|
715
|
+
if (!session) throw new Error("session is required");
|
|
716
|
+
if (!googleIdToken) throw new Error("googleIdToken is required");
|
|
717
|
+
const response = await this.request("POST", `/auth/login/${appKey}`, {
|
|
718
|
+
token,
|
|
719
|
+
session,
|
|
720
|
+
type: "google",
|
|
721
|
+
googleIdToken
|
|
722
|
+
});
|
|
723
|
+
const data = await response.json();
|
|
724
|
+
if (!response.ok || !data.success) {
|
|
725
|
+
throw new Error(data.error || `Google login failed: ${response.statusText}`);
|
|
726
|
+
}
|
|
727
|
+
return {
|
|
728
|
+
username: data.username,
|
|
729
|
+
token: data.token,
|
|
730
|
+
expiresIn: data.expiresIn,
|
|
731
|
+
email: data.email,
|
|
732
|
+
name: data.name,
|
|
733
|
+
picture: data.picture
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
// ============================================================
|
|
737
|
+
// Testing Utilities
|
|
738
|
+
// ============================================================
|
|
739
|
+
/**
|
|
740
|
+
* Get the underlying WalletServerCore (for testing/inspection)
|
|
741
|
+
*/
|
|
742
|
+
getServer() {
|
|
743
|
+
return this.server;
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* Get server's public keys directly (convenience method)
|
|
747
|
+
*/
|
|
748
|
+
getServerPublicKeys() {
|
|
749
|
+
return this.server.getServerKeys();
|
|
750
|
+
}
|
|
751
|
+
};
|
|
752
|
+
|
|
384
753
|
export {
|
|
385
|
-
WalletClient
|
|
754
|
+
WalletClient,
|
|
755
|
+
generateTestServerKeys,
|
|
756
|
+
MemoryWalletClient
|
|
386
757
|
};
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
// clients/memory/mod.ts
|
|
2
|
+
function target(uri, schema, storage) {
|
|
3
|
+
const url = URL.parse(uri);
|
|
4
|
+
const program = `${url.protocol}//${url.hostname}`;
|
|
5
|
+
if (!schema[program]) {
|
|
6
|
+
return { success: false, error: "Program not found" };
|
|
7
|
+
}
|
|
8
|
+
const node = storage.get(program);
|
|
9
|
+
if (!node) {
|
|
10
|
+
return { success: false, error: "Storage not initialized for program" };
|
|
11
|
+
}
|
|
12
|
+
const parts = url.pathname.substring(1).split("/");
|
|
13
|
+
return {
|
|
14
|
+
success: true,
|
|
15
|
+
program,
|
|
16
|
+
path: url.pathname,
|
|
17
|
+
node,
|
|
18
|
+
parts
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
var MemoryClient = class {
|
|
22
|
+
constructor(config) {
|
|
23
|
+
this.schema = config.schema;
|
|
24
|
+
this.storage = config.storage || /* @__PURE__ */ new Map();
|
|
25
|
+
this.cleanup();
|
|
26
|
+
}
|
|
27
|
+
async write(uri, payload) {
|
|
28
|
+
const result = target(uri, this.schema, this.storage);
|
|
29
|
+
if (!result.success) {
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
const { program, node, parts } = result;
|
|
33
|
+
const validator = this.schema[program];
|
|
34
|
+
const validation = await validator({ uri, value: payload, read: this.read.bind(this) });
|
|
35
|
+
if (!validation.valid) {
|
|
36
|
+
return {
|
|
37
|
+
success: false,
|
|
38
|
+
error: validation.error || "Validation failed"
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
const record = {
|
|
42
|
+
ts: Date.now(),
|
|
43
|
+
data: payload
|
|
44
|
+
};
|
|
45
|
+
let prev = node;
|
|
46
|
+
parts.filter(Boolean).forEach((ns) => {
|
|
47
|
+
if (!prev.children) prev.children = /* @__PURE__ */ new Map();
|
|
48
|
+
if (!prev.children.get(ns)) {
|
|
49
|
+
const newnode = {};
|
|
50
|
+
prev.children.set(ns, newnode);
|
|
51
|
+
prev = newnode;
|
|
52
|
+
} else {
|
|
53
|
+
prev = prev.children.get(ns);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
prev.value = record;
|
|
57
|
+
return {
|
|
58
|
+
success: true,
|
|
59
|
+
record
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
read(uri) {
|
|
63
|
+
const result = target(uri, this.schema, this.storage);
|
|
64
|
+
if (!result.success) {
|
|
65
|
+
return Promise.resolve(result);
|
|
66
|
+
}
|
|
67
|
+
const { node, parts } = result;
|
|
68
|
+
let current = node;
|
|
69
|
+
for (const part of parts.filter(Boolean)) {
|
|
70
|
+
current = current?.children?.get(part);
|
|
71
|
+
if (!current) {
|
|
72
|
+
return Promise.resolve({
|
|
73
|
+
success: false,
|
|
74
|
+
error: `Path not found: ${part}`
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (!current.value) {
|
|
79
|
+
return Promise.resolve({
|
|
80
|
+
success: false,
|
|
81
|
+
error: "Not found"
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
return Promise.resolve({
|
|
85
|
+
success: true,
|
|
86
|
+
record: current.value
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
list(uri, options) {
|
|
90
|
+
const result = target(uri, this.schema, this.storage);
|
|
91
|
+
if (!result.success) {
|
|
92
|
+
return Promise.resolve(result);
|
|
93
|
+
}
|
|
94
|
+
const { node, parts, program, path } = result;
|
|
95
|
+
let current = node;
|
|
96
|
+
const filteredParts = parts.filter(Boolean);
|
|
97
|
+
for (const part of filteredParts) {
|
|
98
|
+
current = current?.children?.get(part);
|
|
99
|
+
if (!current) {
|
|
100
|
+
return Promise.resolve({
|
|
101
|
+
success: false,
|
|
102
|
+
error: `Path not found: ${part}`
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (!current.children?.size) {
|
|
107
|
+
return Promise.resolve({
|
|
108
|
+
success: true,
|
|
109
|
+
data: [],
|
|
110
|
+
pagination: {
|
|
111
|
+
page: options?.page ?? 1,
|
|
112
|
+
limit: options?.limit ?? 50,
|
|
113
|
+
total: 0
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
let items = Array.from(current.children.entries()).map(([key, child]) => ({
|
|
118
|
+
uri: path.endsWith("/") ? `${program}://${path}${key}` : `${program}://${path}/${key}`,
|
|
119
|
+
type: child.value ? "file" : "directory"
|
|
120
|
+
}));
|
|
121
|
+
if (options?.pattern) {
|
|
122
|
+
const regex = new RegExp(options.pattern);
|
|
123
|
+
items = items.filter((item) => regex.test(item.uri));
|
|
124
|
+
}
|
|
125
|
+
if (options?.sortBy === "name") {
|
|
126
|
+
items.sort((a, b) => a.uri.localeCompare(b.uri));
|
|
127
|
+
} else if (options?.sortBy === "timestamp") {
|
|
128
|
+
items.sort((a, b) => {
|
|
129
|
+
const aTs = current.children?.get(a.uri.split("/").pop())?.value?.ts ?? 0;
|
|
130
|
+
const bTs = current.children?.get(b.uri.split("/").pop())?.value?.ts ?? 0;
|
|
131
|
+
return aTs - bTs;
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
if (options?.sortOrder === "desc") {
|
|
135
|
+
items.reverse();
|
|
136
|
+
}
|
|
137
|
+
const page = options?.page ?? 1;
|
|
138
|
+
const limit = options?.limit ?? 50;
|
|
139
|
+
const offset = (page - 1) * limit;
|
|
140
|
+
const paginated = items.slice(offset, offset + limit);
|
|
141
|
+
return Promise.resolve({
|
|
142
|
+
success: true,
|
|
143
|
+
data: paginated,
|
|
144
|
+
pagination: { page, limit, total: items.length }
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
health() {
|
|
148
|
+
return Promise.resolve({
|
|
149
|
+
status: "healthy"
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
getSchema() {
|
|
153
|
+
return Promise.resolve(Object.keys(this.schema));
|
|
154
|
+
}
|
|
155
|
+
cleanup() {
|
|
156
|
+
Object.keys(this.schema).forEach((program) => {
|
|
157
|
+
if (!this.storage.get(program)) {
|
|
158
|
+
this.storage.set(program, { children: /* @__PURE__ */ new Map() });
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
return Promise.resolve();
|
|
162
|
+
}
|
|
163
|
+
delete(uri) {
|
|
164
|
+
const result = target(uri, this.schema, this.storage);
|
|
165
|
+
if (!result.success) {
|
|
166
|
+
return Promise.resolve(result);
|
|
167
|
+
}
|
|
168
|
+
const { node, parts } = result;
|
|
169
|
+
const filteredParts = parts.filter(Boolean);
|
|
170
|
+
if (filteredParts.length === 0) {
|
|
171
|
+
return Promise.resolve({
|
|
172
|
+
success: false,
|
|
173
|
+
error: "Cannot delete root path"
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
let current = node;
|
|
177
|
+
const lastPart = filteredParts.pop();
|
|
178
|
+
for (const part of filteredParts) {
|
|
179
|
+
current = current?.children?.get(part);
|
|
180
|
+
if (!current) {
|
|
181
|
+
return Promise.resolve({
|
|
182
|
+
success: false,
|
|
183
|
+
error: `Path not found: ${part}`
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (!current.children?.has(lastPart)) {
|
|
188
|
+
return Promise.resolve({
|
|
189
|
+
success: false,
|
|
190
|
+
error: `Item not found: ${lastPart}`
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
current.children.delete(lastPart);
|
|
194
|
+
return Promise.resolve({
|
|
195
|
+
success: true
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
export {
|
|
201
|
+
MemoryClient
|
|
202
|
+
};
|