@cooperation/vc-storage 1.0.34 → 1.0.41

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.
@@ -1,50 +1,15 @@
1
- // import { GoogleDriveStorage } from './GoogleDriveStorage.js';
2
- // import { StorageStrategy, StorageType } from '../../types/index.js';
3
- // class StorageContext {
4
- // public strategy: StorageStrategy;
5
- // constructor(strategy: StorageStrategy) {
6
- // this.strategy = strategy;
7
- // }
8
- // setStrategy(strategy: StorageStrategy) {
9
- // this.strategy = strategy;
10
- // }
11
- // async createFolder(folderName: string, parentFolderId?: string) {
12
- // return this.strategy.createFolder(folderName, parentFolderId);
13
- // }
14
- // async save(data: any, folderId: string) {
15
- // return this.strategy.save(data, folderId);
16
- // }
17
- // async retrieve(id: string) {
18
- // return this.strategy.retrieve(id);
19
- // }
20
- // async findFolders(id?: string) {
21
- // return this.strategy.findFolders(id);
22
- // }
23
- // async findLastFile(folderId: string) {
24
- // return this.strategy.findLastFile(folderId);
25
- // }
26
- // async getAllClaims() {
27
- // return this.strategy.getAllClaims();
28
- // }
29
- // async getAllSessions() {
30
- // return this.strategy.getAllSessions();
31
- // }
32
- // async getFileContent(fileId: string) {
33
- // return this.strategy.getFileContent(fileId);
34
- // }
35
- // }
36
- // class StorageFactory {
37
- // static getStorageStrategy(type: StorageType, options: any): StorageStrategy {
38
- // switch (type) {
39
- // case 'googleDrive':
40
- // const { accessToken } = options;
41
- // if (!accessToken) {
42
- // throw new Error('Missing required parameters');
43
- // }
44
- // return new GoogleDriveStorage(accessToken);
45
- // default:
46
- // throw new Error('Unsupported storage type');
47
- // }
48
- // }
49
- // }
50
- // export { StorageContext, StorageFactory };
1
+ import { GoogleDriveStorage } from './GoogleDriveStorage.js';
2
+ import { WASZcapStorage } from './WASZcapStorage.js';
3
+ export function createStorage(kind, options) {
4
+ if (kind === 'googleDrive') {
5
+ if (!options?.accessToken)
6
+ throw new Error('Missing accessToken for Google Drive');
7
+ return new GoogleDriveStorage(options.accessToken);
8
+ }
9
+ if (kind === 'wasZcap') {
10
+ if (!options?.appInstance || !options?.capability)
11
+ throw new Error('Missing appInstance or capability for WAS Zcap');
12
+ return new WASZcapStorage({ appInstance: options.appInstance, capability: options.capability });
13
+ }
14
+ throw new Error('Unsupported storage kind');
15
+ }
@@ -0,0 +1,71 @@
1
+ import { StorageClient } from '@wallet.storage/fetch-client';
2
+ import { WAS_BASE_URL } from '../../app.config.js';
3
+ export class LCWStorage {
4
+ static storageClient;
5
+ signer;
6
+ zcap;
7
+ spaceId;
8
+ constructor({ signer, zcap, spaceId }) {
9
+ this.signer = signer;
10
+ this.zcap = zcap;
11
+ this.spaceId = spaceId;
12
+ }
13
+ getStorageClient() {
14
+ if (!LCWStorage.storageClient) {
15
+ LCWStorage.storageClient = new StorageClient(new URL(WAS_BASE_URL));
16
+ }
17
+ return LCWStorage.storageClient;
18
+ }
19
+ getResource(key) {
20
+ const space = this.getStorageClient().space({
21
+ signer: this.signer,
22
+ id: this.spaceId,
23
+ });
24
+ return space.resource(key);
25
+ }
26
+ async add(key, value) {
27
+ const resource = this.getResource(key);
28
+ const blob = new Blob([JSON.stringify(value)], {
29
+ type: 'application/json',
30
+ });
31
+ const res = await resource.put(blob, {
32
+ signer: this.signer,
33
+ });
34
+ if (!res.ok) {
35
+ throw new Error(`Failed to add resource. Status: ${res.status}`);
36
+ }
37
+ return res;
38
+ }
39
+ async read(key) {
40
+ const resource = this.getResource(key);
41
+ const res = await resource.get({ signer: this.signer });
42
+ if (!res.ok) {
43
+ if (res.status === 404)
44
+ return null;
45
+ throw new Error(`Failed to read resource. Status: ${res.status}`);
46
+ }
47
+ return await res.json();
48
+ }
49
+ async update(key, value) {
50
+ return this.add(key, value); // Overwrite = update
51
+ }
52
+ async delete(key) {
53
+ const resource = this.getResource(key);
54
+ const res = await resource.delete({ signer: this.signer });
55
+ if (!res.ok && res.status !== 404) {
56
+ throw new Error(`Failed to delete resource. Status: ${res.status}`);
57
+ }
58
+ return true;
59
+ }
60
+ async list() {
61
+ // const space = this.getStorageClient().space({
62
+ // signer: this.signer,
63
+ // id: this.spaceId as `urn:uuid:${string}`,
64
+ // });
65
+ // const res = await space.resources().list({ signer: this.signer });
66
+ // if (!res.ok) {
67
+ // throw new Error(`Failed to list resources. Status: ${res.status}`);
68
+ // }
69
+ // return await res.json(); // Should contain list of resource IDs or summaries
70
+ }
71
+ }
@@ -0,0 +1,70 @@
1
+ // @ts-nocheck
2
+ import { ZcapClient } from '@digitalcredentials/ezcap';
3
+ import { Ed25519VerificationKey2020 } from '@digitalcredentials/ed25519-verification-key-2020';
4
+ import { Ed25519Signature2020 } from '@digitalcredentials/ed25519-signature-2020';
5
+ export class WASZcapStorage {
6
+ zcapClient;
7
+ capability;
8
+ ready;
9
+ constructor(config) {
10
+ if (!config?.appInstance?.publicKeyMultibase || !config?.appInstance?.privateKeyMultibase) {
11
+ throw new Error('appInstance is missing key material');
12
+ }
13
+ if (!config?.capability) {
14
+ throw new Error('capability (zcap) is required');
15
+ }
16
+ this.capability = config.capability;
17
+ this.ready = this.initClient(config.appInstance);
18
+ }
19
+ async initClient(appInstance) {
20
+ const key = await Ed25519VerificationKey2020.from(appInstance);
21
+ const signer = key.signer();
22
+ // ezcap expects invocationSigner.id
23
+ this.zcapClient = new ZcapClient({
24
+ SuiteClass: Ed25519Signature2020,
25
+ invocationSigner: signer,
26
+ });
27
+ }
28
+ async request(method, url, body) {
29
+ await this.ready;
30
+ return this.zcapClient.request({
31
+ url,
32
+ capability: this.capability,
33
+ method,
34
+ action: method,
35
+ ...(body ? { blob: body } : {}),
36
+ });
37
+ }
38
+ buildUrlForKey(key) {
39
+ const baseUrl = this.capability?.invocationTarget;
40
+ if (!baseUrl)
41
+ throw new Error('Capability invocationTarget is missing');
42
+ return `${baseUrl}/${encodeURIComponent(key)}`;
43
+ }
44
+ async upload({ key, file }) {
45
+ const url = this.buildUrlForKey(key);
46
+ const res = await this.request('PUT', url, file);
47
+ return this.extractId(res) ?? url;
48
+ }
49
+ async read(key) {
50
+ const url = this.buildUrlForKey(key);
51
+ const res = await this.request('GET', url);
52
+ if (res?.status === 404)
53
+ return null;
54
+ try {
55
+ return await res.json();
56
+ }
57
+ catch (err) {
58
+ console.error('Error reading file:', err);
59
+ }
60
+ }
61
+ async delete(key) {
62
+ const url = this.buildUrlForKey(key);
63
+ const res = await this.request('DELETE', url);
64
+ return res?.ok || res?.status === 404;
65
+ }
66
+ // Updates can be performed by calling upload() with the same key
67
+ extractId(res) {
68
+ return res?.id || res?.result?.id || res?.url;
69
+ }
70
+ }
@@ -0,0 +1,61 @@
1
+ import { Ed25519Signer } from '@did.coop/did-key-ed25519';
2
+ import { v4 as uuidv4 } from 'uuid';
3
+ import { LCWStorage } from '../models/WASStorage.js';
4
+ import { StorageClient } from '@wallet.storage/fetch-client';
5
+ import { WAS_BASE_URL } from '../../app.config.js';
6
+ async function main() {
7
+ const appDidSigner = await Ed25519Signer.generate();
8
+ console.log('Signer:', appDidSigner);
9
+ const spaceUUID = uuidv4();
10
+ const spaceId = `urn:uuid:${spaceUUID}`;
11
+ console.log('Space ID:', spaceId);
12
+ const storage = new StorageClient(new URL(WAS_BASE_URL));
13
+ const space = storage.space({
14
+ signer: appDidSigner,
15
+ id: spaceId,
16
+ });
17
+ const spaceObject = {
18
+ id: spaceId,
19
+ controller: appDidSigner.id.split('#')[0],
20
+ };
21
+ console.log('Creating space with object:', spaceObject);
22
+ const spaceObjectBlob = new Blob([JSON.stringify(spaceObject)], { type: 'application/json' });
23
+ // Create the space
24
+ const response = await space.put(spaceObjectBlob, {
25
+ signer: appDidSigner,
26
+ });
27
+ console.log('🚀 ~ main ~ response:', response);
28
+ console.log('Space PUT response:', {
29
+ status: response.status,
30
+ ok: response.ok,
31
+ });
32
+ if (!response.ok) {
33
+ throw new Error(`Failed to initialize space. Status: ${response.status}`);
34
+ }
35
+ // Store the signer for future connections
36
+ const signerJson = await appDidSigner.toJSON();
37
+ console.log('Signer JSON:', signerJson);
38
+ const lcwStorage = new LCWStorage({ signer: appDidSigner, zcap: {}, spaceId });
39
+ const res = await lcwStorage.add('test', { test: 'test' });
40
+ if (res.ok) {
41
+ console.log('Record added successfully');
42
+ }
43
+ else {
44
+ console.error('Failed to add record');
45
+ }
46
+ const res2 = await lcwStorage.read('test');
47
+ if (res2) {
48
+ console.log('Record read successfully');
49
+ }
50
+ else {
51
+ console.error('Failed to read record');
52
+ }
53
+ const res3 = await lcwStorage.update('test', { test: 'test2' });
54
+ if (res3.ok) {
55
+ console.log('Record updated successfully');
56
+ }
57
+ else {
58
+ console.error('Failed to update record');
59
+ }
60
+ }
61
+ main();
@@ -1,6 +1,10 @@
1
1
  export * from './models/GoogleDriveStorage.js';
