@xyo-network/xl1-rest-block-viewer 2.1.0 → 2.1.1

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/README.md CHANGED
@@ -19,13 +19,14 @@ Finalized XL1 blocks and completed steps never change, so the whole chain can be
19
19
  /payload/<hash>.json ← payload by hash
20
20
  ```
21
21
 
22
- The path builders in `paths.ts` are the shared contract between the publisher that writes these files and this viewer that reads them. Every file except the head pointer is immutable, so responses are cached aggressively (in-memory LRU here, CDN/edge in front). `blocksByStep` serves completed steps only — the in-flight tail step has no file and returns `[]`.
22
+ The path builders in `paths.ts` are the shared contract between the writers of these files and this viewer that reads them. The finalized (immutable) files and the mutable chain state (the head pointer) may live in separate buckets behind separate domains — the publisher writes the finalized files, the chain's finalizer writes the head pointer. Every file except the head pointer is immutable, so responses are cached aggressively (in-memory LRU here, CDN/edge in front). `blocksByStep` serves completed steps only — the in-flight tail step has no file and returns `[]`.
23
23
 
24
24
  ```ts
25
25
  import { RestBlockViewer } from '@xyo-network/xl1-rest-block-viewer'
26
26
 
27
27
  const viewer = await RestBlockViewer.create({
28
- baseUrl: 'https://chain.example.com',
28
+ finalizedBaseUrl: 'https://chain.example.com', // finalized (immutable) files
29
+ chainStateBaseUrl: 'https://head.example.com', // optional: chain state / head pointer domain (defaults to finalizedBaseUrl)
29
30
  headPollIntervalMs: 10_000, // optional: emit headUpdated as the head advances
30
31
  })
31
32
  await viewer.start()
@@ -6,18 +6,24 @@ import type { CreatableProviderParams } from '@xyo-network/xl1-protocol-sdk';
6
6
  import { AbstractCreatableProvider } from '@xyo-network/xl1-protocol-sdk';
7
7
  /** Parameters for RestBlockViewer. */
8
8
  export interface RestBlockViewerParams extends CreatableProviderParams {
9
- /** Base URL of the static chain layout (e.g. a public R2 bucket or CDN domain). */
10
- baseUrl: string;
9
+ /**
10
+ * Base URL serving the mutable chain state (the head pointer, `chain/head.json`) when it
11
+ * lives in a separate bucket/domain from the finalized files. Defaults to `finalizedBaseUrl`.
12
+ */
13
+ chainStateBaseUrl?: string;
11
14
  /** Optional fetch override (custom agents, auth headers, tests). Defaults to the global fetch. */
12
15
  fetchFn?: typeof globalThis.fetch;
16
+ /** Base URL of the finalized (immutable) static chain files (e.g. a public R2 bucket or CDN domain). */
17
+ finalizedBaseUrl: string;
13
18
  headPollIntervalMs?: number;
14
19
  }
15
20
  /**
16
21
  * A BlockViewer over a statically generated REST file layout (see `paths.ts`).
17
22
  *
18
23
  * Reads are anonymous HTTP GETs against immutable files, so everything except the head
19
- * pointer is cached aggressively. `blocksByStep` serves only completed steps the
20
- * in-flight tail step has no static file and returns [].
24
+ * pointer is cached aggressively. The head pointer is read from `chainStateBaseUrl` when
25
+ * the chain state and finalized files live in separate buckets/domains. `blocksByStep`
26
+ * serves only completed steps — the in-flight tail step has no static file and returns [].
21
27
  */
