@filoz/repair-cli 0.4.0 → 1.0.0

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 (106) hide show
  1. package/dist/package.json +3 -3
  2. package/dist/src/api.d.ts +35 -0
  3. package/dist/src/api.d.ts.map +1 -0
  4. package/dist/src/api.js +118 -0
  5. package/dist/src/api.js.map +1 -0
  6. package/dist/src/commands/datasets.d.ts +3 -5
  7. package/dist/src/commands/datasets.d.ts.map +1 -1
  8. package/dist/src/commands/datasets.js +24 -33
  9. package/dist/src/commands/datasets.js.map +1 -1
  10. package/dist/src/commands/providers.d.ts +3 -5
  11. package/dist/src/commands/providers.d.ts.map +1 -1
  12. package/dist/src/commands/providers.js +32 -33
  13. package/dist/src/commands/providers.js.map +1 -1
  14. package/dist/src/commands/repair.d.ts +3 -5
  15. package/dist/src/commands/repair.d.ts.map +1 -1
  16. package/dist/src/commands/replicate.d.ts +3 -5
  17. package/dist/src/commands/replicate.d.ts.map +1 -1
  18. package/dist/src/commands/second-copy.d.ts +3 -5
  19. package/dist/src/commands/second-copy.d.ts.map +1 -1
  20. package/dist/src/commands/session-key.d.ts +3 -5
  21. package/dist/src/commands/session-key.d.ts.map +1 -1
  22. package/dist/src/commands/setup.d.ts.map +1 -1
  23. package/dist/src/commands/setup.js +11 -32
  24. package/dist/src/commands/setup.js.map +1 -1
  25. package/dist/src/commands/wallet.d.ts +3 -5
  26. package/dist/src/commands/wallet.d.ts.map +1 -1
  27. package/dist/src/db/dedupe-cids.d.ts +6 -7
  28. package/dist/src/db/dedupe-cids.d.ts.map +1 -1
  29. package/dist/src/db/dedupe-cids.js +2 -14
  30. package/dist/src/db/dedupe-cids.js.map +1 -1
  31. package/dist/src/db/find-repair-dataset.d.ts +3 -3
  32. package/dist/src/db/find-repair-dataset.d.ts.map +1 -1
  33. package/dist/src/db/find-repair-dataset.js +16 -14
  34. package/dist/src/db/find-repair-dataset.js.map +1 -1
  35. package/dist/src/db/get-active-data-set-ids.d.ts +9 -0
  36. package/dist/src/db/get-active-data-set-ids.d.ts.map +1 -0
  37. package/dist/src/db/get-active-data-set-ids.js +9 -0
  38. package/dist/src/db/get-active-data-set-ids.js.map +1 -0
  39. package/dist/src/db/get-alternate-provider-urls.d.ts +6 -0
  40. package/dist/src/db/get-alternate-provider-urls.d.ts.map +1 -0
  41. package/dist/src/db/get-alternate-provider-urls.js +24 -0
  42. package/dist/src/db/get-alternate-provider-urls.js.map +1 -0
  43. package/dist/src/db/get-pieces.d.ts +8 -27
  44. package/dist/src/db/get-pieces.d.ts.map +1 -1
  45. package/dist/src/db/get-pieces.js +44 -97
  46. package/dist/src/db/get-pieces.js.map +1 -1
  47. package/dist/src/db/get-repair-provider.d.ts +2 -2
  48. package/dist/src/db/get-repair-provider.d.ts.map +1 -1
  49. package/dist/src/db/get-repair-provider.js +8 -20
  50. package/dist/src/db/get-repair-provider.js.map +1 -1
  51. package/dist/src/db/repair-create.js +3 -3
  52. package/dist/src/db/repair-create.js.map +1 -1
  53. package/dist/src/db/replicate-create.d.ts.map +1 -1
  54. package/dist/src/db/replicate-create.js +13 -21
  55. package/dist/src/db/replicate-create.js.map +1 -1
  56. package/dist/src/db/second-copy-create.js +2 -2
  57. package/dist/src/db/second-copy-create.js.map +1 -1
  58. package/dist/src/local-schema.d.ts +1 -1
  59. package/dist/src/middleware.d.ts +7 -11
  60. package/dist/src/middleware.d.ts.map +1 -1
  61. package/dist/src/middleware.js +6 -10
  62. package/dist/src/middleware.js.map +1 -1
  63. package/dist/src/pipeline/add-pieces.d.ts +3 -3
  64. package/dist/src/pipeline/add-pieces.d.ts.map +1 -1
  65. package/dist/src/pipeline/add-pieces.js +15 -7
  66. package/dist/src/pipeline/add-pieces.js.map +1 -1
  67. package/dist/src/pipeline/create-datasets.d.ts +4 -4
  68. package/dist/src/pipeline/create-datasets.d.ts.map +1 -1
  69. package/dist/src/pipeline/create-datasets.js +7 -14
  70. package/dist/src/pipeline/create-datasets.js.map +1 -1
  71. package/dist/src/types.d.ts +6 -7
  72. package/dist/src/types.d.ts.map +1 -1
  73. package/dist/src/utils.d.ts +7 -10
  74. package/dist/src/utils.d.ts.map +1 -1
  75. package/dist/src/utils.js +1 -2
  76. package/dist/src/utils.js.map +1 -1
  77. package/package.json +3 -3
  78. package/readme.md +16 -13
  79. package/src/api.ts +220 -0
  80. package/src/commands/datasets.ts +24 -36
  81. package/src/commands/providers.ts +34 -42
  82. package/src/commands/setup.ts +12 -35
  83. package/src/db/dedupe-cids.ts +7 -24
  84. package/src/db/find-repair-dataset.ts +17 -28
  85. package/src/db/get-active-data-set-ids.ts +29 -0
  86. package/src/db/get-alternate-provider-urls.ts +43 -0
  87. package/src/db/get-pieces.ts +82 -214
  88. package/src/db/get-repair-provider.ts +10 -30
  89. package/src/db/repair-create.ts +3 -3
  90. package/src/db/replicate-create.ts +13 -21
  91. package/src/db/second-copy-create.ts +2 -2
  92. package/src/middleware.ts +7 -11
  93. package/src/pipeline/add-pieces.ts +20 -10
  94. package/src/pipeline/create-datasets.ts +9 -29
  95. package/src/types.ts +9 -7
  96. package/src/utils.ts +2 -4
  97. package/dist/src/db/find-providers-by-cid.d.ts +0 -9
  98. package/dist/src/db/find-providers-by-cid.d.ts.map +0 -1
  99. package/dist/src/db/find-providers-by-cid.js +0 -43
  100. package/dist/src/db/find-providers-by-cid.js.map +0 -1
  101. package/dist/src/indexer-schema.d.ts +0 -549
  102. package/dist/src/indexer-schema.d.ts.map +0 -1
  103. package/dist/src/indexer-schema.js +0 -56
  104. package/dist/src/indexer-schema.js.map +0 -1
  105. package/src/db/find-providers-by-cid.ts +0 -70
  106. package/src/indexer-schema.ts +0 -77