2
2
  export * from './models/CredentialEngine.js';
3
3
  export * from './utils/google.js';
4
- export * from './utils/presentation.js';
5
4
  export * from './models/Resume.js';
6
5
  export * from './models/ResumeVC.js';
6
+ export * from './models/WASStorage.js';
7
+ export * from './models/WASZcapStorage.js';
8
+ export * from './models/StorageContext.js';
9
+ export * from './utils/createWASSpace.js';
10
+ export * from './utils/getOrCreateAppDID.js';
@@ -50,7 +50,7 @@ export declare class CredentialEngine {
50
50
  }>;
51
51
  /**
52
52
  * Sign a Verifiable Credential (VC)
53
- * @param {'VC' | 'RECOMMENDATION'} type - The signature type.
53
+ * @param {'VC' | 'RECOMMENDATION' | 'EMPLOYMENT' | 'VOLUNTEERING' | 'PERFORMANCE_REVIEW'} type - The signature type.
54
54
  * @param {string} issuerId - The ID of the issuer [currently we put it as the did id]
55
55
  * @param {KeyPair} keyPair - The key pair to use for signing.
56
56
  * @param {FormDataI | RecommendationFormDataI} formData - The form data to include in the VC.
@@ -3,70 +3,64 @@ type FileType = 'KEYPAIRs' | 'VCs' | 'SESSIONs' | 'DIDs' | 'RECOMMENDATIONs' | '
3
3
  * @class GoogleDriveStorage
4
4
  * @description Class to interact with Google Drive API
5
5
  * @param accessToken - Access token to authenticate with Google Drive API
6
- * @method createFolder - Create a new folder in Google Drive
7
- * @method save - Save data to Google Drive
8
- * @method addCommentToFile - Add a comment to a file in Google Drive
9
- * @method addCommenterRoleToFile - Add commenter role to a file in Google Drive
6
+ * @method saveFile - Save a file to Google Drive
10
7
  * @method retrieve - Retrieve a file from Google Drive
11
- * @method findFolders - Find folders in Google Drive
12
- * @method findLastFile - Find the last file in a folder
13
- * @method getAllVCs - Get all verifiable credentials from Google Drive
14
- * @method getAllSessions - Get all sessions from Google Drive
8
+ * @method createFolder - Create a new folder in Google Drive
9
+ * @method getOrCreateMediaFolder - Get the ID of the MEDIAs folder
10
+ * @method uploadBinaryFile - Upload a binary file to Google Drive
11
+ * @method updateFileData - Update the data of a file
12
+ * @method updateRelationsFile - Update the relations file
15
13
  * @method delete - Delete a file from Google Drive
14
+ * @method checkEmailExists - Check if an email VC exists and return its content
15
+ * @method findFolders - Find folders in Google Drive
16
+ * @method findFolderFiles - Find files in a folder
16
17
  */