22
28
  export declare class RestBlockViewer extends AbstractCreatableProvider<RestBlockViewerParams, BlockViewer['eventData']> implements BlockViewer {
23
29
  static readonly defaultMoniker: "BlockViewer";
@@ -127,12 +133,14 @@ export declare class RestBlockViewer extends AbstractCreatableProvider<RestBlock
127
133
  private _headPollHash?;
128
134
  private _headPollInProgress;
129
135
  private _headPollTimer;
130
- get baseUrl(): string;
136
+ get chainStateBaseUrl(): string;
137
+ get finalizedBaseUrl(): string;
131
138
  protected get fetchFn(): typeof globalThis.fetch;
132
139
  protected get headPollIntervalMs(): number | undefined;
133
140
  static paramsHandler(params: Partial<RestBlockViewerParams>): Promise<{
134
- baseUrl: string;
141
+ chainStateBaseUrl: string | undefined;
135
142
  fetchFn: typeof fetch | undefined;
143
+ finalizedBaseUrl: string;
136
144
  headPollIntervalMs: number | undefined;
137
145
  context: import("@xyo-network/xl1-protocol-sdk").CreatableProviderContextType;
138
146
  name?: import("@xylabs/sdk-js").CreatableName;
@@ -1 +1 @@
1
- {"version":3,"file":"RestBlockViewer.d.ts","sourceRoot":"","sources":["../../src/RestBlockViewer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"RestBlockViewer.d.ts","sourceRoot":"","sources":["../../src/RestBlockViewer.ts"],"names":[],"mappings":"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"}
@@ -48,8 +48,11 @@ var RestBlockViewer = class extends AbstractCreatableProvider {
48
48
  _headPollHash;
49
49
  _headPollInProgress = false;
50
50
  _headPollTimer = null;
51
- get baseUrl() {
52
- return this.params.baseUrl;
51
+ get chainStateBaseUrl() {
52
+ return this.params.chainStateBaseUrl ?? this.params.finalizedBaseUrl;
53
+ }
54
+ get finalizedBaseUrl() {
55
+ return this.params.finalizedBaseUrl;
53
56
  }
54
57
  get fetchFn() {
55
58
  return this.params.fetchFn ?? globalThis.fetch.bind(globalThis);
@@ -64,8 +67,9 @@ var RestBlockViewer = class extends AbstractCreatableProvider {
64
67
  }
65
68
  return {
66
69
  ...await super.paramsHandler(params),
67
- baseUrl: assertEx(params.baseUrl, () => "baseUrl is required").replace(/\/+$/, ""),
70
+ chainStateBaseUrl: params.chainStateBaseUrl?.replace(/\/+$/, ""),
68
71
  fetchFn: params.fetchFn,
72
+ finalizedBaseUrl: assertEx(params.finalizedBaseUrl, () => "finalizedBaseUrl is required").replace(/\/+$/, ""),
69
73
  headPollIntervalMs
70
74
  };
71
75
  }
@@ -140,7 +144,7 @@ var RestBlockViewer = class extends AbstractCreatableProvider {
140
144
  }
141
145
  async currentBlock() {
142
146
  return await this.spanAsync("currentBlock", async () => {
143
- const parsed = assertEx(await this.fetchJson(headPath()), () => "Head not found");
147
+ const parsed = assertEx(await this.fetchJson(headPath(), this.chainStateBaseUrl), () => "Head not found");
144
148
  return this.cacheBlock(parsed);
145
149
  }, this.context);
146
150
  }
@@ -195,8 +199,8 @@ var RestBlockViewer = class extends AbstractCreatableProvider {
195
199
  this.blockByNumberCache.set(block[0].block, block);
196
200
  return block;
197
201
  }
198
- async fetchJson(path) {
199
- const response = await this.fetchFn(`${this.baseUrl}/${path}`);
202
+ async fetchJson(path, baseUrl = this.finalizedBaseUrl) {
203
+ const response = await this.fetchFn(`${baseUrl}/${path}`);
200
204
  if (response.status === 404) return void 0;
201
205
  assertEx(response.ok, () => `Request failed [${response.status}] for ${path}`);
202
206
  return await response.json();
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/paths.ts", "../../src/RestBlockViewer.ts"],
4
- "sourcesContent": ["import type { Hash } from '@xylabs/sdk-js'\n\n/**\n * Path contract for the static REST chain layout.\n *\n * These builders are the single source of truth for where files live, shared by the\n * publisher (which writes the static files) and `RestBlockViewer` (which reads them),\n * so the layout cannot drift between the two. Paths are relative \u2014 no leading slash \u2014\n * and joined to a base URL by the consumer.\n *\n * Every file except the head pointer is immutable once written:\n * - block and payload files never change after finalization\n * - step files are only written for completed steps\n * - the head pointer is rewritten as the chain advances and must not be cached long-term\n */\n\n/** The mutable head pointer: the current head SignedHydratedBlockWithHashMeta. */\nexport const headPath = (): string => 'chain/head.json'\n\n/** A block by number: SignedHydratedBlockWithHashMeta. */\nexport const blockNumberPath = (block: number): string => `block/number/${block}.json`\n\n/** A block by hash: the same content as its by-number twin. */\nexport const blockHashPath = (hash: Hash): string => `block/hash/${hash}.json`\n\n/**\n * A completed step's blocks: a BlocksStepSummary payload whose `blocks` array is ordered\n * oldest-first. Only complete steps are published; the in-flight tail step has no file.\n */\nexport const blocksStepPath = (stepLevel: number, stepIndex: number): string => `blocks/step/${stepLevel}/${stepIndex}.json`\n\n/** A payload by hash: WithHashMeta<Payload>. */\nexport const payloadPath = (hash: Hash): string => `payload/${hash}.json`\n", "import type { Hash } from '@xylabs/sdk-js'\nimport { assertEx, exists } from '@xylabs/sdk-js'\nimport type { Payload, WithHashMeta } from '@xyo-network/sdk-js'\nimport { PayloadZodLoose, WithHashMetaZod } from '@xyo-network/sdk-js'\nimport { LruCacheMap } from '@xyo-network/xl1-driver-memory'\nimport type {\n BlockRate, BlockViewer, ChainId,\n SignedHydratedBlockWithHashMeta, SingleTimeConfig, TimeDurations, XL1BlockNumber, XL1BlockRange,\n} from '@xyo-network/xl1-protocol-lib'\nimport {\n asSignedHydratedBlockWithHashMeta, asXL1BlockNumber, BlockViewerMoniker, stepSize,\n} from '@xyo-network/xl1-protocol-lib'\nimport type { CreatableProviderParams } from '@xyo-network/xl1-protocol-sdk'\nimport {\n AbstractCreatableProvider, asBlocksStepSummary, blocksMaxStep, calculateBlockRate, calculateStepSizeRate, calculateTimeRate, creatableProvider,\n} from '@xyo-network/xl1-protocol-sdk'\n\nimport {\n blockHashPath, blockNumberPath, blocksStepPath, headPath, payloadPath,\n} from './paths.ts'\n\n/** Parameters for RestBlockViewer. */\nexport interface RestBlockViewerParams extends CreatableProviderParams {\n /** Base URL of the static chain layout (e.g. a public R2 bucket or CDN domain). */\n baseUrl: string\n /** Optional fetch override (custom agents, auth headers, tests). Defaults to the global fetch. */\n fetchFn?: typeof globalThis.fetch\n headPollIntervalMs?: number\n}\n\nconst MIN_HEAD_POLL_INTERVAL_MS = 5000\n\nconst PayloadFileZod = WithHashMetaZod(PayloadZodLoose)\n\n/**\n * A BlockViewer over a statically generated REST file layout (see `paths.ts`).\n *\n * Reads are anonymous HTTP GETs against immutable files, so everything except the head\n * pointer is cached aggressively. `blocksByStep` serves only completed steps \u2014 the\n * in-flight tail step has no static file and returns [].\n */\n@creatableProvider()\nexport class RestBlockViewer extends AbstractCreatableProvider<RestBlockViewerParams, BlockViewer['eventData']> implements BlockViewer {\n static readonly defaultMoniker = BlockViewerMoniker\n static readonly dependencies = []\n static readonly monikers = [BlockViewerMoniker]\n moniker = RestBlockViewer.defaultMoniker\n\n protected blockByHashCache = new LruCacheMap<Hash, SignedHydratedBlockWithHashMeta>({ max: 2000 })\n protected blockByNumberCache = new LruCacheMap<XL1BlockNumber, SignedHydratedBlockWithHashMeta>({ max: 2000 })\n protected payloadCache = new LruCacheMap<Hash, WithHashMeta<Payload>>({ max: 10_000 })\n protected stepCache = new LruCacheMap<string, SignedHydratedBlockWithHashMeta[]>({ max: 4 })\n\n private _headPollHash?: Hash\n private _headPollInProgress = false\n private _headPollTimer: ReturnType<typeof setInterval> | null = null\n\n get baseUrl(): string {\n return this.params.baseUrl\n }\n\n protected get fetchFn(): typeof globalThis.fetch {\n return this.params.fetchFn ?? globalThis.fetch.bind(globalThis)\n }\n\n protected get headPollIntervalMs() {\n return this.params.headPollIntervalMs\n }\n\n static override async paramsHandler(params: Partial<RestBlockViewerParams>) {\n const headPollIntervalMs = params.headPollIntervalMs\n if (headPollIntervalMs !== undefined) {\n assertEx(headPollIntervalMs >= MIN_HEAD_POLL_INTERVAL_MS, () => `headPollIntervalMs must be at least ${MIN_HEAD_POLL_INTERVAL_MS}ms`)\n }\n return {\n ...await super.paramsHandler(params),\n baseUrl: assertEx(params.baseUrl, () => 'baseUrl is required').replace(/\\/+$/, ''),\n fetchFn: params.fetchFn,\n headPollIntervalMs,\n } satisfies RestBlockViewerParams\n }\n\n async blockByHash(hash: Hash): Promise<SignedHydratedBlockWithHashMeta | null> {\n return await this.spanAsync('blockByHash', async () => {\n const cached = this.blockByHashCache.get(hash)\n if (cached) return cached\n const parsed = await this.fetchJson(blockHashPath(hash))\n return parsed === undefined ? null : this.cacheBlock(parsed)\n }, { ...this.context, timeBudgetLimit: 100 })\n }\n\n async blockByNumber(blockNumber: XL1BlockNumber): Promise<SignedHydratedBlockWithHashMeta | null> {\n return await this.spanAsync('blockByNumber', async () => {\n const cached = this.blockByNumberCache.get(blockNumber)\n if (cached) return cached\n const parsed = await this.fetchJson(blockNumberPath(blockNumber))\n return parsed === undefined ? null : this.cacheBlock(parsed)\n }, { ...this.context, timeBudgetLimit: 100 })\n }\n\n async blocksByHash(hash: Hash, limit = 50): Promise<SignedHydratedBlockWithHashMeta[]> {\n return await this.spanAsync('blocksByHash', async () => {\n assertEx(limit > 0, () => 'limit must be greater than 0')\n assertEx(limit <= 100, () => 'limit must be less than 100')\n const blocks: SignedHydratedBlockWithHashMeta[] = []\n let current = await this.blockByHash(hash)\n while (current && blocks.length < limit) {\n blocks.push(current)\n const previousHash = current[0].previous\n if (previousHash === null) break\n current = await this.blockByHash(previousHash)\n }\n return blocks\n }, { ...this.context, timeBudgetLimit: 300 })\n }\n\n async blocksByNumber(blockNumber: XL1BlockNumber, limit = 50): Promise<SignedHydratedBlockWithHashMeta[]> {\n return await this.spanAsync('blocksByNumber', async () => {\n assertEx(limit > 0, () => 'limit must be greater than 0')\n assertEx(limit <= 100, () => 'limit must be less than 100')\n const blocks: SignedHydratedBlockWithHashMeta[] = []\n let current = await this.blockByNumber(blockNumber)\n while (current && blocks.length < limit) {\n blocks.push(current)\n if (current[0].block === 0) break\n current = await this.blockByNumber(asXL1BlockNumber(current[0].block - 1, true))\n }\n return blocks\n }, this.context)\n }\n\n async blocksByStep(stepLevel: number, stepIndex: number): Promise<SignedHydratedBlockWithHashMeta[]> {\n return await this.spanAsync('blocksByStep', async () => {\n assertEx(Number.isInteger(stepIndex) && stepIndex >= 0, () => 'stepIndex must be a non-negative integer')\n stepSize(stepLevel) // validates the level against StepSizes\n assertEx(\n stepLevel <= blocksMaxStep,\n () => `blocksByStep does not support step levels above ${blocksMaxStep} (requested ${stepLevel})`,\n )\n const cacheKey = `${stepLevel}|${stepIndex}`\n const cached = this.stepCache.get(cacheKey)\n if (cached) return cached\n const parsed = await this.fetchJson(blocksStepPath(stepLevel, stepIndex))\n // Only completed steps are published; the in-flight tail step has no file\n if (parsed === undefined) return []\n const summary = asBlocksStepSummary(parsed, { required: true })\n // Step files store blocks oldest-first; the viewer interface returns newest-first\n const blocks = summary.blocks.map(b => asSignedHydratedBlockWithHashMeta(b, true)).toReversed()\n this.stepCache.set(cacheKey, blocks)\n return blocks\n }, this.context)\n }\n\n chainId(): Promise<ChainId>\n chainId(blockNumber: XL1BlockNumber): Promise<ChainId>\n chainId(blockNumber: 'latest'): Promise<ChainId>\n async chainId(blockNumber: XL1BlockNumber | 'latest' = 'latest'): Promise<ChainId> {\n return await this.spanAsync('chainId', async () => {\n return blockNumber === 'latest'\n ? (await this.currentBlock())[0].chain\n : assertEx(await this.blockByNumber(blockNumber), () => `Block not found [${blockNumber}]`)[0].chain\n }, this.context)\n }\n\n async currentBlock(): Promise<SignedHydratedBlockWithHashMeta> {\n return await this.spanAsync('currentBlock', async () => {\n const parsed = assertEx(await this.fetchJson(headPath()), () => 'Head not found')\n return this.cacheBlock(parsed)\n }, this.context)\n }\n\n async currentBlockHash(): Promise<Hash> {\n return (await this.currentBlock())[0]._hash\n }\n\n async currentBlockNumber(): Promise<XL1BlockNumber> {\n return (await this.currentBlock())[0].block\n }\n\n async payloadByHash(hash: Hash): Promise<WithHashMeta<Payload> | null> {\n const [payload] = await this.payloadsByHash([hash])\n return payload ?? null\n }\n\n async payloadsByHash(hashes: Hash[]): Promise<WithHashMeta<Payload>[]> {\n return await this.spanAsync('payloadsByHash', async () => {\n const results = await Promise.all(hashes.map(async (hash) => {\n const cached = this.payloadCache.get(hash)\n if (cached) return cached\n const parsed = await this.fetchJson(payloadPath(hash))\n if (parsed === undefined) return\n const payload = PayloadFileZod.parse(parsed)\n this.payloadCache.set(hash, payload)\n return payload\n }))\n return results.filter(exists)\n }, this.context)\n }\n\n async rate(range: XL1BlockRange, timeUnit?: keyof TimeDurations): Promise<BlockRate> {\n return await calculateBlockRate(this, range, timeUnit)\n }\n\n async stepSizeRate(start: XL1BlockNumber, stepIndex: number, count = 1, timeUnit?: keyof TimeDurations): Promise<BlockRate> {\n return await calculateStepSizeRate(this, start, stepIndex, count, timeUnit)\n }\n\n async timeDurationRate(\n timeConfig: SingleTimeConfig,\n startBlockNumber?: XL1BlockNumber,\n timeUnit?: keyof TimeDurations,\n toleranceMs?: number,\n maxAttempts?: number,\n ): Promise<BlockRate> {\n return await calculateTimeRate(this, timeConfig, startBlockNumber, timeUnit, toleranceMs, maxAttempts)\n }\n\n protected override async startHandler(): Promise<void> {\n await super.startHandler()\n if (this.headPollIntervalMs === undefined) return\n await this.pollHead(false)\n this.startHeadPolling()\n }\n\n protected override async stopHandler(): Promise<void> {\n this.stopHeadPolling()\n this._headPollHash = undefined\n await super.stopHandler()\n }\n\n /** Validates a parsed block file and populates both block caches. */\n private cacheBlock(parsed: unknown): SignedHydratedBlockWithHashMeta {\n const block = asSignedHydratedBlockWithHashMeta(parsed, true)\n this.blockByHashCache.set(block[0]._hash, block)\n this.blockByNumberCache.set(block[0].block, block)\n return block\n }\n\n private async fetchJson(path: string): Promise<unknown> {\n const response = await this.fetchFn(`${this.baseUrl}/${path}`)\n if (response.status === 404) return undefined\n assertEx(response.ok, () => `Request failed [${response.status}] for ${path}`)\n return await response.json()\n }\n\n private async pollHead(emitOnChange: boolean): Promise<void> {\n if (this._headPollInProgress) return\n this._headPollInProgress = true\n try {\n const block = await this.currentBlock()\n const hash = block[0]._hash\n if (this._headPollHash === undefined) {\n this._headPollHash = hash\n return\n }\n if (hash !== this._headPollHash) {\n this._headPollHash = hash\n if (emitOnChange) {\n await this.emit('headUpdated', { block })\n }\n }\n } catch (ex) {\n this.logger?.error('Error polling block head', ex)\n } finally {\n this._headPollInProgress = false\n }\n }\n\n private startHeadPolling() {\n if (this.headPollIntervalMs === undefined) return\n this.stopHeadPolling()\n this._headPollTimer = setInterval(() => {\n void this.pollHead(true)\n }, this.headPollIntervalMs)\n }\n\n private stopHeadPolling() {\n if (this._headPollTimer) {\n clearInterval(this._headPollTimer)\n this._headPollTimer = null\n }\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { Hash } from '@xylabs/sdk-js'\n\n/**\n * Path contract for the static REST chain layout.\n *\n * These builders are the single source of truth for where files live, shared by the\n * publisher (which writes the static files) and `RestBlockViewer` (which reads them),\n * so the layout cannot drift between the two. Paths are relative \u2014 no leading slash \u2014\n * and joined to a base URL by the consumer.\n *\n * Every file except the head pointer is immutable once written:\n * - block and payload files never change after finalization\n * - step files are only written for completed steps\n * - the head pointer is rewritten as the chain advances and must not be cached long-term\n *\n * The finalized (immutable) files and the mutable chain state (the head pointer) may live\n * in separate buckets behind separate domains: the publisher writes only the finalized\n * files, the chain's finalizer writes the head pointer, and the viewer joins `headPath` to\n * its chain-state base URL and everything else to its finalized base URL.\n */\n\n/** The mutable head pointer: the current head SignedHydratedBlockWithHashMeta. */\nexport const headPath = (): string => 'chain/head.json'\n\n/** A block by number: SignedHydratedBlockWithHashMeta. */\nexport const blockNumberPath = (block: number): string => `block/number/${block}.json`\n\n/** A block by hash: the same content as its by-number twin. */\nexport const blockHashPath = (hash: Hash): string => `block/hash/${hash}.json`\n\n/**\n * A completed step's blocks: a BlocksStepSummary payload whose `blocks` array is ordered\n * oldest-first. Only complete steps are published; the in-flight tail step has no file.\n */\nexport const blocksStepPath = (stepLevel: number, stepIndex: number): string => `blocks/step/${stepLevel}/${stepIndex}.json`\n\n/** A payload by hash: WithHashMeta<Payload>. */\nexport const payloadPath = (hash: Hash): string => `payload/${hash}.json`\n", "import type { Hash } from '@xylabs/sdk-js'\nimport { assertEx, exists } from '@xylabs/sdk-js'\nimport type { Payload, WithHashMeta } from '@xyo-network/sdk-js'\nimport { PayloadZodLoose, WithHashMetaZod } from '@xyo-network/sdk-js'\nimport { LruCacheMap } from '@xyo-network/xl1-driver-memory'\nimport type {\n BlockRate, BlockViewer, ChainId,\n SignedHydratedBlockWithHashMeta, SingleTimeConfig, TimeDurations, XL1BlockNumber, XL1BlockRange,\n} from '@xyo-network/xl1-protocol-lib'\nimport {\n asSignedHydratedBlockWithHashMeta, asXL1BlockNumber, BlockViewerMoniker, stepSize,\n} from '@xyo-network/xl1-protocol-lib'\nimport type { CreatableProviderParams } from '@xyo-network/xl1-protocol-sdk'\nimport {\n AbstractCreatableProvider, asBlocksStepSummary, blocksMaxStep, calculateBlockRate, calculateStepSizeRate, calculateTimeRate, creatableProvider,\n} from '@xyo-network/xl1-protocol-sdk'\n\nimport {\n blockHashPath, blockNumberPath, blocksStepPath, headPath, payloadPath,\n} from './paths.ts'\n\n/** Parameters for RestBlockViewer. */\nexport interface RestBlockViewerParams extends CreatableProviderParams {\n /**\n * Base URL serving the mutable chain state (the head pointer, `chain/head.json`) when it\n * lives in a separate bucket/domain from the finalized files. Defaults to `finalizedBaseUrl`.\n */\n chainStateBaseUrl?: string\n /** Optional fetch override (custom agents, auth headers, tests). Defaults to the global fetch. */\n fetchFn?: typeof globalThis.fetch\n /** Base URL of the finalized (immutable) static chain files (e.g. a public R2 bucket or CDN domain). */\n finalizedBaseUrl: string\n headPollIntervalMs?: number\n}\n\nconst MIN_HEAD_POLL_INTERVAL_MS = 5000\n\nconst PayloadFileZod = WithHashMetaZod(PayloadZodLoose)\n\n/**\n * A BlockViewer over a statically generated REST file layout (see `paths.ts`).\n *\n * Reads are anonymous HTTP GETs against immutable files, so everything except the head\n * pointer is cached aggressively. The head pointer is read from `chainStateBaseUrl` when\n * the chain state and finalized files live in separate buckets/domains. `blocksByStep`\n * serves only completed steps \u2014 the in-flight tail step has no static file and returns [].\n */\n@creatableProvider()\nexport class RestBlockViewer extends AbstractCreatableProvider<RestBlockViewerParams, BlockViewer['eventData']> implements BlockViewer {\n static readonly defaultMoniker = BlockViewerMoniker\n static readonly dependencies = []\n static readonly monikers = [BlockViewerMoniker]\n moniker = RestBlockViewer.defaultMoniker\n\n protected blockByHashCache = new LruCacheMap<Hash, SignedHydratedBlockWithHashMeta>({ max: 2000 })\n protected blockByNumberCache = new LruCacheMap<XL1BlockNumber, SignedHydratedBlockWithHashMeta>({ max: 2000 })\n protected payloadCache = new LruCacheMap<Hash, WithHashMeta<Payload>>({ max: 10_000 })\n protected stepCache = new LruCacheMap<string, SignedHydratedBlockWithHashMeta[]>({ max: 4 })\n\n private _headPollHash?: Hash\n private _headPollInProgress = false\n private _headPollTimer: ReturnType<typeof setInterval> | null = null\n\n get chainStateBaseUrl(): string {\n return this.params.chainStateBaseUrl ?? this.params.finalizedBaseUrl\n }\n\n get finalizedBaseUrl(): string {\n return this.params.finalizedBaseUrl\n }\n\n protected get fetchFn(): typeof globalThis.fetch {\n return this.params.fetchFn ?? globalThis.fetch.bind(globalThis)\n }\n\n protected get headPollIntervalMs() {\n return this.params.headPollIntervalMs\n }\n\n static override async paramsHandler(params: Partial<RestBlockViewerParams>) {\n const headPollIntervalMs = params.headPollIntervalMs\n if (headPollIntervalMs !== undefined) {\n assertEx(headPollIntervalMs >= MIN_HEAD_POLL_INTERVAL_MS, () => `headPollIntervalMs must be at least ${MIN_HEAD_POLL_INTERVAL_MS}ms`)\n }\n return {\n ...await super.paramsHandler(params),\n chainStateBaseUrl: params.chainStateBaseUrl?.replace(/\\/+$/, ''),\n fetchFn: params.fetchFn,\n finalizedBaseUrl: assertEx(params.finalizedBaseUrl, () => 'finalizedBaseUrl is required').replace(/\\/+$/, ''),\n headPollIntervalMs,\n } satisfies RestBlockViewerParams\n }\n\n async blockByHash(hash: Hash): Promise<SignedHydratedBlockWithHashMeta | null> {\n return await this.spanAsync('blockByHash', async () => {\n const cached = this.blockByHashCache.get(hash)\n if (cached) return cached\n const parsed = await this.fetchJson(blockHashPath(hash))\n return parsed === undefined ? null : this.cacheBlock(parsed)\n }, { ...this.context, timeBudgetLimit: 100 })\n }\n\n async blockByNumber(blockNumber: XL1BlockNumber): Promise<SignedHydratedBlockWithHashMeta | null> {\n return await this.spanAsync('blockByNumber', async () => {\n const cached = this.blockByNumberCache.get(blockNumber)\n if (cached) return cached\n const parsed = await this.fetchJson(blockNumberPath(blockNumber))\n return parsed === undefined ? null : this.cacheBlock(parsed)\n }, { ...this.context, timeBudgetLimit: 100 })\n }\n\n async blocksByHash(hash: Hash, limit = 50): Promise<SignedHydratedBlockWithHashMeta[]> {\n return await this.spanAsync('blocksByHash', async () => {\n assertEx(limit > 0, () => 'limit must be greater than 0')\n assertEx(limit <= 100, () => 'limit must be less than 100')\n const blocks: SignedHydratedBlockWithHashMeta[] = []\n let current = await this.blockByHash(hash)\n while (current && blocks.length < limit) {\n blocks.push(current)\n const previousHash = current[0].previous\n if (previousHash === null) break\n current = await this.blockByHash(previousHash)\n }\n return blocks\n }, { ...this.context, timeBudgetLimit: 300 })\n }\n\n async blocksByNumber(blockNumber: XL1BlockNumber, limit = 50): Promise<SignedHydratedBlockWithHashMeta[]> {\n return await this.spanAsync('blocksByNumber', async () => {\n assertEx(limit > 0, () => 'limit must be greater than 0')\n assertEx(limit <= 100, () => 'limit must be less than 100')\n const blocks: SignedHydratedBlockWithHashMeta[] = []\n let current = await this.blockByNumber(blockNumber)\n while (current && blocks.length < limit) {\n blocks.push(current)\n if (current[0].block === 0) break\n current = await this.blockByNumber(asXL1BlockNumber(current[0].block - 1, true))\n }\n return blocks\n }, this.context)\n }\n\n async blocksByStep(stepLevel: number, stepIndex: number): Promise<SignedHydratedBlockWithHashMeta[]> {\n return await this.spanAsync('blocksByStep', async () => {\n assertEx(Number.isInteger(stepIndex) && stepIndex >= 0, () => 'stepIndex must be a non-negative integer')\n stepSize(stepLevel) // validates the level against StepSizes\n assertEx(\n stepLevel <= blocksMaxStep,\n () => `blocksByStep does not support step levels above ${blocksMaxStep} (requested ${stepLevel})`,\n )\n const cacheKey = `${stepLevel}|${stepIndex}`\n const cached = this.stepCache.get(cacheKey)\n if (cached) return cached\n const parsed = await this.fetchJson(blocksStepPath(stepLevel, stepIndex))\n // Only completed steps are published; the in-flight tail step has no file\n if (parsed === undefined) return []\n const summary = asBlocksStepSummary(parsed, { required: true })\n // Step files store blocks oldest-first; the viewer interface returns newest-first\n const blocks = summary.blocks.map(b => asSignedHydratedBlockWithHashMeta(b, true)).toReversed()\n this.stepCache.set(cacheKey, blocks)\n return blocks\n }, this.context)\n }\n\n chainId(): Promise<ChainId>\n chainId(blockNumber: XL1BlockNumber): Promise<ChainId>\n chainId(blockNumber: 'latest'): Promise<ChainId>\n async chainId(blockNumber: XL1BlockNumber | 'latest' = 'latest'): Promise<ChainId> {\n return await this.spanAsync('chainId', async () => {\n return blockNumber === 'latest'\n ? (await this.currentBlock())[0].chain\n : assertEx(await this.blockByNumber(blockNumber), () => `Block not found [${blockNumber}]`)[0].chain\n }, this.context)\n }\n\n async currentBlock(): Promise<SignedHydratedBlockWithHashMeta> {\n return await this.spanAsync('currentBlock', async () => {\n const parsed = assertEx(await this.fetchJson(headPath(), this.chainStateBaseUrl), () => 'Head not found')\n return this.cacheBlock(parsed)\n }, this.context)\n }\n\n async currentBlockHash(): Promise<Hash> {\n return (await this.currentBlock())[0]._hash\n }\n\n async currentBlockNumber(): Promise<XL1BlockNumber> {\n return (await this.currentBlock())[0].block\n }\n\n async payloadByHash(hash: Hash): Promise<WithHashMeta<Payload> | null> {\n const [payload] = await this.payloadsByHash([hash])\n return payload ?? null\n }\n\n async payloadsByHash(hashes: Hash[]): Promise<WithHashMeta<Payload>[]> {\n return await this.spanAsync('payloadsByHash', async () => {\n const results = await Promise.all(hashes.map(async (hash) => {\n const cached = this.payloadCache.get(hash)\n if (cached) return cached\n const parsed = await this.fetchJson(payloadPath(hash))\n if (parsed === undefined) return\n const payload = PayloadFileZod.parse(parsed)\n this.payloadCache.set(hash, payload)\n return payload\n }))\n return results.filter(exists)\n }, this.context)\n }\n\n async rate(range: XL1BlockRange, timeUnit?: keyof TimeDurations): Promise<BlockRate> {\n return await calculateBlockRate(this, range, timeUnit)\n }\n\n async stepSizeRate(start: XL1BlockNumber, stepIndex: number, count = 1, timeUnit?: keyof TimeDurations): Promise<BlockRate> {\n return await calculateStepSizeRate(this, start, stepIndex, count, timeUnit)\n }\n\n async timeDurationRate(\n timeConfig: SingleTimeConfig,\n startBlockNumber?: XL1BlockNumber,\n timeUnit?: keyof TimeDurations,\n toleranceMs?: number,\n maxAttempts?: number,\n ): Promise<BlockRate> {\n return await calculateTimeRate(this, timeConfig, startBlockNumber, timeUnit, toleranceMs, maxAttempts)\n }\n\n protected override async startHandler(): Promise<void> {\n await super.startHandler()\n if (this.headPollIntervalMs === undefined) return\n await this.pollHead(false)\n this.startHeadPolling()\n }\n\n protected override async stopHandler(): Promise<void> {\n this.stopHeadPolling()\n this._headPollHash = undefined\n await super.stopHandler()\n }\n\n /** Validates a parsed block file and populates both block caches. */\n private cacheBlock(parsed: unknown): SignedHydratedBlockWithHashMeta {\n const block = asSignedHydratedBlockWithHashMeta(parsed, true)\n this.blockByHashCache.set(block[0]._hash, block)\n this.blockByNumberCache.set(block[0].block, block)\n return block\n }\n\n private async fetchJson(path: string, baseUrl = this.finalizedBaseUrl): Promise<unknown> {\n const response = await this.fetchFn(`${baseUrl}/${path}`)\n if (response.status === 404) return undefined\n assertEx(response.ok, () => `Request failed [${response.status}] for ${path}`)\n return await response.json()\n }\n\n private async pollHead(emitOnChange: boolean): Promise<void> {\n if (this._headPollInProgress) return\n this._headPollInProgress = true\n try {\n const block = await this.currentBlock()\n const hash = block[0]._hash\n if (this._headPollHash === undefined) {\n this._headPollHash = hash\n return\n }\n if (hash !== this._headPollHash) {\n this._headPollHash = hash\n if (emitOnChange) {\n await this.emit('headUpdated', { block })\n }\n }\n } catch (ex) {\n this.logger?.error('Error polling block head', ex)\n } finally {\n this._headPollInProgress = false\n }\n }\n\n private startHeadPolling() {\n if (this.headPollIntervalMs === undefined) return\n this.stopHeadPolling()\n this._headPollTimer = setInterval(() => {\n void this.pollHead(true)\n }, this.headPollIntervalMs)\n }\n\n private stopHeadPolling() {\n if (this._headPollTimer) {\n clearInterval(this._headPollTimer)\n this._headPollTimer = null\n }\n }\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;AAsBO,IAAM,WAAW,MAAc;AAG/B,IAAM,kBAAkB,CAAC,UAA0B,gBAAgB,KAAK;AAGxE,IAAM,gBAAgB,CAAC,SAAuB,cAAc,IAAI;AAMhE,IAAM,iBAAiB,CAAC,WAAmB,cAA8B,eAAe,SAAS,IAAI,SAAS;AAG9G,IAAM,cAAc,CAAC,SAAuB,WAAW,IAAI;;;ACpClE,SAAS,UAAU,cAAc;AAEjC,SAAS,iBAAiB,uBAAuB;AACjD,SAAS,mBAAmB;AAK5B;AAAA,EACE;AAAA,EAAmC;AAAA,EAAkB;AAAA,EAAoB;AAAA,OACpE;AAEP;AAAA,EACE;AAAA,EAA2B;AAAA,EAAqB;AAAA,EAAe;AAAA,EAAoB;AAAA,EAAuB;AAAA,EAAmB;AAAA,OACxH;AAoBP,IAAM,4BAA4B;AAElC,IAAM,iBAAiB,gBAAgB,eAAe;AAW/C,IAAM,kBAAN,cAA8B,0BAAkG;AAAA,EAIrI,UAAU,gBAAgB;AAAA,EAEhB,mBAAmB,IAAI,YAAmD,EAAE,KAAK,IAAK,CAAC;AAAA,EACvF,qBAAqB,IAAI,YAA6D,EAAE,KAAK,IAAK,CAAC;AAAA,EACnG,eAAe,IAAI,YAAyC,EAAE,KAAK,IAAO,CAAC;AAAA,EAC3E,YAAY,IAAI,YAAuD,EAAE,KAAK,EAAE,CAAC;AAAA,EAEnF;AAAA,EACA,sBAAsB;AAAA,EACtB,iBAAwD;AAAA,EAEhE,IAAI,oBAA4B;AAC9B,WAAO,KAAK,OAAO,qBAAqB,KAAK,OAAO;AAAA,EACtD;AAAA,EAEA,IAAI,mBAA2B;AAC7B,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA,EAEA,IAAc,UAAmC;AAC/C,WAAO,KAAK,OAAO,WAAW,WAAW,MAAM,KAAK,UAAU;AAAA,EAChE;AAAA,EAEA,IAAc,qBAAqB;AACjC,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA,EAEA,aAAsB,cAAc,QAAwC;AAC1E,UAAM,qBAAqB,OAAO;AAClC,QAAI,uBAAuB,QAAW;AACpC,eAAS,sBAAsB,2BAA2B,MAAM,uCAAuC,yBAAyB,IAAI;AAAA,IACtI;AACA,WAAO;AAAA,MACL,GAAG,MAAM,MAAM,cAAc,MAAM;AAAA,MACnC,mBAAmB,OAAO,mBAAmB,QAAQ,QAAQ,EAAE;AAAA,MAC/D,SAAS,OAAO;AAAA,MAChB,kBAAkB,SAAS,OAAO,kBAAkB,MAAM,8BAA8B,EAAE,QAAQ,QAAQ,EAAE;AAAA,MAC5G;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,YAAY,MAA6D;AAC7E,WAAO,MAAM,KAAK,UAAU,eAAe,YAAY;AACrD,YAAM,SAAS,KAAK,iBAAiB,IAAI,IAAI;AAC7C,UAAI,OAAQ,QAAO;AACnB,YAAM,SAAS,MAAM,KAAK,UAAU,cAAc,IAAI,CAAC;AACvD,aAAO,WAAW,SAAY,OAAO,KAAK,WAAW,MAAM;AAAA,IAC7D,GAAG,EAAE,GAAG,KAAK,SAAS,iBAAiB,IAAI,CAAC;AAAA,EAC9C;AAAA,EAEA,MAAM,cAAc,aAA8E;AAChG,WAAO,MAAM,KAAK,UAAU,iBAAiB,YAAY;AACvD,YAAM,SAAS,KAAK,mBAAmB,IAAI,WAAW;AACtD,UAAI,OAAQ,QAAO;AACnB,YAAM,SAAS,MAAM,KAAK,UAAU,gBAAgB,WAAW,CAAC;AAChE,aAAO,WAAW,SAAY,OAAO,KAAK,WAAW,MAAM;AAAA,IAC7D,GAAG,EAAE,GAAG,KAAK,SAAS,iBAAiB,IAAI,CAAC;AAAA,EAC9C;AAAA,EAEA,MAAM,aAAa,MAAY,QAAQ,IAAgD;AACrF,WAAO,MAAM,KAAK,UAAU,gBAAgB,YAAY;AACtD,eAAS,QAAQ,GAAG,MAAM,8BAA8B;AACxD,eAAS,SAAS,KAAK,MAAM,6BAA6B;AAC1D,YAAM,SAA4C,CAAC;AACnD,UAAI,UAAU,MAAM,KAAK,YAAY,IAAI;AACzC,aAAO,WAAW,OAAO,SAAS,OAAO;AACvC,eAAO,KAAK,OAAO;AACnB,cAAM,eAAe,QAAQ,CAAC,EAAE;AAChC,YAAI,iBAAiB,KAAM;AAC3B,kBAAU,MAAM,KAAK,YAAY,YAAY;AAAA,MAC/C;AACA,aAAO;AAAA,IACT,GAAG,EAAE,GAAG,KAAK,SAAS,iBAAiB,IAAI,CAAC;AAAA,EAC9C;AAAA,EAEA,MAAM,eAAe,aAA6B,QAAQ,IAAgD;AACxG,WAAO,MAAM,KAAK,UAAU,kBAAkB,YAAY;AACxD,eAAS,QAAQ,GAAG,MAAM,8BAA8B;AACxD,eAAS,SAAS,KAAK,MAAM,6BAA6B;AAC1D,YAAM,SAA4C,CAAC;AACnD,UAAI,UAAU,MAAM,KAAK,cAAc,WAAW;AAClD,aAAO,WAAW,OAAO,SAAS,OAAO;AACvC,eAAO,KAAK,OAAO;AACnB,YAAI,QAAQ,CAAC,EAAE,UAAU,EAAG;AAC5B,kBAAU,MAAM,KAAK,cAAc,iBAAiB,QAAQ,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;AAAA,MACjF;AACA,aAAO;AAAA,IACT,GAAG,KAAK,OAAO;AAAA,EACjB;AAAA,EAEA,MAAM,aAAa,WAAmB,WAA+D;AACnG,WAAO,MAAM,KAAK,UAAU,gBAAgB,YAAY;AACtD,eAAS,OAAO,UAAU,SAAS,KAAK,aAAa,GAAG,MAAM,0CAA0C;AACxG,eAAS,SAAS;AAClB;AAAA,QACE,aAAa;AAAA,QACb,MAAM,mDAAmD,aAAa,eAAe,SAAS;AAAA,MAChG;AACA,YAAM,WAAW,GAAG,SAAS,IAAI,SAAS;AAC1C,YAAM,SAAS,KAAK,UAAU,IAAI,QAAQ;AAC1C,UAAI,OAAQ,QAAO;AACnB,YAAM,SAAS,MAAM,KAAK,UAAU,eAAe,WAAW,SAAS,CAAC;AAExE,UAAI,WAAW,OAAW,QAAO,CAAC;AAClC,YAAM,UAAU,oBAAoB,QAAQ,EAAE,UAAU,KAAK,CAAC;AAE9D,YAAM,SAAS,QAAQ,OAAO,IAAI,OAAK,kCAAkC,GAAG,IAAI,CAAC,EAAE,WAAW;AAC9F,WAAK,UAAU,IAAI,UAAU,MAAM;AACnC,aAAO;AAAA,IACT,GAAG,KAAK,OAAO;AAAA,EACjB;AAAA,EAKA,MAAM,QAAQ,cAAyC,UAA4B;AACjF,WAAO,MAAM,KAAK,UAAU,WAAW,YAAY;AACjD,aAAO,gBAAgB,YAClB,MAAM,KAAK,aAAa,GAAG,CAAC,EAAE,QAC/B,SAAS,MAAM,KAAK,cAAc,WAAW,GAAG,MAAM,oBAAoB,WAAW,GAAG,EAAE,CAAC,EAAE;AAAA,IACnG,GAAG,KAAK,OAAO;AAAA,EACjB;AAAA,EAEA,MAAM,eAAyD;AAC7D,WAAO,MAAM,KAAK,UAAU,gBAAgB,YAAY;AACtD,YAAM,SAAS,SAAS,MAAM,KAAK,UAAU,SAAS,GAAG,KAAK,iBAAiB,GAAG,MAAM,gBAAgB;AACxG,aAAO,KAAK,WAAW,MAAM;AAAA,IAC/B,GAAG,KAAK,OAAO;AAAA,EACjB;AAAA,EAEA,MAAM,mBAAkC;AACtC,YAAQ,MAAM,KAAK,aAAa,GAAG,CAAC,EAAE;AAAA,EACxC;AAAA,EAEA,MAAM,qBAA8C;AAClD,YAAQ,MAAM,KAAK,aAAa,GAAG,CAAC,EAAE;AAAA,EACxC;AAAA,EAEA,MAAM,cAAc,MAAmD;AACrE,UAAM,CAAC,OAAO,IAAI,MAAM,KAAK,eAAe,CAAC,IAAI,CAAC;AAClD,WAAO,WAAW;AAAA,EACpB;AAAA,EAEA,MAAM,eAAe,QAAkD;AACrE,WAAO,MAAM,KAAK,UAAU,kBAAkB,YAAY;AACxD,YAAM,UAAU,MAAM,QAAQ,IAAI,OAAO,IAAI,OAAO,SAAS;AAC3D,cAAM,SAAS,KAAK,aAAa,IAAI,IAAI;AACzC,YAAI,OAAQ,QAAO;AACnB,cAAM,SAAS,MAAM,KAAK,UAAU,YAAY,IAAI,CAAC;AACrD,YAAI,WAAW,OAAW;AAC1B,cAAM,UAAU,eAAe,MAAM,MAAM;AAC3C,aAAK,aAAa,IAAI,MAAM,OAAO;AACnC,eAAO;AAAA,MACT,CAAC,CAAC;AACF,aAAO,QAAQ,OAAO,MAAM;AAAA,IAC9B,GAAG,KAAK,OAAO;AAAA,EACjB;AAAA,EAEA,MAAM,KAAK,OAAsB,UAAoD;AACnF,WAAO,MAAM,mBAAmB,MAAM,OAAO,QAAQ;AAAA,EACvD;AAAA,EAEA,MAAM,aAAa,OAAuB,WAAmB,QAAQ,GAAG,UAAoD;AAC1H,WAAO,MAAM,sBAAsB,MAAM,OAAO,WAAW,OAAO,QAAQ;AAAA,EAC5E;AAAA,EAEA,MAAM,iBACJ,YACA,kBACA,UACA,aACA,aACoB;AACpB,WAAO,MAAM,kBAAkB,MAAM,YAAY,kBAAkB,UAAU,aAAa,WAAW;AAAA,EACvG;AAAA,EAEA,MAAyB,eAA8B;AACrD,UAAM,MAAM,aAAa;AACzB,QAAI,KAAK,uBAAuB,OAAW;AAC3C,UAAM,KAAK,SAAS,KAAK;AACzB,SAAK,iBAAiB;AAAA,EACxB;AAAA,EAEA,MAAyB,cAA6B;AACpD,SAAK,gBAAgB;AACrB,SAAK,gBAAgB;AACrB,UAAM,MAAM,YAAY;AAAA,EAC1B;AAAA;AAAA,EAGQ,WAAW,QAAkD;AACnE,UAAM,QAAQ,kCAAkC,QAAQ,IAAI;AAC5D,SAAK,iBAAiB,IAAI,MAAM,CAAC,EAAE,OAAO,KAAK;AAC/C,SAAK,mBAAmB,IAAI,MAAM,CAAC,EAAE,OAAO,KAAK;AACjD,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,UAAU,MAAc,UAAU,KAAK,kBAAoC;AACvF,UAAM,WAAW,MAAM,KAAK,QAAQ,GAAG,OAAO,IAAI,IAAI,EAAE;AACxD,QAAI,SAAS,WAAW,IAAK,QAAO;AACpC,aAAS,SAAS,IAAI,MAAM,mBAAmB,SAAS,MAAM,SAAS,IAAI,EAAE;AAC7E,WAAO,MAAM,SAAS,KAAK;AAAA,EAC7B;AAAA,EAEA,MAAc,SAAS,cAAsC;AAC3D,QAAI,KAAK,oBAAqB;AAC9B,SAAK,sBAAsB;AAC3B,QAAI;AACF,YAAM,QAAQ,MAAM,KAAK,aAAa;AACtC,YAAM,OAAO,MAAM,CAAC,EAAE;AACtB,UAAI,KAAK,kBAAkB,QAAW;AACpC,aAAK,gBAAgB;AACrB;AAAA,MACF;AACA,UAAI,SAAS,KAAK,eAAe;AAC/B,aAAK,gBAAgB;AACrB,YAAI,cAAc;AAChB,gBAAM,KAAK,KAAK,eAAe,EAAE,MAAM,CAAC;AAAA,QAC1C;AAAA,MACF;AAAA,IACF,SAAS,IAAI;AACX,WAAK,QAAQ,MAAM,4BAA4B,EAAE;AAAA,IACnD,UAAE;AACA,WAAK,sBAAsB;AAAA,IAC7B;AAAA,EACF;AAAA,EAEQ,mBAAmB;AACzB,QAAI,KAAK,uBAAuB,OAAW;AAC3C,SAAK,gBAAgB;AACrB,SAAK,iBAAiB,YAAY,MAAM;AACtC,WAAK,KAAK,SAAS,IAAI;AAAA,IACzB,GAAG,KAAK,kBAAkB;AAAA,EAC5B;AAAA,EAEQ,kBAAkB;AACxB,QAAI,KAAK,gBAAgB;AACvB,oBAAc,KAAK,cAAc;AACjC,WAAK,iBAAiB;AAAA,IACxB;AAAA,EACF;AACF;AApPE,cADW,iBACK,kBAAiB;AACjC,cAFW,iBAEK,gBAAe,CAAC;AAChC,cAHW,iBAGK,YAAW,CAAC,kBAAkB;AAHnC,kBAAN;AAAA,EADN,kBAAkB;AAAA,GACN;",
6
6
  "names": []
7
7
  }
@@ -11,6 +11,11 @@ import type { Hash } from '@xylabs/sdk-js';
11
11
  * - block and payload files never change after finalization
12
12
  * - step files are only written for completed steps
13
13
  * - the head pointer is rewritten as the chain advances and must not be cached long-term
14
+ *
15
+ * The finalized (immutable) files and the mutable chain state (the head pointer) may live
16
+ * in separate buckets behind separate domains: the publisher writes only the finalized
17
+ * files, the chain's finalizer writes the head pointer, and the viewer joins `headPath` to
18
+ * its chain-state base URL and everything else to its finalized base URL.
14
19
  */
15
20
  /** The mutable head pointer: the current head SignedHydratedBlockWithHashMeta. */
16
21
  export declare const headPath: () => string;
@@ -1 +1 @@
1
- {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/paths.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAA;AAE1C;;;;;;;;;;;;GAYG;AAEH,kFAAkF;AAClF,eAAO,MAAM,QAAQ,QAAO,MAA2B,CAAA;AAEvD,0DAA0D;AAC1D,eAAO,MAAM,eAAe,GAAI,OAAO,MAAM,KAAG,MAAsC,CAAA;AAEtF,+DAA+D;AAC/D,eAAO,MAAM,aAAa,GAAI,MAAM,IAAI,KAAG,MAAmC,CAAA;AAE9E;;;GAGG;AACH,eAAO,MAAM,cAAc,GAAI,WAAW,MAAM,EAAE,WAAW,MAAM,KAAG,MAAsD,CAAA;AAE5H,gDAAgD;AAChD,eAAO,MAAM,WAAW,GAAI,MAAM,IAAI,KAAG,MAAgC,CAAA"}
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/paths.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAA;AAE1C;;;;;;;;;;;;;;;;;GAiBG;AAEH,kFAAkF;AAClF,eAAO,MAAM,QAAQ,QAAO,MAA2B,CAAA;AAEvD,0DAA0D;AAC1D,eAAO,MAAM,eAAe,GAAI,OAAO,MAAM,KAAG,MAAsC,CAAA;AAEtF,+DAA+D;AAC/D,eAAO,MAAM,aAAa,GAAI,MAAM,IAAI,KAAG,MAAmC,CAAA;AAE9E;;;GAGG;AACH,eAAO,MAAM,cAAc,GAAI,WAAW,MAAM,EAAE,WAAW,MAAM,KAAG,MAAsD,CAAA;AAE5H,gDAAgD;AAChD,eAAO,MAAM,WAAW,GAAI,MAAM,IAAI,KAAG,MAAgC,CAAA"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "http://json.schemastore.org/package.json",
3
3
  "name": "@xyo-network/xl1-rest-block-viewer",
4
- "version": "2.1.0",
4
+ "version": "2.1.1",
5
5
  "description": "XYO Layer One BlockViewer over a statically generated REST file layout",
6
6
  "homepage": "https://xylabs.com",
7
7
  "bugs": {
@@ -35,9 +35,9 @@
35
35
  "README.md"
36
36
  ],
37
37
  "dependencies": {
38
- "@xyo-network/xl1-protocol-sdk": "~2.1.0",
39
- "@xyo-network/xl1-protocol-lib": "~2.1.0",
40
- "@xyo-network/xl1-driver-memory": "~2.1.0"
38
+ "@xyo-network/xl1-driver-memory": "~2.1.1",
39
+ "@xyo-network/xl1-protocol-sdk": "~2.1.1",
40
+ "@xyo-network/xl1-protocol-lib": "~2.1.1"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@bitauth/libauth": "~3.0.0",