@lukso/transaction-view-headless 0.2.2-dev.a8c9315

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 (76) hide show
  1. package/.turbo/turbo-build.log +51 -0
  2. package/CHANGELOG.md +88 -0
  3. package/COMPONENT_ARCHITECTURE.md +190 -0
  4. package/COMPONENT_CONVERSION_ANALYSIS.md +371 -0
  5. package/CONTEXT_VARIANT_GUIDE.md +425 -0
  6. package/LAZY_LOADING_GUIDE.md +534 -0
  7. package/LICENSE +201 -0
  8. package/PLAYBACK_EXAMPLES.md +658 -0
  9. package/PLAYBACK_GUIDE.md +402 -0
  10. package/README.md +43 -0
  11. package/REGISTRY_SUMMARY.md +638 -0
  12. package/SUFFERING_ECONOMICS.md +346 -0
  13. package/SUFFERING_ECONOMICS.zip +0 -0
  14. package/VIEW_REGISTRATION_GUIDE.md +795 -0
  15. package/dist/composables/index.cjs +1321 -0
  16. package/dist/composables/index.cjs.map +1 -0
  17. package/dist/composables/index.d.cts +6 -0
  18. package/dist/composables/index.d.ts +6 -0
  19. package/dist/composables/index.js +1276 -0
  20. package/dist/composables/index.js.map +1 -0
  21. package/dist/index-CMLU1hKL.d.ts +374 -0
  22. package/dist/index-CSDjhiKV.d.cts +374 -0
  23. package/dist/index.cjs +1534 -0
  24. package/dist/index.cjs.map +1 -0
  25. package/dist/index.d.cts +93 -0
  26. package/dist/index.d.ts +93 -0
  27. package/dist/index.js +1463 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/registry/index.cjs +522 -0
  30. package/dist/registry/index.cjs.map +1 -0
  31. package/dist/registry/index.d.cts +46 -0
  32. package/dist/registry/index.d.ts +46 -0
  33. package/dist/registry/index.js +487 -0
  34. package/dist/registry/index.js.map +1 -0
  35. package/dist/types/index.cjs +19 -0
  36. package/dist/types/index.cjs.map +1 -0
  37. package/dist/types/index.d.cts +203 -0
  38. package/dist/types/index.d.ts +203 -0
  39. package/dist/types/index.js +1 -0
  40. package/dist/types/index.js.map +1 -0
  41. package/dist/utils/index.cjs +217 -0
  42. package/dist/utils/index.cjs.map +1 -0
  43. package/dist/utils/index.d.cts +47 -0
  44. package/dist/utils/index.d.ts +47 -0
  45. package/dist/utils/index.js +188 -0
  46. package/dist/utils/index.js.map +1 -0
  47. package/dist/view-loader-dD86QAlQ.d.cts +121 -0
  48. package/dist/view-loader-ii8AxiQR.d.ts +121 -0
  49. package/package.json +64 -0
  50. package/publish.log +0 -0
  51. package/src/composables/index.ts +59 -0
  52. package/src/composables/use-image-loader.ts +315 -0
  53. package/src/composables/use-transaction-playback.ts +396 -0
  54. package/src/composables/use-transaction-view.ts +309 -0
  55. package/src/config/lukso-config.ts +171 -0
  56. package/src/examples/context-aware-views.ts +300 -0
  57. package/src/examples/example-views.ts +197 -0
  58. package/src/examples/lit-plus-rn-strategy.ts +448 -0
  59. package/src/examples/lsp7-custom-matcher-examples.ts +174 -0
  60. package/src/examples/opt-in-context-examples.ts +372 -0
  61. package/src/examples/optimized-view-definitions.ts +408 -0
  62. package/src/examples/vue-to-lit-compilation.ts +358 -0
  63. package/src/hooks/useAddressQuery.ts +135 -0
  64. package/src/hooks/useAddressResolution.ts +437 -0
  65. package/src/index.ts +36 -0
  66. package/src/registry/index.ts +13 -0
  67. package/src/registry/view-loader.ts +305 -0
  68. package/src/registry/view-registry.ts +135 -0
  69. package/src/types/index.ts +16 -0
  70. package/src/types/view-contexts.ts +72 -0
  71. package/src/types/view-matching.ts +173 -0
  72. package/src/utils/context-matcher.ts +165 -0
  73. package/src/utils/index.ts +5 -0
  74. package/src/utils/view-matcher.ts +338 -0
  75. package/tsconfig.json +20 -0
  76. package/tsup.config.ts +34 -0