17
18
  export declare class GoogleDriveStorage {
18
19
  private accessToken;
19
- folderCache: any;
20
- private fileIdsCache;
21
- private updateFileIdsJson;
20
+ static folderCache: any;
21
+ private static fileIdsCache;
22
22
  constructor(accessToken: string);
23
+ private updateFileIdsJson;
23
24
  private fetcher;
24
25
  private getFileContent;
25
26
  private searchFiles;
27
+ private getOrCreateMediaFolder;
28
+ findFolderFiles(folderId: string): Promise<any[]>;
26
29
  createFolder({ folderName, parentFolderId }: {
27
30
  folderName: string;
28
31
  parentFolderId: string;
29
32
  }): Promise<any>;
30
- getMediaFolderId(): Promise<any>;
33
+ /**
34
+ * Get the ID of the MEDIAs folder (public wrapper for getOrCreateMediaFolder)
35
+ * @returns The folder ID for the MEDIAs folder
36
+ */
37
+ getMediaFolderId(): Promise<string>;
31
38
  uploadBinaryFile({ file }: {
32
39
  file: File;
33
40
  }): Promise<any>;
34
- saveFile({ data, folderId }: {
41
+ saveFile({ data, folderId, fileId }: {
35
42
  data: any;
36
- folderId: string;
43
+ folderId?: string;
44
+ fileId?: string;
37
45
  }): Promise<any>;
38
- /**
39
- * Get file from google drive by id
40
- * @param id
41
- * @returns file content
42
- */
43
46
  retrieve(id: string): Promise<{
44
47
  data: any;
45
48
  id: string;
46
49
  } | null>;
47
- /**
48
- * Get folder by folderId, if folderId == null you will have them all
49
- * @param folderId [Optional]
50
- * @returns
51
- */
52
50
  findFolders(folderId?: string): Promise<any[]>;
53
- /**
54
- * Get all files content for the specified type ('KEYPAIRs' | 'VCs' | 'SESSIONs' | 'DIDs' | 'RECOMMENDATIONs')
55
- * @param type
56
- * @returns
57
- */
58
51
  getAllFilesByType(type: FileType): Promise<any[]>;
52
+ updateFileData(fileId: string, data: {
53
+ fileName: string;
54
+ }): Promise<any>;
59
55
  /**
60
- * Update the name of a file in Google Drive
56
+ * Update the content of an existing file in Google Drive
61
57
  * @param fileId - The ID of the file to update
62
- * @param newFileName - The new name for the file
63
- * @returns The updated file metadata, including the new name
58
+ * @param data - The new content for the file
59
+ * @returns The updated file metadata
64
60
  */
65
- updateFileName(fileId: string, newFileName: string): Promise<any>;
66
- findFileByName(name: string): Promise<any>;
67
- findFilesUnderFolder(folderId: string): Promise<any[]>;
68
- updateFileData(fileId: string, data: {
69
- fileName: string;
61
+ updateFileContent({ fileId, data }: {
62
+ fileId: string;
63
+ data: any;
70
64
  }): Promise<any>;
71
65
  getFileParents(fileId: string): Promise<any>;
72
66
  updateRelationsFile({ relationsFileId, recommendationFileId }: {
@@ -76,25 +70,7 @@ export declare class GoogleDriveStorage {
76
70
  createRelationsFile({ vcFolderId }: {
77
71
  vcFolderId: string;
78
72
  }): Promise<any>;
79
- updateResumeRelation({ authorFolderId, draftFileId, signedFileId, }: {
80
- authorFolderId: string;
81
- draftFileId: string;
82
- signedFileId: string;
83
- }): Promise<any>;
84
- /**
85
- * Delete file by id
86
- * @param id
87
- * @returns
88
- */
89
73
  delete(id: string): Promise<any>;
90
- update(fileId: string, data: any): Promise<any>;
91
- getFileIdsFromAppDataFolder(): Promise<any>;
92
- getAllFilesData(): Promise<any>;
93
- /**
94
- * Check if an email VC exists and return its content
95
- * @param email - The email address to check
96
- * @returns {Promise<{data: any, id: string} | null>} - The email VC content and ID if exists, null otherwise
97
- */
98
74
  checkEmailExists(email: string): Promise<{
99
75
  data: any;
100
76
  id: string;
@@ -1,4 +1,4 @@
1
- import { GoogleDriveStorage } from './GoogleDriveStorage.js';
1
+ import { GoogleDriveStorage } from './GoogleDriveStorage';
2
2
  export declare const resumeFolderTypes: {
3
3
  root: string;
4
4
  nonSigned: string;
@@ -14,9 +14,10 @@ export declare class StorageHandler {
14
14
  }
15
15
  export declare class Resume extends StorageHandler {
16
16
  constructor(storage: GoogleDriveStorage);
17
- saveResume({ resume, type }: {
17
+ saveResume({ resume, type, id }: {
18
18
  resume: any;
19
19
  type: 'sign' | 'unsigned';
20
+ id?: string;
20
21
  }): Promise<any>;
21
22
  find(): Promise<{
22
23
  signed: any[];
@@ -0,0 +1,16 @@
1
+ import { GoogleDriveStorage } from './GoogleDriveStorage.js';
2
+ import { WASZcapStorage } from './WASZcapStorage.js';
3
+ export type StorageKind = 'googleDrive' | 'was' | 'wasZcap';
4
+ export type GoogleDriveOptions = {
5
+ accessToken: string;
6
+ };
7
+ export type WASOptions = {
8
+ signer: any;
9
+ spaceId: string;
10
+ };
11
+ export type WASZcapOptions = {
12
+ appInstance: any;
13
+ capability: any;
14
+ };
15
+ export declare function createStorage(kind: 'googleDrive', options: GoogleDriveOptions): GoogleDriveStorage;
16
+ export declare function createStorage(kind: 'wasZcap', options: WASZcapOptions): WASZcapStorage;
@@ -0,0 +1,30 @@
1
+ export declare class LCWStorage {
2
+ private static storageClient;
3
+ private signer;
4
+ private zcap;
5
+ private spaceId;
6
+ constructor({ signer, zcap, spaceId }: {
7
+ signer: any;
8
+ zcap: any;
9
+ spaceId: string;
10
+ });
11
+ private getStorageClient;
12
+ private getResource;
13
+ add(key: string, value: any): Promise<{
14
+ ok: boolean;
15
+ headers: [string, string][];
16
+ status: number;
17
+ blob(): Promise<import("buffer").Blob>;
18
+ json(): Promise<unknown>;
19
+ }>;
20
+ read(key: string): Promise<unknown>;
21
+ update(key: string, value: any): Promise<{
22
+ ok: boolean;
23
+ headers: [string, string][];
24
+ status: number;
25
+ blob(): Promise<import("buffer").Blob>;
26
+ json(): Promise<unknown>;
27
+ }>;
28
+ delete(key: string): Promise<boolean>;
29
+ list(): Promise<void>;
30
+ }
@@ -0,0 +1,41 @@
1
+ export interface WasZcapStorageConfig {
2
+ appInstance: {
3
+ publicKeyMultibase: string;
4
+ privateKeyMultibase: string;
5
+ controller: string;
6
+ id: string;
7
+ };
8
+ capability: {
9
+ '@context': string[];
10
+ allowedAction: string[];
11
+ controller: string;
12
+ expires: string;
13
+ id: string;
14
+ invocationTarget: string;
15
+ parentCapability: string;
16
+ proof: {
17
+ capabilityChain: string[];
18
+ type: string;
19
+ created: string;
20
+ proofPurpose: string;
21
+ proofValue: string;
22
+ verificationMethod: string;
23
+ };
24
+ };
25
+ }
26
+ export declare class WASZcapStorage {
27
+ private zcapClient;
28
+ private capability;
29
+ private ready;
30
+ constructor(config: WasZcapStorageConfig);
31
+ private initClient;
32
+ private request;
33
+ private buildUrlForKey;
34
+ upload({ key, file }: {
35
+ key: string;
36
+ file: File | Blob;
37
+ }): Promise<string>;
38
+ read(key: string): Promise<any | null>;
39
+ delete(key: string): Promise<boolean>;
40
+ private extractId;
41
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,9 @@
1
+ import { Ed25519Signer } from '@did.coop/did-key-ed25519';
2
+ /**
3
+ * Create a new WAS space
4
+ * @returns {Promise<{ signer: InstanceType<typeof Ed25519Signer>; spaceId: `urn:uuid:${string}` }>}
5
+ */
6
+ export declare function createSpace(): Promise<{
7
+ signer: InstanceType<typeof Ed25519Signer>;
8
+ spaceId: `urn:uuid:${string}`;
9
+ }>;
@@ -1,4 +1,4 @@
1
- import { KeyPair, DidDocument, FormDataI, RecommendationCredential, Credential, RecommendationFormDataI, VerifiableCredential, EmploymentFormDataI, VolunteeringFormDataI, PerformanceReviewFormDataI } from '../../types/credential';
1
+ import { KeyPair, DidDocument, FormDataI, RecommendationCredential, Credential, RecommendationFormDataI, VerifiableCredential, EmploymentFormDataI, VolunteeringFormDataI, PerformanceReviewFormDataI } from '../../types';
2
2
  /**
3
3
  * Create a DID document using the provided key pair.
4
4
  * @param {KeyPair} keyPair - The key pair used to create the DID document.
@@ -0,0 +1,11 @@
1
+ import { Ed25519VerificationKey2020 } from '@digitalbazaar/ed25519-verification-key-2020';
2
+ export interface AppInstanceKeyPair {
3
+ controller: string;
4
+ id: string;
5
+ publicKeyMultibase: string;
6
+ privateKeyMultibase: string;
7
+ }
8
+ export declare function getOrCreateAppInstanceDid(): Promise<{
9
+ did: string;
10
+ keyPair: Ed25519VerificationKey2020;
11
+ }>;
@@ -1,4 +1,4 @@
1
- import { GoogleDriveStorage } from '../models/GoogleDriveStorage.js';
1
+ import { GoogleDriveStorage } from '../models/GoogleDriveStorage';
2
2
  export type FileType = 'VC' | 'DID' | 'SESSION' | 'RECOMMENDATION' | 'KEYPAIR';
3
3
  interface SaveToGooglePropsI {
4
4
  storage: GoogleDriveStorage;
@@ -0,0 +1,27 @@
1
+ import { StorageClient } from '@wallet.storage/fetch-client';
2
+ import { Ed25519Signer } from '@did.coop/did-key-ed25519';
3
+ import { v4 as uuidv4 } from 'uuid';
4
+ import { WAS_BASE_URL } from '../../app.config.js';
5
+ /**
6
+ * Create a new WAS space
7
+ * @returns {Promise<{ signer: InstanceType<typeof Ed25519Signer>; spaceId: `urn:uuid:${string}` }>}
8
+ */
9
+ export async function createSpace() {
10
+ const signer = await Ed25519Signer.generate();
11
+ const controller = signer.id.split('#')[0];
12
+ const spaceUUID = uuidv4();
13
+ const spaceId = `urn:uuid:${spaceUUID}`;
14
+ const client = new StorageClient(new URL(WAS_BASE_URL));
15
+ const space = client.space({ signer, id: spaceId });
16
+ const spaceObject = {
17
+ id: spaceId,
18
+ controller,
19
+ };
20
+ const blob = new Blob([JSON.stringify(spaceObject)], { type: 'application/json' });
21
+ const res = await space.put(blob, { signer });
22
+ if (!res.ok) {
23
+ throw new Error(`Failed to initialize WAS space. Status: ${res.status}`);
24
+ }
25
+ console.log('✅ Provisioned and saved new WAS space');
26
+ return { signer, spaceId };
27
+ }
@@ -177,10 +177,7 @@ export function generateUnsignedRecommendation({ vcId, recommendation, issuerDid
177
177
  export function generateUnsignedEmployment({ formData, issuerDid }) {
178
178
  const issuanceDate = new Date().toISOString();
179
179
  const unsignedCredential = {
180
- '@context': [
181
- 'https://www.w3.org/2018/credentials/v1',
182
- employmentCredentialContext['@context'],
183
- ],
180
+ '@context': ['https://www.w3.org/2018/credentials/v1', employmentCredentialContext['@context']],
184
181
  id: '',
185
182
  type: ['VerifiableCredential', 'EmploymentCredential'],
186
183
  issuer: { id: issuerDid, type: ['Profile'] },
@@ -192,7 +189,7 @@ export function generateUnsignedEmployment({ formData, issuerDid }) {
192
189
  credentialName: formData.credentialName,
193
190
  credentialDuration: formData.credentialDuration,
194
191
  credentialDescription: formData.credentialDescription,
195
- portfolio: formData.portfolio.map(item => ({ name: item.name, url: item.url })),
192
+ portfolio: formData.portfolio.map((item) => ({ name: item.name, url: item.url })),
196
193
  evidenceLink: formData.evidenceLink,
197
194
  evidenceDescription: formData.evidenceDescription,
198
195
  company: formData.company,
@@ -208,10 +205,7 @@ export function generateUnsignedEmployment({ formData, issuerDid }) {
208
205
  export function generateUnsignedVolunteering({ formData, issuerDid }) {
209
206
  const issuanceDate = new Date().toISOString();
210
207
  const unsignedCredential = {
211
- '@context': [
212
- 'https://www.w3.org/2018/credentials/v1',
213
- volunteeringCredentialContext['@context'],
214
- ],
208
+ '@context': ['https://www.w3.org/2018/credentials/v1', volunteeringCredentialContext['@context']],
215
209
  id: '',
216
210
  type: ['VerifiableCredential', 'VolunteeringCredential'],
217
211
  issuer: { id: issuerDid, type: ['Profile'] },
@@ -223,10 +217,10 @@ export function generateUnsignedVolunteering({ formData, issuerDid }) {
223
217
  volunteerWork: formData.volunteerWork,
224
218
  volunteerOrg: formData.volunteerOrg,
225
219
  volunteerDescription: formData.volunteerDescription,
226
- skillsGained: formData.skillsGained ? formData.skillsGained.split(',').map(s => s.trim()) : undefined,
220
+ skillsGained: formData.skillsGained ? formData.skillsGained.split(',').map((s) => s.trim()) : undefined,
227
221
  duration: formData.duration,
228
222
  volunteerDates: formData.volunteerDates,
229
- portfolio: formData.portfolio.map(item => ({ name: item.name, url: item.url })),
223
+ portfolio: formData.portfolio.map((item) => ({ name: item.name, url: item.url })),
230
224
  evidenceLink: formData.evidenceLink,
231
225
  evidenceDescription: formData.evidenceDescription,
232
226
  },
@@ -240,10 +234,7 @@ export function generateUnsignedVolunteering({ formData, issuerDid }) {
240
234
  export function generateUnsignedPerformanceReview({ formData, issuerDid }) {
241
235
  const issuanceDate = new Date().toISOString();
242
236
  const unsignedCredential = {
243
- '@context': [
244
- 'https://www.w3.org/2018/credentials/v1',
245
- performanceReviewCredentialContext['@context'],
246
- ],
237
+ '@context': ['https://www.w3.org/2018/credentials/v1', performanceReviewCredentialContext['@context']],
247
238
  id: '',
248
239
  type: ['VerifiableCredential', 'PerformanceReviewCredential'],
249
240
  issuer: { id: issuerDid, type: ['Profile'] },
@@ -266,7 +257,7 @@ export function generateUnsignedPerformanceReview({ formData, issuerDid }) {
266
257
  overallRating: formData.overallRating,
267
258
  reviewComments: formData.reviewComments,
268
259
  goalsNext: formData.goalsNext,
269
- portfolio: formData.portfolio.map(item => ({ name: item.name, url: item.url })),
260
+ portfolio: formData.portfolio.map((item) => ({ name: item.name, url: item.url })),
270
261
  evidenceLink: formData.evidenceLink,
271
262
  evidenceDescription: formData.evidenceDescription,
272
263
  },