@beclab/olaresid 0.1.1 → 0.1.3

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.
Files changed (93) hide show
  1. package/CLI.md +1300 -0
  2. package/README.md +40 -31
  3. package/TAG.md +589 -0
  4. package/dist/abi/RootResolver2ABI.d.ts +54 -0
  5. package/dist/abi/RootResolver2ABI.d.ts.map +1 -0
  6. package/dist/abi/RootResolver2ABI.js +240 -0
  7. package/dist/abi/RootResolver2ABI.js.map +1 -0
  8. package/dist/business/index.d.ts +302 -0
  9. package/dist/business/index.d.ts.map +1 -0
  10. package/dist/business/index.js +1211 -0
  11. package/dist/business/index.js.map +1 -0
  12. package/dist/business/tag-context.d.ts +219 -0
  13. package/dist/business/tag-context.d.ts.map +1 -0
  14. package/dist/business/tag-context.js +560 -0
  15. package/dist/business/tag-context.js.map +1 -0
  16. package/dist/cli.js +2102 -39
  17. package/dist/cli.js.map +1 -1
  18. package/dist/debug.d.ts.map +1 -1
  19. package/dist/debug.js +14 -2
  20. package/dist/debug.js.map +1 -1
  21. package/dist/index.d.ts +51 -2
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +241 -12
  24. package/dist/index.js.map +1 -1
  25. package/dist/utils/crypto-utils.d.ts +130 -0
  26. package/dist/utils/crypto-utils.d.ts.map +1 -0
  27. package/dist/utils/crypto-utils.js +402 -0
  28. package/dist/utils/crypto-utils.js.map +1 -0
  29. package/dist/utils/error-parser.d.ts +35 -0
  30. package/dist/utils/error-parser.d.ts.map +1 -0
  31. package/dist/utils/error-parser.js +202 -0
  32. package/dist/utils/error-parser.js.map +1 -0
  33. package/dist/utils/olares-id.d.ts +36 -0
  34. package/dist/utils/olares-id.d.ts.map +1 -0
  35. package/dist/utils/olares-id.js +52 -0
  36. package/dist/utils/olares-id.js.map +1 -0
  37. package/dist/utils/tag-abi-codec.d.ts +69 -0
  38. package/dist/utils/tag-abi-codec.d.ts.map +1 -0
  39. package/dist/utils/tag-abi-codec.js +144 -0
  40. package/dist/utils/tag-abi-codec.js.map +1 -0
  41. package/dist/utils/tag-type-builder.d.ts +158 -0
  42. package/dist/utils/tag-type-builder.d.ts.map +1 -0
  43. package/dist/utils/tag-type-builder.js +410 -0
  44. package/dist/utils/tag-type-builder.js.map +1 -0
  45. package/examples/crypto-utilities.ts +140 -0
  46. package/examples/domain-context.ts +80 -0
  47. package/examples/generate-mnemonic.ts +149 -0
  48. package/examples/index.ts +1 -1
  49. package/examples/ip.ts +171 -0
  50. package/examples/legacy.ts +10 -10
  51. package/examples/list-wallets.ts +81 -0
  52. package/examples/olares-id-format.ts +197 -0
  53. package/examples/quasar-demo/.eslintrc.js +23 -0
  54. package/examples/quasar-demo/.quasar/app.js +43 -0
  55. package/examples/quasar-demo/.quasar/client-entry.js +38 -0
  56. package/examples/quasar-demo/.quasar/client-prefetch.js +130 -0
  57. package/examples/quasar-demo/.quasar/quasar-user-options.js +16 -0
  58. package/examples/quasar-demo/README.md +49 -0
  59. package/examples/quasar-demo/index.html +11 -0
  60. package/examples/quasar-demo/package-lock.json +6407 -0
  61. package/examples/quasar-demo/package.json +36 -0
  62. package/examples/quasar-demo/quasar.config.js +73 -0
  63. package/examples/quasar-demo/src/App.vue +13 -0
  64. package/examples/quasar-demo/src/css/app.scss +1 -0
  65. package/examples/quasar-demo/src/layouts/MainLayout.vue +21 -0
  66. package/examples/quasar-demo/src/pages/IndexPage.vue +905 -0
  67. package/examples/quasar-demo/src/router/index.ts +25 -0
  68. package/examples/quasar-demo/src/router/routes.ts +11 -0
  69. package/examples/quasar-demo/tsconfig.json +28 -0
  70. package/examples/register-subdomain.ts +152 -0
  71. package/examples/rsa-keypair.ts +148 -0
  72. package/examples/tag-builder.ts +235 -0
  73. package/examples/tag-management.ts +534 -0
  74. package/examples/tag-nested-tuple.ts +190 -0
  75. package/examples/tag-simple.ts +149 -0
  76. package/examples/tag-tagger.ts +217 -0
  77. package/examples/test-nested-tuple-conversion.ts +143 -0
  78. package/examples/test-type-bytes-parser.ts +70 -0
  79. package/examples/transfer-domain.ts +197 -0
  80. package/examples/wallet-management.ts +196 -0
  81. package/package.json +24 -15
  82. package/src/abi/RootResolver2ABI.ts +237 -0
  83. package/src/business/index.ts +1492 -0
  84. package/src/business/tag-context.ts +747 -0
  85. package/src/cli.ts +2772 -39
  86. package/src/debug.ts +17 -2
  87. package/src/index.ts +313 -17
  88. package/src/utils/crypto-utils.ts +459 -0
  89. package/src/utils/error-parser.ts +225 -0
  90. package/src/utils/olares-id.ts +49 -0
  91. package/src/utils/tag-abi-codec.ts +158 -0
  92. package/src/utils/tag-type-builder.ts +469 -0
  93. package/tsconfig.json +1 -1