@@ -0,0 +1,795 @@
1
+ # View Registration Guide
2
+
3
+ This guide explains how to register transaction views in the registry, with patterns for creating custom view packages that bundle views with decoder enhancers.
4
+
5
+ ## Framework Priority System
6
+
7
+ The registry automatically prefers native framework implementations:
8
+
9
+ ```
10
+ Vue view: Base score + 0.15 bonus = preferred in Vue apps
11
+ RN view: Base score + 0.15 bonus = preferred in RN apps
12
+ Lit view: Base score + 0.00 bonus = universal fallback
13
+ ```
14
+
15
+ **Example:** When both Vue and Lit views exist for LSP7 transfers:
16
+ - Vue app requests `framework: 'vue'` → Vue view gets 0.15 bonus → Vue view selected
17
+ - Angular app requests `framework: 'lit'` → Only Lit view available → Lit view selected
18
+
19
+ ## Basic View Registration
20
+
21
+ ### 1. Register Views at App Startup
22
+
23
+ ```typescript
24
+ // main.ts or app initialization
25
+ import { getGlobalRegistry } from '@lukso/transaction-view-headless'
26
+ import type { ViewDefinition } from '@lukso/transaction-view-headless'
27
+
28
+ const registry = getGlobalRegistry()
29
+
30
+ // Register a single view
31
+ const lsp7View: ViewDefinition = {
32
+ id: 'lsp7-transfer-custom',
33
+ name: 'Custom LSP7 Transfer',
34
+ priority: 100,
35
+ criteria: {
36
+ standard: 'LSP7',
37
+ recordType: 'lsp7-transfer'
38
+ },
39
+ frameworks: {
40
+ lit: {
41
+ loader: { type: 'custom-element', tagName: 'custom-lsp7-view' },
42
+ component: 'custom-lsp7-view'
43
+ }
44
+ }
45
+ }
46
+
47
+ registry.register(lsp7View)
48
+ ```
49
+
50
+ ### 2. Register Multiple Views
51
+
52
+ ```typescript
53
+ import { getGlobalRegistry } from '@lukso/transaction-view-headless'
54
+
55
+ const registry = getGlobalRegistry()
56
+
57
+ const myViews: ViewDefinition[] = [
58
+ { id: 'view-1', /* ... */ },
59
+ { id: 'view-2', /* ... */ },
60
+ { id: 'view-3', /* ... */ }
61
+ ]
62
+
63
+ // Bulk register
64
+ registry.registerMany(myViews)
65
+ ```
66
+
67
+ ### 3. Framework-Specific Registration
68
+
69
+ ```typescript
70
+ // Vue app - register native Vue views
71
+ const vueViews: ViewDefinition[] = [
72
+ {
73
+ id: 'lsp7-transfer-vue',
74
+ name: 'LSP7 Transfer (Vue)',
75
+ priority: 100,
76
+ criteria: {
77
+ standard: 'LSP7',
78
+ recordType: 'lsp7-transfer'
79
+ },
80
+ frameworks: {
81
+ lit: {
82
+ loader: { type: 'custom-element', tagName: 'lsp7-transfer-lit' },
83
+ component: 'lsp7-transfer-lit'
84
+ },
85
+ vue: {
86
+ loader: { type: 'dynamic-import', path: './components/LSP7Transfer.vue' },
87
+ component: 'LSP7Transfer'
88
+ }
89
+ }
90
+ }
91
+ ]
92
+
93
+ registry.registerMany(vueViews)
94
+ ```
95
+
96
+ ## Creating a Custom View Package
97
+
98
+ ### Package Structure
99
+
100
+ ```
101
+ @myorg/custom-lukso-views/
102
+ ├── src/
103
+ │ ├── enhancers/ # Decoder enhancers
104
+ │ │ ├── enhanceCustomToken.ts
105
+ │ │ └── index.ts
106
+ │ ├── views/
107
+ │ │ ├── lit/ # Lit web components
108
+ │ │ │ ├── custom-token-view.ts
109
+ │ │ │ └── index.ts
110
+ │ │ ├── vue/ # Vue components (optional)
111
+ │ │ │ ├── CustomTokenView.vue
112
+ │ │ │ └── index.ts
113
+ │ │ └── react-native/ # RN components (optional)
114
+ │ │ ├── CustomTokenView.tsx
115
+ │ │ └── index.ts
116
+ │ ├── registry/ # View definitions
117
+ │ │ ├── view-definitions.ts
118
+ │ │ └── register.ts
119
+ │ └── index.ts # Main export
120
+ ├── package.json
121
+ └── README.md
122
+ ```
123
+
124
+ ### Example Package Implementation
125
+
126
+ #### 1. Decoder Enhancer (`src/enhancers/enhanceCustomToken.ts`)
127
+
128
+ ```typescript
129
+ import type { DecoderPlugin } from '@lukso/transaction-decoder'
130
+
131
+ /**
132
+ * Enhancer for custom token transactions
133
+ */
134
+ export const enhanceCustomToken: DecoderPlugin = {
135
+ name: 'CustomToken',
136
+
137
+ async enhance(transaction, context) {
138
+ // Check if this is our custom token
139
+ const contractAddress = transaction.to?.toLowerCase()
140
+ const isCustomToken = contractAddress === '0x123...' // Your token address
141
+
142
+ if (!isCustomToken) return transaction
143
+
144
+ // Add custom data
145
+ return {
146
+ ...transaction,
147
+ standard: 'CustomToken',
148
+ recordType: 'custom-token-transfer',
149
+ enhancedInfo: {
150
+ ...transaction.enhancedInfo,
151
+ customData: await fetchCustomTokenData(contractAddress)
152
+ }
153
+ }
154
+ }
155
+ }
156
+
157
+ async function fetchCustomTokenData(address: string) {
158
+ // Fetch custom metadata
159
+ return {
160
+ // ... custom data
161
+ }
162
+ }
163
+ ```
164
+
165
+ #### 2. Lit View (`src/views/lit/custom-token-view.ts`)
166
+
167
+ ```typescript
168
+ import { LitElement, html, css } from 'lit'
169
+ import { customElement, property } from 'lit/decorators.js'
170
+
171
+ @customElement('custom-token-view')
172
+ export class CustomTokenView extends LitElement {
173
+ @property({ type: Object })
174
+ transaction: any
175
+
176
+ static styles = css`
177
+ :host {
178
+ display: block;
179
+ padding: 16px;
180
+ }
181
+ `
182
+
183
+ render() {
184
+ return html`
185
+ <div class="custom-token">
186
+ <h3>Custom Token Transfer</h3>
187
+ <p>Amount: ${this.transaction.value}</p>
188
+ <!-- Custom rendering -->
189
+ </div>
190
+ `
191
+ }
192
+ }
193
+ ```
194
+
195
+ #### 3. Vue View (Optional) (`src/views/vue/CustomTokenView.vue`)
196
+
197
+ ```vue
198
+ <template>
199
+ <div class="custom-token">
200
+ <h3>Custom Token Transfer</h3>
201
+ <p>Amount: {{ transaction.value }}</p>
202
+ <!-- Custom rendering -->
203
+ </div>
204
+ </template>
205
+
206
+ <script setup lang="ts">
207
+ import type { DecoderResult } from '@lukso/transaction-decoder'
208
+
209
+ const props = defineProps<{
210
+ transaction: DecoderResult
211
+ }>()
212
+ </script>
213
+
214
+ <style scoped>
215
+ .custom-token {
216
+ padding: 16px;
217
+ }
218
+ </style>
219
+ ```
220
+
221
+ #### 4. View Definitions (`src/registry/view-definitions.ts`)
222
+
223
+ ```typescript
224
+ import type { ViewDefinition } from '@lukso/transaction-view-headless'
225
+
226
+ export const customTokenViews: ViewDefinition[] = [
227
+ {
228
+ id: 'custom-token-transfer',
229
+ name: 'Custom Token Transfer',
230
+ description: 'View for our custom token transfers',
231
+ priority: 120, // Higher priority than generic views
232
+ criteria: {
233
+ standard: 'CustomToken',
234
+ recordType: 'custom-token-transfer',
235
+ contractAddress: '0x123...' // Specific to your token
236
+ },
237
+ frameworks: {
238
+ lit: {
239
+ loader: { type: 'custom-element', tagName: 'custom-token-view' },
240
+ component: 'custom-token-view',
241
+ package: '@myorg/custom-lukso-views'
242
+ },
243
+ vue: {
244
+ loader: {
245
+ type: 'dynamic-import',
246
+ path: '@myorg/custom-lukso-views/vue/CustomTokenView.vue'
247
+ },
248
+ component: 'CustomTokenView',
249
+ package: '@myorg/custom-lukso-views'
250
+ }
251
+ }
252
+ }
253
+ ]
254
+ ```
255
+
256
+ #### 5. Registration Helper (`src/registry/register.ts`)
257
+
258
+ ```typescript
259
+ import { getGlobalRegistry } from '@lukso/transaction-view-headless'
260
+ import { customTokenViews } from './view-definitions'
261
+ import { enhanceCustomToken } from '../enhancers'
262
+
263
+ /**
264
+ * Register custom token views and enhancers
265
+ * Call this once at app startup
266
+ */
267
+ export function registerCustomTokenViews(options?: {
268
+ /** Whether to load Lit components */
269
+ loadLit?: boolean
270
+ /** Whether to load Vue components */
271
+ loadVue?: boolean
272
+ /** Whether to load React Native components */
273
+ loadReactNative?: boolean
274
+ }) {
275
+ const {
276
+ loadLit = true,
277
+ loadVue = false,
278
+ loadReactNative = false
279
+ } = options || {}
280
+
281
+ // Register view definitions
282
+ const registry = getGlobalRegistry()
283
+ registry.registerMany(customTokenViews)
284
+
285
+ // Load framework-specific components
286
+ if (loadLit) {
287
+ // Import Lit components to register custom elements
288
+ import('../views/lit')
289
+ }
290
+
291
+ if (loadVue) {
292
+ // Vue components loaded on-demand via dynamic import
293
+ // No need to do anything here, loader handles it
294
+ }
295
+
296
+ if (loadReactNative) {
297
+ // React Native components loaded on-demand
298
+ // No need to do anything here, loader handles it
299
+ }
300
+
301
+ console.log('✓ Custom token views registered')
302
+ }
303
+
304
+ /**
305
+ * Register custom token decoder enhancers
306
+ * Add this plugin to your decoder configuration
307
+ */
308
+ export function getCustomTokenEnhancers() {
309
+ return [enhanceCustomToken]
310
+ }
311
+ ```
312
+
313
+ #### 6. Main Export (`src/index.ts`)
314
+
315
+ ```typescript
316
+ // Export registration functions
317
+ export {
318
+ registerCustomTokenViews,
319
+ getCustomTokenEnhancers
320
+ } from './registry/register'
321
+
322
+ // Export view definitions (for inspection)
323
+ export { customTokenViews } from './registry/view-definitions'
324
+
325
+ // Export enhancers (for manual registration)
326
+ export { enhanceCustomToken } from './enhancers'
327
+
328
+ // Export Lit components
329
+ export * from './views/lit'
330
+
331
+ // Export Vue components (tree-shakeable)
332
+ export { default as CustomTokenView } from './views/vue/CustomTokenView.vue'
333
+
334
+ // Export types
335
+ export type { CustomTokenData } from './types'
336
+ ```
337
+
338
+ ### Usage in Your App
339
+
340
+ #### Vue App
341
+
342
+ ```typescript
343
+ // main.ts
344
+ import { createApp } from 'vue'
345
+ import { initializeDecoder } from '@lukso/transaction-decoder'
346
+ import {
347
+ registerCustomTokenViews,
348
+ getCustomTokenEnhancers
349
+ } from '@myorg/custom-lukso-views'
350
+
351
+ // Initialize decoder with custom enhancers
352
+ const decoder = initializeDecoder({
353
+ enhancers: [
354
+ ...getCustomTokenEnhancers(),
355
+ // ... other enhancers
356
+ ]
357
+ })
358
+
359
+ // Register custom views (load Vue + Lit)
360
+ registerCustomTokenViews({
361
+ loadVue: true, // Load native Vue views (gets priority bonus)
362
+ loadLit: true // Load Lit fallbacks
363
+ })
364
+
365
+ const app = createApp(App)
366
+ app.mount('#app')
367
+ ```
368
+
369
+ #### React Native App
370
+
371
+ ```typescript
372
+ // index.tsx
373
+ import { AppRegistry } from 'react-native'
374
+ import { initializeDecoder } from '@lukso/transaction-decoder'
375
+ import {
376
+ registerCustomTokenViews,
377
+ getCustomTokenEnhancers
378
+ } from '@myorg/custom-lukso-views'
379
+
380
+ // Initialize decoder with custom enhancers
381
+ const decoder = initializeDecoder({
382
+ enhancers: getCustomTokenEnhancers()
383
+ })
384
+
385
+ // Register custom views (load RN only)
386
+ registerCustomTokenViews({
387
+ loadReactNative: true, // Load native RN views
388
+ loadLit: false // Skip Lit for RN
389
+ })
390
+
391
+ AppRegistry.registerComponent('App', () => App)
392
+ ```
393
+
394
+ #### Angular/Svelte/Vanilla (Lit Only)
395
+
396
+ ```typescript
397
+ // main.ts
398
+ import { initializeDecoder } from '@lukso/transaction-decoder'
399
+ import {
400
+ registerCustomTokenViews,
401
+ getCustomTokenEnhancers
402
+ } from '@myorg/custom-lukso-views'
403
+
404
+ // Initialize decoder with custom enhancers
405
+ const decoder = initializeDecoder({
406
+ enhancers: getCustomTokenEnhancers()
407
+ })
408
+
409
+ // Register custom views (Lit only)
410
+ registerCustomTokenViews({
411
+ loadLit: true // Lit works everywhere
412
+ })
413
+ ```
414
+
415
+ ## Advanced Patterns
416
+
417
+ ### 1. Lazy Loading Views
418
+
419
+ ```typescript
420
+ // Only register view definitions, load components on-demand
421
+ const lazyViews: ViewDefinition[] = [
422
+ {
423
+ id: 'heavy-view',
424
+ name: 'Heavy View',
425
+ priority: 100,
426
+ criteria: { recordType: 'heavy-transaction' },
427
+ frameworks: {
428
+ lit: {
429
+ loader: {
430
+ type: 'dynamic-import',
431
+ path: () => import('./heavy-view') // Lazy loaded!
432
+ },
433
+ component: 'heavy-view'
434
+ }
435
+ }
436
+ }
437
+ ]
438
+ ```
439
+
440
+ ### 2. Context-Specific Views
441
+
442
+ ```typescript
443
+ const contextViews: ViewDefinition[] = [
444
+ {
445
+ id: 'approval-view',
446
+ name: 'Approval View',
447
+ priority: 150,
448
+ criteria: {
449
+ recordType: 'lsp7-transfer',
450
+ context: 'approval' // Only matches approval context
451
+ },
452
+ frameworks: {
453
+ lit: { /* ... */ }
454
+ }
455
+ },
456
+ {
457
+ id: 'feed-view',
458
+ name: 'Feed View',
459
+ priority: 140,
460
+ criteria: {
461
+ recordType: 'lsp7-transfer',
462
+ context: ['feed', 'list'] // Matches feed OR list
463
+ },
464
+ frameworks: {
465
+ lit: { /* ... */ }
466
+ }
467
+ }
468
+ ]
469
+ ```
470
+
471
+ ### 3. Chain-Specific Views
472
+
473
+ ```typescript
474
+ const chainViews: ViewDefinition[] = [
475
+ {
476
+ id: 'mainnet-only-view',
477
+ name: 'Mainnet Only',
478
+ priority: 120,
479
+ criteria: {
480
+ recordType: 'special-transaction',
481
+ chainId: 42 // LUKSO mainnet only
482
+ },
483
+ frameworks: {
484
+ lit: { /* ... */ }
485
+ }
486
+ }
487
+ ]
488
+ ```
489
+
490
+ ### 4. Custom Matcher Logic (Numeric Scoring)
491
+
492
+ Custom matchers now return a **match count** (number) instead of boolean, allowing for more granular scoring:
493
+
494
+ ```typescript
495
+ const advancedViews: ViewDefinition[] = [
496
+ {
497
+ id: 'lsp7-token-transfer',
498
+ name: 'LSP7 Token Transfer',
499
+ priority: 100,
500
+ criteria: {
501
+ standard: 'LSP7',
502
+ functionName: 'transfer',
503
+ customMatcher: (transaction) => {
504
+ let matches = 0
505
+ // Check metadata populated by enhancer
506
+ if (transaction.custom?.tokenType === 0) matches++
507
+ if (transaction.custom?.isToken === true) matches++
508
+ // Check transaction properties
509
+ if (BigInt(transaction.value || 0) > 0) matches++
510
+ return matches // 0-3 score (normalized to 0-1)
511
+ }
512
+ },
513
+ frameworks: {
514
+ lit: { /* ... */ }
515
+ }
516
+ }
517
+ ]
518
+ ```
519
+
520
+ **Key Points:**
521
+ - Return `0` for no match (view won't be selected)
522
+ - Return `1-10` for matching conditions (higher = better match)
523
+ - Scores are normalized: `Math.min(matchCount / 10, 1)`
524
+ - Can access `transaction.custom` for enhancer-populated metadata
525
+
526
+ #### Example: Token Type Differentiation
527
+
528
+ ```typescript
529
+ // LSP7 Token view (tokenType === 0)
530
+ const tokenView: ViewDefinition = {
531
+ id: 'lsp7-token-transfer',
532
+ name: 'LSP7 Token Transfer',
533
+ priority: 100,
534
+ criteria: {
535
+ standard: 'LSP7',
536
+ functionName: 'transfer',
537
+ customMatcher: (tx) => {
538
+ let matches = 0
539
+ if (tx.custom?.tokenType === 0) matches++
540
+ if (tx.custom?.isToken === true) matches++
541
+ return matches // Returns 2 if token, 0 if not
542
+ }
543
+ },
544
+ frameworks: { /* ... */ }
545
+ }
546
+
547
+ // LSP7 NFT view (tokenType === 1)
548
+ const nftView: ViewDefinition = {
549
+ id: 'lsp7-nft-transfer',
550
+ name: 'LSP7 NFT Transfer',
551
+ priority: 100,
552
+ criteria: {
553
+ standard: 'LSP7',
554
+ functionName: 'transfer',
555
+ customMatcher: (tx) => {
556
+ let matches = 0
557
+ if (tx.custom?.tokenType === 1) matches++
558
+ if (tx.custom?.isNFT === true) matches++
559
+ return matches // Returns 2 if NFT, 0 if not
560
+ }
561
+ },
562
+ frameworks: { /* ... */ }
563
+ }
564
+
565
+ // Generic fallback (no customMatcher)
566
+ const genericView: ViewDefinition = {
567
+ id: 'lsp7-generic-transfer',
568
+ name: 'LSP7 Transfer',
569
+ priority: 50, // Lower priority
570
+ criteria: {
571
+ standard: 'LSP7',
572
+ functionName: 'transfer'
573
+ // No customMatcher - matches all LSP7 transfers
574
+ },
575
+ frameworks: { /* ... */ }
576
+ }
577
+ ```
578
+
579
+ #### Populating Metadata in Enhancers
580
+
581
+ To use `transaction.custom` for view matching, populate it in your enhancer:
582
+
583
+ ```typescript
584
+ // In your decoder enhancer
585
+ export const enhanceLSP7Metadata: EnhancerCallback = async (
586
+ result,
587
+ pluginOptions,
588
+ options
589
+ ) => {
590
+ if (!result.to || !options.cache) return result
591
+
592
+ // Use cache to avoid redundant RPC calls for batch operations
593
+ const metadata = await options.cache.getOrSet(
594
+ `contract-meta:${result.to}:lsp4-token-type`,
595
+ async () => {
596
+ const client = getPublicClient(options.chain)
597
+ const tokenTypeData = await client.readContract({
598
+ address: result.to,
599
+ abi: LSP7_ABI,
600
+ functionName: 'getData',
601
+ args: ['0xe0261fa95db2eb3b5439bd033cda66d56b96f92f243a8228fd87550ed7bdfdb3']
602
+ })
603
+
604
+ const typeValue = tokenTypeData ? parseInt(tokenTypeData as string, 16) : 0
605
+
606
+ return {
607
+ tokenType: typeValue,
608
+ isToken: typeValue === 0,
609
+ isNFT: typeValue === 1,
610
+ isCollection: typeValue === 2
611
+ }
612
+ },
613
+ { ttl: 1000 * 60 * 60 * 24 } // 24 hour cache
614
+ )
615
+
616
+ // Populate custom field for view matching
617
+ result.custom = result.custom || {}
618
+ Object.assign(result.custom, metadata)
619
+
620
+ return result
621
+ }
622
+ ```
623
+
624
+ **Benefits:**
625
+ - Metadata fetched once per contract (cached)
626
+ - Shared across batch operations (5 transactions = 1 RPC call)
627
+ - Synchronous view matching (no async in customMatcher)
628
+ - Graceful degradation (fallback views when metadata missing)
629
+
630
+ ## Package Distribution
631
+
632
+ ### NPM Package
633
+
634
+ ```json
635
+ // package.json
636
+ {
637
+ "name": "@myorg/custom-lukso-views",
638
+ "version": "1.0.0",
639
+ "type": "module",
640
+ "main": "./dist/index.js",
641
+ "types": "./dist/index.d.ts",
642
+ "exports": {
643
+ ".": "./dist/index.js",
644
+ "./lit": "./dist/views/lit/index.js",
645
+ "./vue": "./dist/views/vue/index.js",
646
+ "./react-native": "./dist/views/react-native/index.js",
647
+ "./enhancers": "./dist/enhancers/index.js",
648
+ "./registry": "./dist/registry/index.js"
649
+ },
650
+ "peerDependencies": {
651
+ "@lukso/transaction-decoder": "^1.0.0",
652
+ "@lukso/transaction-view-headless": "^1.0.0",
653
+ "lit": "^3.0.0",
654
+ "vue": "^3.0.0"
655
+ },
656
+ "peerDependenciesMeta": {
657
+ "vue": {
658
+ "optional": true
659
+ },
660
+ "react-native": {
661
+ "optional": true
662
+ }
663
+ }
664
+ }
665
+ ```
666
+
667
+ ### Installation
668
+
669
+ ```bash
670
+ # Install the package
671
+ npm install @myorg/custom-lukso-views
672
+
673
+ # For Vue apps (peer deps)
674
+ npm install vue @lukso/transaction-view-headless
675
+
676
+ # For React Native apps
677
+ npm install react-native @lukso/transaction-view-headless
678
+ ```
679
+
680
+ ## Best Practices
681
+
682
+ ### 1. Priority Guidelines
683
+ - **Core LUKSO standards** (LSP7, LSP8): 100-110
684
+ - **Custom tokens/extensions**: 120-130
685
+ - **Specific contracts**: 140-150
686
+ - **Context-specific views**: 150-160
687
+ - **Fallbacks**: 10-50
688
+
689
+ ### 2. Framework Support Strategy
690
+ - **Always provide Lit**: Universal fallback for all web frameworks
691
+ - **Add Vue optionally**: When you want Vue-specific optimizations
692
+ - **Add RN optionally**: When you want native mobile performance
693
+
694
+ ### 3. View ID Naming
695
+ - Use descriptive, unique IDs: `custom-token-transfer-mainnet`
696
+ - Include namespace for custom views: `@myorg/special-view`
697
+ - Avoid conflicts with core views
698
+
699
+ ### 4. Registration Timing
700
+ - Register views **before** rendering any transactions
701
+ - Register enhancers **when configuring the decoder**
702
+ - Use a single `registerViews()` call at app startup
703
+
704
+ ### 5. Testing
705
+ ```typescript
706
+ import { getGlobalRegistry } from '@lukso/transaction-view-headless'
707
+
708
+ // Test view registration
709
+ const registry = getGlobalRegistry()
710
+ registerCustomTokenViews()
711
+
712
+ const stats = registry.getStats()
713
+ console.log('Registered views:', stats.totalViews)
714
+ console.log('By framework:', stats.viewsByFramework)
715
+
716
+ // Test view selection
717
+ const matches = registry.findMatches(mockTransaction, {
718
+ framework: 'vue',
719
+ minScore: 0.5
720
+ })
721
+ console.log('Best match:', matches[0]?.view.id)
722
+ ```
723
+
724
+ ## Migration from Old System
725
+
726
+ ### Before (Manual component selection)
727
+ ```vue
728
+ <template>
729
+ <LSP7Transfer v-if="isLSP7" />
730
+ <LSP8Transfer v-else-if="isLSP8" />
731
+ <GenericTransaction v-else />
732
+ </template>
733
+ ```
734
+
735
+ ### After (Registry-based)
736
+ ```vue
737
+ <template>
738
+ <TransactionPlayback
739
+ :transaction="transaction"
740
+ :index="1"
741
+ framework="vue"
742
+ />
743
+ </template>
744
+ ```
745
+
746
+ The registry handles all view selection automatically!
747
+
748
+ ## Troubleshooting
749
+
750
+ ### Issue: Custom view not selected
751
+
752
+ **Solution:** Check priority and criteria:
753
+ ```typescript
754
+ const registry = getGlobalRegistry()
755
+ const matches = registry.findMatches(transaction, {
756
+ framework: 'vue',
757
+ minScore: 0
758
+ })
759
+ console.log('All matches:', matches.map(m => ({
760
+ id: m.view.id,
761
+ score: m.score,
762
+ matched: m.matchedCriteria
763
+ })))
764
+ ```
765
+
766
+ ### Issue: Lit view selected instead of Vue view
767
+
768
+ **Solution:** Ensure Vue view is registered and framework is 'vue':
769
+ ```typescript
770
+ // Check if Vue view exists
771
+ const view = registry.getView('my-vue-view')
772
+ console.log('Has Vue framework:', !!view?.frameworks.vue)
773
+
774
+ // Request with correct framework
775
+ const result = useTransactionPlayback(transaction, 1, {
776
+ framework: 'vue' // NOT 'lit'
777
+ })
778
+ ```
779
+
780
+ ### Issue: Components not loading
781
+
782
+ **Solution:** Ensure components are imported:
783
+ ```typescript
784
+ // For Lit components
785
+ import '@myorg/custom-lukso-views/lit'
786
+
787
+ // For Vue components (lazy loaded automatically)
788
+ // No import needed, loader handles it
789
+ ```
790
+
791
+ ## Next Steps
792
+
793
+ - See [PLAYBACK_GUIDE.md](./PLAYBACK_GUIDE.md) for playback system
794
+ - See [PLAYBACK_EXAMPLES.md](./PLAYBACK_EXAMPLES.md) for usage examples
795
+ - See [COMPONENT_ARCHITECTURE.md](../../docs/COMPONENT_ARCHITECTURE.md) for architecture