@@ -3,17 +3,18 @@ import * as Piece from '@filoz/synapse-core/piece'
3
3
  import * as SP from '@filoz/synapse-core/sp'
4
4
  import { and, asc, eq, gt, inArray } from 'drizzle-orm'
5
5
  import PQueue from 'p-queue'
6
+ import { listDataSetPieces } from '../api.ts'
6
7
  import { dedupeCids } from '../db/dedupe-cids.ts'
7
8
  import { getTargetDataset } from '../db/get-target-dataset.ts'
8
9
  import { repairUpdate } from '../db/repair-update.ts'
9
10
  import { upsertOperations } from '../db/upsert-operations.ts'
10
11
  import type { OperationSelect, RepairSelect } from '../local-schema.ts'
11
- import type { IndexerDatabase, LocalDatabase, WalletClient } from '../types.ts'
12
+ import type { IndexerApi, LocalDatabase, WalletClient } from '../types.ts'
12
13
  import { completeConfirmedOperations, excludeOperationsByCid, hashLink, operationsToPullPieces } from '../utils.ts'
13
14
 
14
15
  export type RunPullPiecesPhaseOptions = {
15
16
  localDb: LocalDatabase
16
- indexerDb: IndexerDatabase
17
+ api: IndexerApi
17
18
  repair: RepairSelect
18
19
  concurrency: number
19
20
  batchSize: number
@@ -22,7 +23,8 @@ export type RunPullPiecesPhaseOptions = {
22
23
 
23
24
  type CreateAddPiecesWorkerOptions = {
24
25
  localDb: LocalDatabase
25
- indexerDb: IndexerDatabase
26
+ /** CIDs already in the target dataset; repairs only. Updated as batches are added. */
27
+ targetCids?: Set<string>
26
28
  repair: RepairSelect
27
29
  client: WalletClient
28
30
  state: {
@@ -42,12 +44,11 @@ type AddPiecesJobOptions = {
42
44
  /**
43
45
  * Create a worker function that adds pieces to the target repair dataset.
44
46
  */
45
- function createAddPiecesWorker({ localDb, indexerDb, repair, client, state, log }: CreateAddPiecesWorkerOptions) {
47
+ function createAddPiecesWorker({ localDb, targetCids, repair, client, state, log }: CreateAddPiecesWorkerOptions) {
46
48
  return async (options: AddPiecesJobOptions) => {
47
49
  let completedOps = 0
48
50
  let failedOps = 0
49
51
  let operations: OperationSelect[] = options.operations
50
- const isRepair = repair.repairDataSetId == null
51
52
 
52
53
  const group = log.group(`Batch ${options.batchNumber}/${state.totalBatches}`)
53
54
 
@@ -55,8 +56,8 @@ function createAddPiecesWorker({ localDb, indexerDb, repair, client, state, log
55
56
  const dataset = await getTargetDataset({ localDb, repairId: repair.id, client })
56
57
 
57
58
  // dedupe operations by CID on the target dataset for repairs jobs
58
- if (isRepair) {
59
- operations = await dedupeCids({ indexerDb, localDb, dataSetId: dataset.dataSetId, operations })
59
+ if (targetCids) {
60
+ operations = await dedupeCids({ localDb, existingCids: targetCids, operations })
60
61
  }
61
62
  // A previous run may have submitted a transaction and crashed before marking rows completed.
62
63
  const operationsBeforeConfirmationCheck = operations.length
@@ -111,7 +112,7 @@ function createAddPiecesWorker({ localDb, indexerDb, repair, client, state, log
111
112
  clientDataSetId: dataset.clientDataSetId,
112
113
  pieces: operations.map((operation) => ({
113
114
  pieceCid: Piece.from(operation.cid),
114
- metadata: isRepair ? undefined : operation.metadata,
115
+ metadata: targetCids ? undefined : operation.metadata,
115
116
  })),
116
117
  })
117
118
  await upsertOperations({
@@ -126,6 +127,7 @@ function createAddPiecesWorker({ localDb, indexerDb, repair, client, state, log
126
127
  const waitForAddPieces = await SP.waitForAddPieces(addPiecesResult)
127
128
  state.completedOperations += operations.length
128
129
  completedOps += operations.length
130
+ for (const operation of operations) targetCids?.add(operation.cid)
129
131
  await upsertOperations({
130
132
  localDb,
131
133
  operations: operations.map((operation) => ({
@@ -158,7 +160,7 @@ function createAddPiecesWorker({ localDb, indexerDb, repair, client, state, log
158
160
  */
159
161
  export async function runAddPieces({
160
162
  localDb,
161
- indexerDb,
163
+ api,
162
164
  repair,
163
165
  concurrency,
164
166
  batchSize,
@@ -188,6 +190,14 @@ export async function runAddPieces({
188
190
  limit: 1,
189
191
  })
190
192
 
193
+ // Repairs and second copies dedupe into one shared dataset; read its CIDs once instead of per batch.
194
+ let targetCids: Set<string> | undefined
195
+ if (repair.repairDataSetId == null && totalOperations > 0) {
196
+ const dataset = await getTargetDataset({ localDb, repairId: repair.id, client })
197
+ const pieces = await listDataSetPieces(api, dataset.dataSetId, { removed: false })
198
+ targetCids = new Set(pieces.flatMap((piece) => (piece.cid == null ? [] : [piece.cid])))
199
+ }
200
+
191
201
  async function getNextBatch(): Promise<OperationSelect[] | null> {
192
202
  const operations = await localDb.query.operations.findMany({
193
203
  where: and(
@@ -207,7 +217,7 @@ export async function runAddPieces({
207
217
  return operations
208
218
  }
209
219
 
210
- const addPiecesJob = createAddPiecesWorker({ localDb, indexerDb, repair, client, state, log })
220
+ const addPiecesJob = createAddPiecesWorker({ localDb, targetCids, repair, client, state, log })
211
221
  const queue = new PQueue({ concurrency })
212
222
 
213
223
  while (true) {
@@ -1,18 +1,18 @@
1
1
  import * as p from '@clack/prompts'
2
2
  import * as SP from '@filoz/synapse-core/sp'
3
3
  import { getPDPProvider } from '@filoz/synapse-core/sp-registry'
4
- import { eq } from 'drizzle-orm'
5
4
  import type { Address } from 'viem'
5
+ import { getDataSet, toStringMetadata } from '../api.ts'
6
6
  import { findRepairDataset } from '../db/find-repair-dataset.ts'
7
7
  import { repairUpdate } from '../db/repair-update.ts'
8
8
  import type { RepairSelect } from '../local-schema.ts'
9
- import type { IndexerDatabase, LocalDatabase, WalletClient } from '../types.ts'
9
+ import type { IndexerApi, LocalDatabase, WalletClient } from '../types.ts'
10
10
  import { hashLink } from '../utils.ts'
11
11
 
12
12
  export type EnsureRepairDatasetOptions = {
13
13
  source: string
14
14
  localDb: LocalDatabase
15
- indexerDb: IndexerDatabase
15
+ api: IndexerApi
16
16
  client: WalletClient
17
17
  repair: RepairSelect
18
18
  payer: Address
@@ -23,14 +23,7 @@ export type EnsureRepairDatasetOptions = {
23
23
  *
24
24
  * @param options - The options for ensuring the repair dataset.
25
25
  */
26
- export async function ensureRepairDataset({
27
- source,
28
- localDb,
29
- indexerDb,
30
- client,
31
- repair,
32
- payer,
33
- }: EnsureRepairDatasetOptions) {
26
+ export async function ensureRepairDataset({ source, localDb, api, client, repair, payer }: EnsureRepairDatasetOptions) {
34
27
  const log = p.taskLog({
35
28
  title: 'Ensuring repair dataset',
36
29
  })
@@ -43,7 +36,7 @@ export async function ensureRepairDataset({
43
36
  let datasetId: bigint | null = null
44
37
  // check if dataset already exists
45
38
  const existingDatasetId = await findRepairDataset({
46
- indexerDb,
39
+ api,
47
40
  providerId: repair.targetProviderId,
48
41
  payer,
49
42
  source,
@@ -83,13 +76,7 @@ export async function ensureRepairDataset({
83
76
  *
84
77
  * @param options - The options for ensuring the replication dataset.
85
78
  */
86
- export async function ensureReplicateDataset({
87
- localDb,
88
- indexerDb,
89
- client,
90
- repair,
91
- payer,
92
- }: EnsureRepairDatasetOptions) {
79
+ export async function ensureReplicateDataset({ localDb, api, client, repair, payer }: EnsureRepairDatasetOptions) {
93
80
  const log = p.taskLog({
94
81
  title: 'Ensuring replication dataset',
95
82
  })
@@ -109,14 +96,7 @@ export async function ensureReplicateDataset({
109
96
  return repair.targetDataSetId
110
97
  }
111
98
 
112
- const schema = indexerDb._.fullSchema
113
- const sourceDataSet = await indexerDb.query.dataSets.findFirst({
114
- where: eq(schema.dataSets.dataSetId, repair.repairDataSetId),
115
- columns: {
116
- metadata: true,
117
- withCdn: true,
118
- },
119
- })
99
+ const sourceDataSet = await getDataSet(api, repair.repairDataSetId)
120
100
 
121
101
  if (!sourceDataSet) {
122
102
  throw new Error(`Source dataset ${repair.repairDataSetId} not found`)
@@ -126,8 +106,8 @@ export async function ensureReplicateDataset({
126
106
  payee: provider.payee,
127
107
  serviceURL: provider.pdp.serviceURL,
128
108
  payer,
129
- cdn: sourceDataSet.withCdn,
130
- metadata: sourceDataSet.metadata ?? undefined,
109
+ cdn: sourceDataSet.withCdn ?? false,
110
+ metadata: sourceDataSet.metadata ? toStringMetadata(sourceDataSet.metadata) : undefined,
131
111
  })
132
112
  log.message(`Waiting for data to be created at ${provider.pdp.serviceURL} ${hashLink(txHash, client.chain)}...`)
133
113
  const waitForResult = await SP.waitForCreateDataSet({
package/src/types.ts CHANGED
@@ -1,25 +1,27 @@
1
+ import type { ApiClient } from '@filecoin-project/api-client'
1
2
  import type { Chain } from '@filoz/synapse-core/chains'
2
3
  import type { Client } from '@libsql/client'
3
4
  import type { LibSQLDatabase } from 'drizzle-orm/libsql'
4
- import type { NodePgDatabase } from 'drizzle-orm/node-postgres'
5
5
  import type { z } from 'incur'
6
- import type { Pool } from 'pg'
7
6
  import type { Account, Address, Hex, Transport, Client as ViemClient } from 'viem'
8
- import type * as indexerSchema from './indexer-schema.ts'
7
+ import type { Network } from './api.ts'
9
8
  import type * as localSchema from './local-schema.ts'
10
9
  import type { contextSchema } from './middleware.ts'
11
10
  export type LocalDatabase = LibSQLDatabase<typeof localSchema> & {
12
11
  $client: Client
13
12
  }
14
13
 
15
- export type IndexerDatabase = NodePgDatabase<typeof indexerSchema> & {
16
- $client: Pool
14
+ /**
15
+ * filecoin-api client bound to the configured network.
16
+ */
17
+ export type IndexerApi = {
18
+ client: ApiClient
19
+ network: Network
17
20
  }
18
21
 
19
22
  export interface Config {
20
23
  privateKey: Hex
21
- indexerMainnetUrl: string
22
- indexerCalibrationUrl: string
24
+ apiUrl: string
23
25
  chainId: number
24
26
  dbPath: string
25
27
  }
package/src/utils.ts CHANGED
@@ -3,7 +3,6 @@ import * as Piece from '@filoz/synapse-core/piece'
3
3
  import type * as SP from '@filoz/synapse-core/sp'
4
4
  import { getTableColumns, inArray, type SQL, sql } from 'drizzle-orm'
5
5
  import { drizzle } from 'drizzle-orm/libsql'
6
- import type { PgTable } from 'drizzle-orm/pg-core'
7
6
  import type { SQLiteTable } from 'drizzle-orm/sqlite-core'
8
7
  import { z } from 'incur'
9
8
  import { Conf } from 'iso-conf'
@@ -20,8 +19,7 @@ import type { LocalDatabase, WalletClient } from './types.ts'
20
19
 
21
20
  export const configSchema = z.object({
22
21
  privateKey: z.string().optional(),
23
- indexerMainnetUrl: z.url().optional(),
24
- indexerCalibrationUrl: z.url().optional(),
22
+ apiUrl: z.url().optional(),
25
23
  chainId: z.number().optional(),
26
24
  dbPath: z.string().optional(),
27
25
  source: z.string().optional(),
@@ -273,7 +271,7 @@ export async function findPieceOnProviders(providers: string[], pieceCid: string
273
271
  return result
274
272
  }
275
273
 
276
- export const buildConflictUpdateColumns = <T extends PgTable | SQLiteTable, Q extends keyof T['_']['columns']>(
274
+ export const buildConflictUpdateColumns = <T extends SQLiteTable, Q extends keyof T['_']['columns']>(
277
275
  table: T,
278
276
  columns?: Q[]
279
277
  ) => {
@@ -1,9 +0,0 @@
1
- import type { IndexerDatabase, RepairProvider } from '../types.ts';
2
- export type FindProvidersByCidOptions = {
3
- indexerDb: IndexerDatabase;
4
- cids: readonly string[];
5
- blockNumber: bigint;
6
- };
7
- export type ProvidersByCid = Record<string, RepairProvider[]>;
8
- export declare function findProvidersByCid({ indexerDb, cids, blockNumber, }: FindProvidersByCidOptions): Promise<ProvidersByCid>;
9
- //# sourceMappingURL=find-providers-by-cid.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"find-providers-by-cid.d.ts","sourceRoot":"","sources":["../../../src/db/find-providers-by-cid.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAElE,MAAM,MAAM,yBAAyB,GAAG;IACtC,SAAS,EAAE,eAAe,CAAA;IAC1B,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;IACvB,WAAW,EAAE,MAAM,CAAA;CACpB,CAAA;AAKD,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,EAAE,CAAC,CAAA;AAS7D,wBAAsB,kBAAkB,CAAC,EACvC,SAAS,EACT,IAAI,EACJ,WAAW,GACZ,EAAE,yBAAyB,GAAG,OAAO,CAAC,cAAc,CAAC,CA4CrD"}
@@ -1,43 +0,0 @@
1
- import { and, asc, eq, inArray, isNull, lte, or } from 'drizzle-orm';
2
- export async function findProvidersByCid({ indexerDb, cids, blockNumber, }) {
3
- const schema = indexerDb._.fullSchema;
4
- const providersByCid = Object.fromEntries(cids.map((cid) => [cid, []]));
5
- if (cids.length === 0)
6
- return providersByCid;
7
- const filters = [
8
- inArray(schema.pieces.cid, [...cids]),
9
- eq(schema.dataSets.deleted, false),
10
- or(isNull(schema.dataSets.pdpEndEpoch), lte(schema.dataSets.pdpEndEpoch, blockNumber)),
11
- eq(schema.pieces.removed, false),
12
- or(eq(schema.providers.approved, true), eq(schema.providers.endorsed, true)),
13
- ];
14
- const rows = await indexerDb
15
- .selectDistinct({
16
- cid: schema.pieces.cid,
17
- providerId: schema.providers.providerId,
18
- providerAddress: schema.providers.providerAddress,
19
- name: schema.providers.name,
20
- serviceUrl: schema.providers.serviceUrl,
21
- approved: schema.providers.approved,
22
- endorsed: schema.providers.endorsed,
23
- })
24
- .from(schema.pieces)
25
- .innerJoin(schema.dataSets, eq(schema.pieces.dataSetId, schema.dataSets.dataSetId))
26
- .innerJoin(schema.providers, eq(schema.dataSets.providerId, schema.providers.providerId))
27
- .where(and(...filters))
28
- .orderBy(asc(schema.pieces.cid), asc(schema.providers.providerId));
29
- for (const { cid, ...provider } of rows) {
30
- if (provider.providerAddress && provider.serviceUrl && provider.name) {
31
- providersByCid[cid]?.push({
32
- providerId: provider.providerId,
33
- providerAddress: provider.providerAddress,
34
- name: provider.name,
35
- serviceUrl: provider.serviceUrl,
36
- approved: provider.approved,
37
- endorsed: provider.endorsed,
38
- });
39
- }
40
- }
41
- return providersByCid;
42
- }
43
- //# sourceMappingURL=find-providers-by-cid.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"find-providers-by-cid.js","sourceRoot":"","sources":["../../../src/db/find-providers-by-cid.ts"],"names":[],"mappings":"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"}