@@ -0,0 +1,534 @@
1
+ /**
2
+ * Tag Management Example
3
+ *
4
+ * This example demonstrates the complete Tag system workflow:
5
+ * 1. Defining Tag types (simple types, arrays, tuples)
6
+ * 2. Setting and getting Tag values
7
+ * 3. Array operations (push, pop, update)
8
+ * 4. Cross-domain Tag operations
9
+ *
10
+ * Prerequisites:
11
+ * - Set PRIVATE_KEY_OR_MNEMONIC environment variable
12
+ * - You must own the domain you're operating on
13
+ *
14
+ * Run:
15
+ * export PRIVATE_KEY_OR_MNEMONIC="your private key or mnemonic"
16
+ * npx ts-node examples/tag-management.ts
17
+ */
18
+
19
+ import OlaresID, { TagTypeBuilder } from '../src/index';
20
+
21
+ // Helper function to wait for blockchain indexing
22
+ async function waitForIndexing(seconds: number = 3) {
23
+ console.log(`⏳ Waiting ${seconds} seconds for blockchain indexing...`);
24
+ await new Promise((resolve) => setTimeout(resolve, seconds * 1000));
25
+ }
26
+
27
+ async function main() {
28
+ // Initialize OlaresID
29
+ const olaresId = OlaresID.createTestnet();
30
+
31
+ // Get private key or mnemonic from environment
32
+ const privateKeyOrMnemonic = process.env.PRIVATE_KEY_OR_MNEMONIC;
33
+ if (!privateKeyOrMnemonic) {
34
+ console.error(
35
+ '❌ Error: PRIVATE_KEY_OR_MNEMONIC environment variable not set'
36
+ );
37
+ console.log(
38
+ 'Usage: export PRIVATE_KEY_OR_MNEMONIC="your private key or mnemonic"'
39
+ );
40
+ process.exit(1);
41
+ }
42
+
43
+ await olaresId.setSigner(privateKeyOrMnemonic);
44
+ console.log('✅ Signer set successfully\n');
45
+
46
+ // Replace with your domain
47
+ const domainName = process.argv[2];
48
+ if (!domainName) {
49
+ console.error('❌ Error: Please provide a domain name');
50
+ console.log(
51
+ 'Usage: npx ts-node examples/tag-management.ts <domain-name>'
52
+ );
53
+ process.exit(1);
54
+ }
55
+
56
+ const domain = olaresId.domain(domainName);
57
+ const signerAddress = await olaresId.getSigner().getAddress();
58
+ console.log(`🏷️ Managing Tags for domain: ${domainName}`);
59
+ console.log(`👤 Signer: ${signerAddress}\n`);
60
+
61
+ // ========================================
62
+ // Part 1: Define Simple Tag Types
63
+ // ========================================
64
+ console.log('='.repeat(60));
65
+ console.log('Part 1: Defining Simple Tag Types');
66
+ console.log('='.repeat(60));
67
+
68
+ // Define "email" tag
69
+ console.log('📝 Defining "email" tag (type: string)...');
70
+ try {
71
+ await domain.defineSimpleTag('email', 'string');
72
+ console.log('✅ "email" tag defined');
73
+ await waitForIndexing(3);
74
+ } catch (error: any) {
75
+ if (
76
+ error.message?.includes('RedefinedTag') ||
77
+ error.message?.includes('already been defined')
78
+ ) {
79
+ console.log('⚠️ "email" already defined, skipping...');
80
+ } else {
81
+ console.error(`❌ Error: ${error.message}`);
82
+ }
83
+ }
84
+
85
+ // Define "age" tag
86
+ console.log('📝 Defining "age" tag (type: uint8)...');
87
+ try {
88
+ await domain.defineSimpleTag('age', 'uint8');
89
+ console.log('✅ "age" tag defined');
90
+ await waitForIndexing(3);
91
+ } catch (error: any) {
92
+ if (
93
+ error.message?.includes('RedefinedTag') ||
94
+ error.message?.includes('already been defined')
95
+ ) {
96
+ console.log('⚠️ "age" already defined, skipping...');
97
+ } else {
98
+ console.error(`❌ Error: ${error.message}`);
99
+ }
100
+ }
101
+
102
+ // Define "verified" tag
103
+ console.log('📝 Defining "verified" tag (type: bool)...');
104
+ try {
105
+ await domain.defineSimpleTag('verified', 'bool');
106
+ console.log('✅ "verified" tag defined');
107
+ await waitForIndexing(3);
108
+ } catch (error: any) {
109
+ if (
110
+ error.message?.includes('RedefinedTag') ||
111
+ error.message?.includes('already been defined')
112
+ ) {
113
+ console.log('⚠️ "verified" already defined, skipping...');
114
+ } else {
115
+ console.error(`❌ Error: ${error.message}`);
116
+ }
117
+ }
118
+
119
+ // Define "wallets" tag
120
+ console.log('📝 Defining "wallets" tag (type: address[])...');
121
+ try {
122
+ await domain.defineSimpleTag('wallets', 'address[]');
123
+ console.log('✅ "wallets" tag defined\n');
124
+ await waitForIndexing(3);
125
+ } catch (error: any) {
126
+ if (
127
+ error.message?.includes('RedefinedTag') ||
128
+ error.message?.includes('already been defined')
129
+ ) {
130
+ console.log('⚠️ "wallets" already defined, skipping...\n');
131
+ } else {
132
+ console.error(`❌ Error: ${error.message}\n`);
133
+ }
134
+ }
135
+
136
+ // ========================================
137
+ // Part 2: Define Complex Tag Types
138
+ // ========================================
139
+ console.log('='.repeat(60));
140
+ console.log('Part 2: Defining Complex Tag Types (using TagTypeBuilder)');
141
+ console.log('='.repeat(60));
142
+
143
+ const tagCtx = domain.tag();
144
+
145
+ // Define "userInfo" tag
146
+ console.log('📝 Defining "userInfo" tag (complex tuple)...');
147
+ try {
148
+ const userInfoType = TagTypeBuilder.tuple({
149
+ name: TagTypeBuilder.string(),
150
+ age: TagTypeBuilder.uint8(),
151
+ wallets: TagTypeBuilder.addressArray(),
152
+ verified: TagTypeBuilder.bool()
153
+ });
154
+ console.log(` Type bytes: 0x${userInfoType.getTypeBytes()}`);
155
+ console.log(
156
+ ` Field names: ${JSON.stringify(userInfoType.getFieldNames())}`
157
+ );
158
+
159
+ await tagCtx.defineTag('userInfo', userInfoType);
160
+ console.log('✅ "userInfo" tag defined\n');
161
+ await waitForIndexing(3);
162
+ } catch (error: any) {
163
+ if (
164
+ error.message?.includes('RedefinedTag') ||
165
+ error.message?.includes('already been defined')
166
+ ) {
167
+ console.log('⚠️ "userInfo" already defined, skipping...\n');
168
+ } else {
169
+ console.error(`❌ Error: ${error.message}\n`);
170
+ }
171
+ }
172
+
173
+ // Define "socialLinks" tag
174
+ console.log('📝 Defining "socialLinks" tag (string array)...');
175
+ try {
176
+ const socialLinksType = TagTypeBuilder.stringArray();
177
+ await tagCtx.defineTag('socialLinks', socialLinksType);
178
+ console.log('✅ "socialLinks" tag defined\n');
179
+ await waitForIndexing(3);
180
+ } catch (error: any) {
181
+ if (
182
+ error.message?.includes('RedefinedTag') ||
183
+ error.message?.includes('already been defined')
184
+ ) {
185
+ console.log('⚠️ "socialLinks" already defined, skipping...\n');
186
+ } else {
187
+ console.error(`❌ Error: ${error.message}\n`);
188
+ }
189
+ }
190
+
191
+ // ========================================
192
+ // Part 3: Set Taggers (IMPORTANT!)
193
+ // ========================================
194
+ console.log('='.repeat(60));
195
+ console.log('Part 3: Setting Taggers (Required Before Setting Values)');
196
+ console.log('='.repeat(60));
197
+
198
+ try {
199
+ console.log('📝 Setting taggers for all defined tags...');
200
+
201
+ // Set tagger for simple tags
202
+ await domain.setTagger('email', signerAddress);
203
+ console.log(' ✅ Set tagger for "email"');
204
+ await waitForIndexing(2);
205
+
206
+ await domain.setTagger('age', signerAddress);
207
+ console.log(' ✅ Set tagger for "age"');
208
+ await waitForIndexing(2);
209
+
210
+ await domain.setTagger('verified', signerAddress);
211
+ console.log(' ✅ Set tagger for "verified"');
212
+ await waitForIndexing(2);
213
+
214
+ await domain.setTagger('wallets', signerAddress);
215
+ console.log(' ✅ Set tagger for "wallets"');
216
+ await waitForIndexing(2);
217
+
218
+ // Set tagger for complex tags
219
+ await domain.setTagger('userInfo', signerAddress);
220
+ console.log(' ✅ Set tagger for "userInfo"');
221
+ await waitForIndexing(2);
222
+
223
+ await domain.setTagger('socialLinks', signerAddress);
224
+ console.log(' ✅ Set tagger for "socialLinks"\n');
225
+ await waitForIndexing(2);
226
+ } catch (error: any) {
227
+ console.error(`❌ Error setting taggers: ${error.message}\n`);
228
+ }
229
+
230
+ // ========================================
231
+ // Part 4: Set and Get Tag Values
232
+ // ========================================
233
+ console.log('='.repeat(60));
234
+ console.log('Part 4: Setting and Getting Tag Values');
235
+ console.log('='.repeat(60));
236
+
237
+ try {
238
+ // Set simple tag values
239
+ console.log('📝 Setting "email" tag...');
240
+ await domain.setTag('email', 'alice@example.com');
241
+ console.log('✅ "email" tag set');
242
+ await waitForIndexing(3);
243
+
244
+ console.log('📝 Setting "age" tag...');
245
+ await domain.setTag('age', 30);
246
+ console.log('✅ "age" tag set');
247
+ await waitForIndexing(3);
248
+
249
+ console.log('📝 Setting "verified" tag...');
250
+ await domain.setTag('verified', true);
251
+ console.log('✅ "verified" tag set\n');
252
+ await waitForIndexing(3);
253
+
254
+ // Get tag values
255
+ console.log('📖 Reading tag values...');
256
+ const email = await domain.getTag('email');
257
+ const age = await domain.getTag('age');
258
+ const verified = await domain.getTag('verified');
259
+
260
+ console.log(` email: ${email}`);
261
+ console.log(` age: ${age}`);
262
+ console.log(` verified: ${verified}\n`);
263
+ } catch (error: any) {
264
+ console.error(`❌ Error with simple tags: ${error.message}\n`);
265
+ }
266
+
267
+ // ========================================
268
+ // Part 5: Array Operations
269
+ // ========================================
270
+ console.log('='.repeat(60));
271
+ console.log('Part 5: Array Operations');
272
+ console.log('='.repeat(60));
273
+
274
+ try {
275
+ const tagCtx = domain.tag();
276
+
277
+ // Initialize array with setTag first (required before push operations)
278
+ console.log('📝 Initializing "socialLinks" array...');
279
+ await tagCtx.setTag(domainName, 'socialLinks', [
280
+ 'https://twitter.com/alice',
281
+ 'https://github.com/alice',
282
+ 'https://linkedin.com/in/alice'
283
+ ]);
284
+ console.log('✅ Social links initialized with 3 items\n');
285
+ await waitForIndexing(3);
286
+
287
+ // Get array length
288
+ const length = await tagCtx.getArrayLength(domainName, 'socialLinks');
289
+ console.log(`📊 Social links count: ${length}`);
290
+
291
+ // Get all array elements
292
+ console.log('📖 Reading all social links:');
293
+ for (let i = 0; i < length; i++) {
294
+ const link = await tagCtx.getElement(domainName, 'socialLinks', [
295
+ i
296
+ ]);
297
+ console.log(` [${i}] ${link}`);
298
+ }
299
+ console.log();
300
+
301
+ // Test push operation (add a new element)
302
+ console.log('📝 Adding a new social link (push)...');
303
+ try {
304
+ await tagCtx.pushElement(
305
+ domainName,
306
+ 'socialLinks',
307
+ 'https://discord.gg/alice'
308
+ );
309
+ console.log('✅ Added Discord link');
310
+ await waitForIndexing(3);
311
+
312
+ const afterPush = await tagCtx.getArrayLength(
313
+ domainName,
314
+ 'socialLinks'
315
+ );
316
+ console.log(`📊 Social links count after push: ${afterPush}\n`);
317
+ const link = await tagCtx.getElement(domainName, 'socialLinks', [
318
+ afterPush - 1
319
+ ]);
320
+ console.log(` [${afterPush - 1}] ${link}`);
321
+ } catch (error: any) {
322
+ console.log(`⚠️ Push operation failed: ${error.message}\n`);
323
+ }
324
+
325
+ // Update an element
326
+ console.log('📝 Updating first social link...');
327
+ await tagCtx.updateElement(
328
+ domainName,
329
+ 'socialLinks',
330
+ [0],
331
+ 'https://x.com/alice'
332
+ );
333
+ console.log('✅ First link updated\n');
334
+ await waitForIndexing(3);
335
+ const link = await tagCtx.getElement(domainName, 'socialLinks', [0]);
336
+ console.log(` [0] ${link}`);
337
+
338
+ // Pop an element (remove last)
339
+ console.log('📝 Removing last social link (pop)...');
340
+ try {
341
+ await tagCtx.popElement(domainName, 'socialLinks');
342
+ console.log('✅ Last link removed');
343
+ await waitForIndexing(3);
344
+
345
+ const afterPop = await tagCtx.getArrayLength(
346
+ domainName,
347
+ 'socialLinks'
348
+ );
349
+ console.log(`📊 Social links count after pop: ${afterPop}\n`);
350
+ } catch (error: any) {
351
+ console.log(`⚠️ Pop operation failed: ${error.message}\n`);
352
+ }
353
+
354
+ // Verify final state
355
+ const finalLength = await tagCtx.getArrayLength(
356
+ domainName,
357
+ 'socialLinks'
358
+ );
359
+ console.log(`📊 Final social links count: ${finalLength}`);
360
+ console.log('📖 Current social links:');
361
+ for (let i = 0; i < finalLength; i++) {
362
+ const link = await tagCtx.getElement(domainName, 'socialLinks', [
363
+ i
364
+ ]);
365
+ console.log(` [${i}] ${link}`);
366
+ }
367
+ console.log();
368
+ } catch (error: any) {
369
+ console.error(`❌ Error with array operations: ${error.message}\n`);
370
+ }
371
+
372
+ // ========================================
373
+ // Part 6: Complex Tuple Values
374
+ // ========================================
375
+ console.log('='.repeat(60));
376
+ console.log('Part 6: Complex Tuple Values');
377
+ console.log('='.repeat(60));
378
+
379
+ try {
380
+ const tagCtx = domain.tag();
381
+
382
+ // Set tuple value using object notation (automatic conversion!)
383
+ console.log('📝 Setting "userInfo" tuple...');
384
+ console.log(' Using object notation (no fieldNames needed)');
385
+ await tagCtx.setTag(domainName, 'userInfo', {
386
+ name: 'Alice Johnson',
387
+ age: 30,
388
+ wallets: [
389
+ '0x1111111111111111111111111111111111111111',
390
+ '0x2222222222222222222222222222222222222222'
391
+ ],
392
+ verified: true
393
+ });
394
+ console.log('✅ "userInfo" tuple set');
395
+ await waitForIndexing(3);
396
+
397
+ // Get tuple value
398
+ console.log('📖 Reading "userInfo" tuple...');
399
+ const userInfo = await tagCtx.getTag(domainName, 'userInfo');
400
+ console.log(' User Info (raw):');
401
+ console.log(
402
+ ` ${JSON.stringify(userInfo, (_, v) =>
403
+ typeof v === 'bigint' ? v.toString() : v
404
+ )}`
405
+ );
406
+ console.log(
407
+ ` Type: ${typeof userInfo}, IsArray: ${Array.isArray(userInfo)}`
408
+ );
409
+
410
+ // The returned data is directly the tuple array: [field1, field2, field3, field4]
411
+ // No need to access [0], it's already the tuple data
412
+ console.log(' User Info (parsed):');
413
+ console.log(` name: ${userInfo[0]}`);
414
+ console.log(` age: ${userInfo[1]}`);
415
+ console.log(` wallets: ${JSON.stringify(userInfo[2])}`);
416
+ console.log(` verified: ${userInfo[3]}\n`);
417
+
418
+ // Update a specific field in the tuple
419
+ console.log('🔄 Updating "age" field in "userInfo" tuple...');
420
+ console.log(` Current age: ${userInfo[1]}`);
421
+
422
+ // To update a single field, we need to read the entire tuple, modify it, and set it back
423
+ const updatedUserInfo = {
424
+ name: userInfo[0], // Keep existing name
425
+ age: 31, // Update age
426
+ wallets: userInfo[2], // Keep existing wallets
427
+ verified: userInfo[3] // Keep existing verified status
428
+ };
429
+
430
+ await tagCtx.setTag(domainName, 'userInfo', updatedUserInfo);
431
+ console.log('✅ "age" field updated to 31');
432
+ await waitForIndexing(3);
433
+
434
+ // Verify the update
435
+ const updatedInfo = await tagCtx.getTag(domainName, 'userInfo');
436
+ console.log('📖 Updated User Info:');
437
+ console.log(
438
+ ` ${JSON.stringify(updatedInfo, (_, v) =>
439
+ typeof v === 'bigint' ? v.toString() : v
440
+ )}\n`
441
+ );
442
+ } catch (error: any) {
443
+ console.error(`❌ Error with tuple values: ${error.message}\n`);
444
+ }
445
+
446
+ // ========================================
447
+ // Part 7: Get All Tags
448
+ // ========================================
449
+ console.log('='.repeat(60));
450
+ console.log('Part 7: Get All Tags');
451
+ console.log('='.repeat(60));
452
+
453
+ try {
454
+ console.log('📖 Getting all tags for domain...');
455
+ const allTags = await domain.getAllTags();
456
+ console.log(`✅ Found ${allTags.length} tags:\n`);
457
+
458
+ for (const tag of allTags) {
459
+ console.log(` Tag: "${tag.name}"`);
460
+ if (typeof tag.value === 'object' && tag.value !== null) {
461
+ console.log(
462
+ ` Value: ${JSON.stringify(tag.value, (_, v) =>
463
+ typeof v === 'bigint' ? v.toString() : v
464
+ )}`
465
+ );
466
+ } else {
467
+ console.log(` Value: ${tag.value}`);
468
+ }
469
+ }
470
+ console.log();
471
+
472
+ console.log('📖 Getting all defined tag types...');
473
+ const definedTags = await domain.getDefinedTags();
474
+ console.log(`✅ Found ${definedTags.length} defined tag types:`);
475
+ for (const tagName of definedTags) {
476
+ console.log(` - ${tagName}`);
477
+ }
478
+ console.log();
479
+ } catch (error: any) {
480
+ console.error(`❌ Error getting all tags: ${error.message}\n`);
481
+ }
482
+
483
+ // ========================================
484
+ // Part 8: Remove Tags
485
+ // ========================================
486
+ console.log('='.repeat(60));
487
+ console.log('Part 8: Remove Tags (Cleanup)');
488
+ console.log('='.repeat(60));
489
+
490
+ console.log('📝 Attempting to remove "verified" tag value...');
491
+ try {
492
+ // Note: removeTag() removes the tag VALUE from the storage
493
+ // The tag DEFINITION (type) remains and can be reused
494
+ // TagInvalidOp error occurs if the tag has no value to remove
495
+ await domain.removeTag('verified');
496
+ console.log('✅ "verified" tag value removed');
497
+ await waitForIndexing(3);
498
+
499
+ // Verify removal
500
+ const verified = await domain.getTag('verified');
501
+ console.log(` Verification: verified = ${verified} (should be null)`);
502
+
503
+ // Check if tag definition still exists
504
+ const definedTags = await domain.getDefinedTags();
505
+ const stillDefined = definedTags.includes('verified');
506
+ console.log(
507
+ ` Tag definition still exists: ${stillDefined} (should be true)`
508
+ );
509
+ console.log(' 💡 You can set a new value for this tag later\n');
510
+ } catch (error: any) {
511
+ if (
512
+ error.message?.includes('TagInvalidOp') ||
513
+ error.message?.includes('Invalid tag operation')
514
+ ) {
515
+ console.log('⚠️ Tag removal failed: Tag has no value to remove');
516
+ console.log(' This happens when:');
517
+ console.log(' - The tag was never set');
518
+ console.log(' - The tag was already removed');
519
+ console.log(' - Running this example multiple times\n');
520
+ } else {
521
+ console.error(`❌ Error removing tag: ${error.message}\n`);
522
+ }
523
+ }
524
+
525
+ console.log('='.repeat(60));
526
+ console.log('✅ Tag Management Example Completed');
527
+ console.log('='.repeat(60));
528
+ }
529
+
530
+ // Run the example
531
+ main().catch((error) => {
532
+ console.error('❌ Fatal error:', error);
533
+ process.exit(1);
534
+ });