@sage-protocol/sdk 0.1.6 → 0.1.8
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 +288 -0
- package/dist/browser/index.mjs +462 -5134
- package/dist/index.cjs +983 -3
- package/dist/index.mjs +983 -3
- package/dist/node/index.cjs +983 -3
- package/dist/node/index.mjs +983 -3
- package/package.json +15 -3
package/dist/node/index.mjs
CHANGED
|
@@ -20,7 +20,7 @@ var require_package = __commonJS({
|
|
|
20
20
|
"package.json"(exports2, module2) {
|
|
21
21
|
module2.exports = {
|
|
22
22
|
name: "@sage-protocol/sdk",
|
|
23
|
-
version: "0.1.
|
|
23
|
+
version: "0.1.7",
|
|
24
24
|
description: "Backend-agnostic SDK for interacting with the Sage Protocol (governance, SubDAOs, tokens).",
|
|
25
25
|
main: "dist/index.cjs",
|
|
26
26
|
module: "dist/index.mjs",
|
|
@@ -56,10 +56,10 @@ var require_package = __commonJS({
|
|
|
56
56
|
],
|
|
57
57
|
sideEffects: false,
|
|
58
58
|
browser: {
|
|
59
|
+
child_process: false,
|
|
59
60
|
fs: false,
|
|
60
|
-
path: false,
|
|
61
61
|
os: false,
|
|
62
|
-
|
|
62
|
+
path: false
|
|
63
63
|
},
|
|
64
64
|
repository: {
|
|
65
65
|
type: "git",
|
|
@@ -88,6 +88,18 @@ var require_package = __commonJS({
|
|
|
88
88
|
sinon: "^17.0.1",
|
|
89
89
|
tsup: "^8.1.0",
|
|
90
90
|
typescript: "^5.4.0"
|
|
91
|
+
},
|
|
92
|
+
peerDependencies: {
|
|
93
|
+
react: "^18.0.0 || ^19.0.0",
|
|
94
|
+
swr: "^2.0.0"
|
|
95
|
+
},
|
|
96
|
+
peerDependenciesMeta: {
|
|
97
|
+
react: {
|
|
98
|
+
optional: true
|
|
99
|
+
},
|
|
100
|
+
swr: {
|
|
101
|
+
optional: true
|
|
102
|
+
}
|
|
91
103
|
}
|
|
92
104
|
};
|
|
93
105
|
}
|
|
@@ -8093,6 +8105,952 @@ var require_doppler = __commonJS({
|
|
|
8093
8105
|
}
|
|
8094
8106
|
});
|
|
8095
8107
|
|
|
8108
|
+
// src/services/utils/cache.js
|
|
8109
|
+
var require_cache = __commonJS({
|
|
8110
|
+
"src/services/utils/cache.js"(exports2, module2) {
|
|
8111
|
+
var SimpleCache = class {
|
|
8112
|
+
constructor(options = {}) {
|
|
8113
|
+
this.enabled = options.enabled !== false;
|
|
8114
|
+
this.defaultTTL = options.ttl || 3e4;
|
|
8115
|
+
this.maxSize = options.maxSize || 100;
|
|
8116
|
+
this._store = /* @__PURE__ */ new Map();
|
|
8117
|
+
}
|
|
8118
|
+
/**
|
|
8119
|
+
* Get value from cache
|
|
8120
|
+
* @param {string} key - Cache key
|
|
8121
|
+
* @returns {any|null} - Cached value or null if expired/missing
|
|
8122
|
+
*/
|
|
8123
|
+
get(key) {
|
|
8124
|
+
if (!this.enabled) return null;
|
|
8125
|
+
const entry = this._store.get(key);
|
|
8126
|
+
if (!entry) return null;
|
|
8127
|
+
if (Date.now() > entry.expires) {
|
|
8128
|
+
this._store.delete(key);
|
|
8129
|
+
return null;
|
|
8130
|
+
}
|
|
8131
|
+
return entry.data;
|
|
8132
|
+
}
|
|
8133
|
+
/**
|
|
8134
|
+
* Set value in cache with TTL
|
|
8135
|
+
* @param {string} key - Cache key
|
|
8136
|
+
* @param {any} data - Data to cache
|
|
8137
|
+
* @param {number} ttl - Time to live in milliseconds (optional)
|
|
8138
|
+
*/
|
|
8139
|
+
set(key, data, ttl) {
|
|
8140
|
+
if (!this.enabled) return;
|
|
8141
|
+
if (this._store.size >= this.maxSize && !this._store.has(key)) {
|
|
8142
|
+
const firstKey = this._store.keys().next().value;
|
|
8143
|
+
this._store.delete(firstKey);
|
|
8144
|
+
}
|
|
8145
|
+
this._store.set(key, {
|
|
8146
|
+
data,
|
|
8147
|
+
expires: Date.now() + (ttl || this.defaultTTL)
|
|
8148
|
+
});
|
|
8149
|
+
}
|
|
8150
|
+
/**
|
|
8151
|
+
* Clear all cached entries
|
|
8152
|
+
*/
|
|
8153
|
+
clear() {
|
|
8154
|
+
this._store.clear();
|
|
8155
|
+
}
|
|
8156
|
+
/**
|
|
8157
|
+
* Delete specific key
|
|
8158
|
+
* @param {string} key - Cache key to delete
|
|
8159
|
+
*/
|
|
8160
|
+
delete(key) {
|
|
8161
|
+
this._store.delete(key);
|
|
8162
|
+
}
|
|
8163
|
+
/**
|
|
8164
|
+
* Get cache statistics
|
|
8165
|
+
* @returns {object} - Cache stats (size, enabled)
|
|
8166
|
+
*/
|
|
8167
|
+
stats() {
|
|
8168
|
+
return {
|
|
8169
|
+
size: this._store.size,
|
|
8170
|
+
maxSize: this.maxSize,
|
|
8171
|
+
enabled: this.enabled
|
|
8172
|
+
};
|
|
8173
|
+
}
|
|
8174
|
+
};
|
|
8175
|
+
module2.exports = { SimpleCache };
|
|
8176
|
+
}
|
|
8177
|
+
});
|
|
8178
|
+
|
|
8179
|
+
// src/services/utils/retry.js
|
|
8180
|
+
var require_retry = __commonJS({
|
|
8181
|
+
"src/services/utils/retry.js"(exports2, module2) {
|
|
8182
|
+
async function retryWithBackoff(fn, options = {}) {
|
|
8183
|
+
const {
|
|
8184
|
+
attempts = 3,
|
|
8185
|
+
baseDelay = 1e3,
|
|
8186
|
+
maxDelay = 1e4,
|
|
8187
|
+
onRetry = null
|
|
8188
|
+
} = options;
|
|
8189
|
+
let lastError;
|
|
8190
|
+
for (let i = 0; i < attempts; i++) {
|
|
8191
|
+
try {
|
|
8192
|
+
return await fn();
|
|
8193
|
+
} catch (error) {
|
|
8194
|
+
lastError = error;
|
|
8195
|
+
if (i === attempts - 1) {
|
|
8196
|
+
break;
|
|
8197
|
+
}
|
|
8198
|
+
const delay = Math.min(baseDelay * Math.pow(2, i), maxDelay);
|
|
8199
|
+
if (onRetry) {
|
|
8200
|
+
onRetry({
|
|
8201
|
+
attempt: i + 1,
|
|
8202
|
+
totalAttempts: attempts,
|
|
8203
|
+
delay,
|
|
8204
|
+
error
|
|
8205
|
+
});
|
|
8206
|
+
}
|
|
8207
|
+
await sleep(delay);
|
|
8208
|
+
}
|
|
8209
|
+
}
|
|
8210
|
+
throw lastError;
|
|
8211
|
+
}
|
|
8212
|
+
function sleep(ms) {
|
|
8213
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
8214
|
+
}
|
|
8215
|
+
module2.exports = { retryWithBackoff, sleep };
|
|
8216
|
+
}
|
|
8217
|
+
});
|
|
8218
|
+
|
|
8219
|
+
// src/errors/index.js
|
|
8220
|
+
var require_errors2 = __commonJS({
|
|
8221
|
+
"src/errors/index.js"(exports2, module2) {
|
|
8222
|
+
var SageSDKError = class extends Error {
|
|
8223
|
+
constructor(message, code, retryable = false, originalError = null) {
|
|
8224
|
+
super(message);
|
|
8225
|
+
this.name = this.constructor.name;
|
|
8226
|
+
this.code = code;
|
|
8227
|
+
this.retryable = retryable;
|
|
8228
|
+
this.originalError = originalError;
|
|
8229
|
+
}
|
|
8230
|
+
};
|
|
8231
|
+
var SubgraphError = class extends SageSDKError {
|
|
8232
|
+
/**
|
|
8233
|
+
* @param {string} message - Error message
|
|
8234
|
+
* @param {'TIMEOUT'|'NETWORK'|'INVALID_RESPONSE'|'NOT_FOUND'|'QUERY_FAILED'} code - Error code
|
|
8235
|
+
* @param {boolean} retryable - Whether error is retryable
|
|
8236
|
+
* @param {Error} originalError - Original error (optional)
|
|
8237
|
+
*/
|
|
8238
|
+
constructor(message, code, retryable = false, originalError = null) {
|
|
8239
|
+
super(message, code, retryable, originalError);
|
|
8240
|
+
}
|
|
8241
|
+
};
|
|
8242
|
+
var IPFSError = class extends SageSDKError {
|
|
8243
|
+
/**
|
|
8244
|
+
* @param {string} message - Error message
|
|
8245
|
+
* @param {'TIMEOUT'|'PIN_FAILED'|'INVALID_CID'|'NOT_FOUND'|'GATEWAY_FAILED'|'UPLOAD_FAILED'} code - Error code
|
|
8246
|
+
* @param {boolean} retryable - Whether error is retryable
|
|
8247
|
+
* @param {Error} originalError - Original error (optional)
|
|
8248
|
+
*/
|
|
8249
|
+
constructor(message, code, retryable = false, originalError = null) {
|
|
8250
|
+
super(message, code, retryable, originalError);
|
|
8251
|
+
}
|
|
8252
|
+
};
|
|
8253
|
+
function formatErrorMessage(error) {
|
|
8254
|
+
if (!error) return "An unknown error occurred";
|
|
8255
|
+
if (error instanceof SubgraphError || error instanceof IPFSError) {
|
|
8256
|
+
switch (error.code) {
|
|
8257
|
+
case "TIMEOUT":
|
|
8258
|
+
return "Request timed out. Please try again.";
|
|
8259
|
+
case "NETWORK":
|
|
8260
|
+
return "Network error. Check your connection and try again.";
|
|
8261
|
+
case "INVALID_RESPONSE":
|
|
8262
|
+
return "Received invalid data from server.";
|
|
8263
|
+
case "NOT_FOUND":
|
|
8264
|
+
return "Content not found.";
|
|
8265
|
+
case "PIN_FAILED":
|
|
8266
|
+
return "Failed to pin content to IPFS. Please try again.";
|
|
8267
|
+
case "INVALID_CID":
|
|
8268
|
+
return "Invalid content identifier (CID).";
|
|
8269
|
+
case "GATEWAY_FAILED":
|
|
8270
|
+
return "All IPFS gateways failed. Content may be temporarily unavailable.";
|
|
8271
|
+
case "UPLOAD_FAILED":
|
|
8272
|
+
return "Failed to upload content. Please try again.";
|
|
8273
|
+
case "QUERY_FAILED":
|
|
8274
|
+
return "Failed to query data. Please try again.";
|
|
8275
|
+
default:
|
|
8276
|
+
return error.message || "An error occurred";
|
|
8277
|
+
}
|
|
8278
|
+
}
|
|
8279
|
+
return error.message || "An error occurred";
|
|
8280
|
+
}
|
|
8281
|
+
function isRetryable(error) {
|
|
8282
|
+
if (error instanceof SageSDKError) {
|
|
8283
|
+
return error.retryable;
|
|
8284
|
+
}
|
|
8285
|
+
if (error.name === "AbortError" || error.name === "TimeoutError") {
|
|
8286
|
+
return true;
|
|
8287
|
+
}
|
|
8288
|
+
const message = String(error.message || "").toLowerCase();
|
|
8289
|
+
const retryablePatterns = [
|
|
8290
|
+
"timeout",
|
|
8291
|
+
"network",
|
|
8292
|
+
"econnrefused",
|
|
8293
|
+
"econnreset",
|
|
8294
|
+
"etimedout",
|
|
8295
|
+
"fetch failed"
|
|
8296
|
+
];
|
|
8297
|
+
return retryablePatterns.some((pattern) => message.includes(pattern));
|
|
8298
|
+
}
|
|
8299
|
+
module2.exports = {
|
|
8300
|
+
SageSDKError,
|
|
8301
|
+
SubgraphError,
|
|
8302
|
+
IPFSError,
|
|
8303
|
+
formatErrorMessage,
|
|
8304
|
+
isRetryable
|
|
8305
|
+
};
|
|
8306
|
+
}
|
|
8307
|
+
});
|
|
8308
|
+
|
|
8309
|
+
// src/services/subgraph/client.js
|
|
8310
|
+
var require_client = __commonJS({
|
|
8311
|
+
"src/services/subgraph/client.js"(exports2, module2) {
|
|
8312
|
+
var subgraph = require_subgraph();
|
|
8313
|
+
var { SimpleCache } = require_cache();
|
|
8314
|
+
var { retryWithBackoff } = require_retry();
|
|
8315
|
+
var { SubgraphError } = require_errors2();
|
|
8316
|
+
var SubgraphService = class {
|
|
8317
|
+
/**
|
|
8318
|
+
* @param {object} config - Service configuration
|
|
8319
|
+
* @param {string} config.url - Subgraph GraphQL endpoint
|
|
8320
|
+
* @param {number} [config.timeout=10000] - Request timeout in ms
|
|
8321
|
+
* @param {number} [config.retries=3] - Number of retry attempts
|
|
8322
|
+
* @param {object} [config.cache] - Cache configuration
|
|
8323
|
+
* @param {boolean} [config.cache.enabled=true] - Enable caching
|
|
8324
|
+
* @param {number} [config.cache.ttl=30000] - Cache TTL in ms
|
|
8325
|
+
* @param {number} [config.cache.maxSize=100] - Max cache entries
|
|
8326
|
+
*/
|
|
8327
|
+
constructor(config) {
|
|
8328
|
+
if (!config || !config.url) {
|
|
8329
|
+
throw new Error("SubgraphService requires a url in config");
|
|
8330
|
+
}
|
|
8331
|
+
this.url = config.url;
|
|
8332
|
+
this.timeout = config.timeout || 1e4;
|
|
8333
|
+
this.retries = config.retries || 3;
|
|
8334
|
+
this._cache = new SimpleCache({
|
|
8335
|
+
enabled: config.cache?.enabled !== false,
|
|
8336
|
+
ttl: config.cache?.ttl || 3e4,
|
|
8337
|
+
maxSize: config.cache?.maxSize || 100
|
|
8338
|
+
});
|
|
8339
|
+
}
|
|
8340
|
+
/**
|
|
8341
|
+
* Get list of SubDAOs
|
|
8342
|
+
* @param {object} [options] - Query options
|
|
8343
|
+
* @param {number} [options.limit=50] - Max results
|
|
8344
|
+
* @param {number} [options.skip=0] - Results to skip
|
|
8345
|
+
* @param {boolean} [options.cache=true] - Use cache
|
|
8346
|
+
* @returns {Promise<Array>} - List of SubDAOs
|
|
8347
|
+
*/
|
|
8348
|
+
async getSubDAOs(options = {}) {
|
|
8349
|
+
const { limit = 50, skip = 0, cache = true } = options;
|
|
8350
|
+
const cacheKey = `subdaos:${limit}:${skip}`;
|
|
8351
|
+
if (cache) {
|
|
8352
|
+
const cached = this._cache.get(cacheKey);
|
|
8353
|
+
if (cached) return cached;
|
|
8354
|
+
}
|
|
8355
|
+
try {
|
|
8356
|
+
const result = await retryWithBackoff(
|
|
8357
|
+
() => this._querySubDAOs({ limit, skip }),
|
|
8358
|
+
{ attempts: this.retries }
|
|
8359
|
+
);
|
|
8360
|
+
if (cache) {
|
|
8361
|
+
this._cache.set(cacheKey, result);
|
|
8362
|
+
}
|
|
8363
|
+
return result;
|
|
8364
|
+
} catch (error) {
|
|
8365
|
+
throw new SubgraphError(
|
|
8366
|
+
`Failed to fetch SubDAOs: ${error.message}`,
|
|
8367
|
+
"QUERY_FAILED",
|
|
8368
|
+
true,
|
|
8369
|
+
error
|
|
8370
|
+
);
|
|
8371
|
+
}
|
|
8372
|
+
}
|
|
8373
|
+
/**
|
|
8374
|
+
* Get list of proposals
|
|
8375
|
+
* @param {object} [options] - Query options
|
|
8376
|
+
* @param {string} [options.governor] - Filter by governor address
|
|
8377
|
+
* @param {string} [options.subdao] - Filter by SubDAO address
|
|
8378
|
+
* @param {string[]} [options.states] - Filter by proposal states
|
|
8379
|
+
* @param {number} [options.fromTimestamp] - Filter by creation time (>=)
|
|
8380
|
+
* @param {number} [options.toTimestamp] - Filter by creation time (<=)
|
|
8381
|
+
* @param {number} [options.limit=20] - Max results
|
|
8382
|
+
* @param {number} [options.skip=0] - Results to skip
|
|
8383
|
+
* @param {boolean} [options.cache=true] - Use cache
|
|
8384
|
+
* @returns {Promise<Array>} - List of proposals
|
|
8385
|
+
*/
|
|
8386
|
+
async getProposals(options = {}) {
|
|
8387
|
+
const {
|
|
8388
|
+
governor,
|
|
8389
|
+
subdao,
|
|
8390
|
+
states,
|
|
8391
|
+
fromTimestamp,
|
|
8392
|
+
toTimestamp,
|
|
8393
|
+
limit = 20,
|
|
8394
|
+
skip = 0,
|
|
8395
|
+
cache = true
|
|
8396
|
+
} = options;
|
|
8397
|
+
const cacheKey = `proposals:${JSON.stringify({
|
|
8398
|
+
governor,
|
|
8399
|
+
subdao,
|
|
8400
|
+
states,
|
|
8401
|
+
fromTimestamp,
|
|
8402
|
+
toTimestamp,
|
|
8403
|
+
limit,
|
|
8404
|
+
skip
|
|
8405
|
+
})}`;
|
|
8406
|
+
if (cache) {
|
|
8407
|
+
const cached = this._cache.get(cacheKey);
|
|
8408
|
+
if (cached) return cached;
|
|
8409
|
+
}
|
|
8410
|
+
try {
|
|
8411
|
+
const result = await retryWithBackoff(
|
|
8412
|
+
() => this._queryProposals({
|
|
8413
|
+
governor,
|
|
8414
|
+
subdao,
|
|
8415
|
+
states,
|
|
8416
|
+
fromTimestamp,
|
|
8417
|
+
toTimestamp,
|
|
8418
|
+
limit,
|
|
8419
|
+
skip
|
|
8420
|
+
}),
|
|
8421
|
+
{ attempts: this.retries }
|
|
8422
|
+
);
|
|
8423
|
+
if (cache) {
|
|
8424
|
+
this._cache.set(cacheKey, result);
|
|
8425
|
+
}
|
|
8426
|
+
return result;
|
|
8427
|
+
} catch (error) {
|
|
8428
|
+
throw new SubgraphError(
|
|
8429
|
+
`Failed to fetch proposals: ${error.message}`,
|
|
8430
|
+
"QUERY_FAILED",
|
|
8431
|
+
true,
|
|
8432
|
+
error
|
|
8433
|
+
);
|
|
8434
|
+
}
|
|
8435
|
+
}
|
|
8436
|
+
/**
|
|
8437
|
+
* Get proposal by ID
|
|
8438
|
+
* @param {string} id - Proposal ID
|
|
8439
|
+
* @param {object} [options] - Query options
|
|
8440
|
+
* @param {boolean} [options.cache=true] - Use cache
|
|
8441
|
+
* @returns {Promise<object|null>} - Proposal or null
|
|
8442
|
+
*/
|
|
8443
|
+
async getProposalById(id2, options = {}) {
|
|
8444
|
+
const { cache = true } = options;
|
|
8445
|
+
const cacheKey = `proposal:${id2}`;
|
|
8446
|
+
if (cache) {
|
|
8447
|
+
const cached = this._cache.get(cacheKey);
|
|
8448
|
+
if (cached !== null) return cached;
|
|
8449
|
+
}
|
|
8450
|
+
try {
|
|
8451
|
+
const result = await retryWithBackoff(
|
|
8452
|
+
() => subgraph.getProposalById({ url: this.url, id: id2 }),
|
|
8453
|
+
{ attempts: this.retries }
|
|
8454
|
+
);
|
|
8455
|
+
if (cache) {
|
|
8456
|
+
this._cache.set(cacheKey, result);
|
|
8457
|
+
}
|
|
8458
|
+
return result;
|
|
8459
|
+
} catch (error) {
|
|
8460
|
+
throw new SubgraphError(
|
|
8461
|
+
`Failed to fetch proposal ${id2}: ${error.message}`,
|
|
8462
|
+
"QUERY_FAILED",
|
|
8463
|
+
true,
|
|
8464
|
+
error
|
|
8465
|
+
);
|
|
8466
|
+
}
|
|
8467
|
+
}
|
|
8468
|
+
/**
|
|
8469
|
+
* Get libraries
|
|
8470
|
+
* @param {object} [options] - Query options
|
|
8471
|
+
* @param {string} [options.subdao] - Filter by SubDAO address
|
|
8472
|
+
* @param {number} [options.limit=50] - Max results
|
|
8473
|
+
* @param {number} [options.skip=0] - Results to skip
|
|
8474
|
+
* @param {boolean} [options.cache=true] - Use cache
|
|
8475
|
+
* @returns {Promise<Array>} - List of libraries
|
|
8476
|
+
*/
|
|
8477
|
+
async getLibraries(options = {}) {
|
|
8478
|
+
const { subdao, limit = 50, skip = 0, cache = true } = options;
|
|
8479
|
+
const cacheKey = `libraries:${subdao || "all"}:${limit}:${skip}`;
|
|
8480
|
+
if (cache) {
|
|
8481
|
+
const cached = this._cache.get(cacheKey);
|
|
8482
|
+
if (cached) return cached;
|
|
8483
|
+
}
|
|
8484
|
+
try {
|
|
8485
|
+
const result = await retryWithBackoff(
|
|
8486
|
+
() => subgraph.listLibraries({ url: this.url, subdao, first: limit, skip }),
|
|
8487
|
+
{ attempts: this.retries }
|
|
8488
|
+
);
|
|
8489
|
+
if (cache) {
|
|
8490
|
+
this._cache.set(cacheKey, result);
|
|
8491
|
+
}
|
|
8492
|
+
return result;
|
|
8493
|
+
} catch (error) {
|
|
8494
|
+
throw new SubgraphError(
|
|
8495
|
+
`Failed to fetch libraries: ${error.message}`,
|
|
8496
|
+
"QUERY_FAILED",
|
|
8497
|
+
true,
|
|
8498
|
+
error
|
|
8499
|
+
);
|
|
8500
|
+
}
|
|
8501
|
+
}
|
|
8502
|
+
/**
|
|
8503
|
+
* Get prompts by tag
|
|
8504
|
+
* @param {object} options - Query options
|
|
8505
|
+
* @param {string} options.tagsHash - Tag hash to filter by
|
|
8506
|
+
* @param {string} [options.registry] - Filter by registry address
|
|
8507
|
+
* @param {number} [options.limit=50] - Max results
|
|
8508
|
+
* @param {number} [options.skip=0] - Results to skip
|
|
8509
|
+
* @param {boolean} [options.cache=true] - Use cache
|
|
8510
|
+
* @returns {Promise<Array>} - List of prompts
|
|
8511
|
+
*/
|
|
8512
|
+
async getPromptsByTag(options) {
|
|
8513
|
+
if (!options || !options.tagsHash) {
|
|
8514
|
+
throw new Error("tagsHash is required");
|
|
8515
|
+
}
|
|
8516
|
+
const { tagsHash, registry, limit = 50, skip = 0, cache = true } = options;
|
|
8517
|
+
const cacheKey = `prompts-tag:${tagsHash}:${registry || "all"}:${limit}:${skip}`;
|
|
8518
|
+
if (cache) {
|
|
8519
|
+
const cached = this._cache.get(cacheKey);
|
|
8520
|
+
if (cached) return cached;
|
|
8521
|
+
}
|
|
8522
|
+
try {
|
|
8523
|
+
const result = await retryWithBackoff(
|
|
8524
|
+
() => subgraph.listPromptsByTag({
|
|
8525
|
+
url: this.url,
|
|
8526
|
+
tagsHash,
|
|
8527
|
+
registry,
|
|
8528
|
+
first: limit,
|
|
8529
|
+
skip
|
|
8530
|
+
}),
|
|
8531
|
+
{ attempts: this.retries }
|
|
8532
|
+
);
|
|
8533
|
+
if (cache) {
|
|
8534
|
+
this._cache.set(cacheKey, result);
|
|
8535
|
+
}
|
|
8536
|
+
return result;
|
|
8537
|
+
} catch (error) {
|
|
8538
|
+
throw new SubgraphError(
|
|
8539
|
+
`Failed to fetch prompts by tag: ${error.message}`,
|
|
8540
|
+
"QUERY_FAILED",
|
|
8541
|
+
true,
|
|
8542
|
+
error
|
|
8543
|
+
);
|
|
8544
|
+
}
|
|
8545
|
+
}
|
|
8546
|
+
/**
|
|
8547
|
+
* Get registry prompts
|
|
8548
|
+
* @param {object} options - Query options
|
|
8549
|
+
* @param {string} options.registry - Registry address
|
|
8550
|
+
* @param {number} [options.limit=50] - Max results
|
|
8551
|
+
* @param {number} [options.skip=0] - Results to skip
|
|
8552
|
+
* @param {boolean} [options.cache=true] - Use cache
|
|
8553
|
+
* @returns {Promise<Array>} - List of prompts
|
|
8554
|
+
*/
|
|
8555
|
+
async getRegistryPrompts(options) {
|
|
8556
|
+
if (!options || !options.registry) {
|
|
8557
|
+
throw new Error("registry is required");
|
|
8558
|
+
}
|
|
8559
|
+
const { registry, limit = 50, skip = 0, cache = true } = options;
|
|
8560
|
+
const cacheKey = `registry-prompts:${registry}:${limit}:${skip}`;
|
|
8561
|
+
if (cache) {
|
|
8562
|
+
const cached = this._cache.get(cacheKey);
|
|
8563
|
+
if (cached) return cached;
|
|
8564
|
+
}
|
|
8565
|
+
try {
|
|
8566
|
+
const result = await retryWithBackoff(
|
|
8567
|
+
() => subgraph.listRegistryPrompts({
|
|
8568
|
+
url: this.url,
|
|
8569
|
+
registry,
|
|
8570
|
+
first: limit,
|
|
8571
|
+
skip
|
|
8572
|
+
}),
|
|
8573
|
+
{ attempts: this.retries }
|
|
8574
|
+
);
|
|
8575
|
+
if (cache) {
|
|
8576
|
+
this._cache.set(cacheKey, result);
|
|
8577
|
+
}
|
|
8578
|
+
return result;
|
|
8579
|
+
} catch (error) {
|
|
8580
|
+
throw new SubgraphError(
|
|
8581
|
+
`Failed to fetch registry prompts: ${error.message}`,
|
|
8582
|
+
"QUERY_FAILED",
|
|
8583
|
+
true,
|
|
8584
|
+
error
|
|
8585
|
+
);
|
|
8586
|
+
}
|
|
8587
|
+
}
|
|
8588
|
+
/**
|
|
8589
|
+
* Clear cache
|
|
8590
|
+
*/
|
|
8591
|
+
clearCache() {
|
|
8592
|
+
this._cache.clear();
|
|
8593
|
+
}
|
|
8594
|
+
/**
|
|
8595
|
+
* Get cache statistics
|
|
8596
|
+
* @returns {object} - Cache stats
|
|
8597
|
+
*/
|
|
8598
|
+
getCacheStats() {
|
|
8599
|
+
return this._cache.stats();
|
|
8600
|
+
}
|
|
8601
|
+
// Private methods
|
|
8602
|
+
async _querySubDAOs({ limit, skip }) {
|
|
8603
|
+
const data = await subgraph.query(
|
|
8604
|
+
this.url,
|
|
8605
|
+
`
|
|
8606
|
+
query($first: Int!, $skip: Int!) {
|
|
8607
|
+
subDAOs(first: $first, skip: $skip, orderBy: createdAt, orderDirection: desc) {
|
|
8608
|
+
id
|
|
8609
|
+
address
|
|
8610
|
+
name
|
|
8611
|
+
description
|
|
8612
|
+
governor
|
|
8613
|
+
registry
|
|
8614
|
+
token
|
|
8615
|
+
createdAt
|
|
8616
|
+
}
|
|
8617
|
+
}
|
|
8618
|
+
`,
|
|
8619
|
+
{ first: limit, skip }
|
|
8620
|
+
);
|
|
8621
|
+
return data?.subDAOs || [];
|
|
8622
|
+
}
|
|
8623
|
+
async _queryProposals({ governor, subdao, states, fromTimestamp, toTimestamp, limit, skip }) {
|
|
8624
|
+
return await subgraph.listProposalsFiltered({
|
|
8625
|
+
url: this.url,
|
|
8626
|
+
governor,
|
|
8627
|
+
states,
|
|
8628
|
+
fromTimestamp,
|
|
8629
|
+
toTimestamp,
|
|
8630
|
+
first: limit,
|
|
8631
|
+
skip
|
|
8632
|
+
});
|
|
8633
|
+
}
|
|
8634
|
+
};
|
|
8635
|
+
module2.exports = { SubgraphService };
|
|
8636
|
+
}
|
|
8637
|
+
});
|
|
8638
|
+
|
|
8639
|
+
// src/services/ipfs/client.js
|
|
8640
|
+
var require_client2 = __commonJS({
|
|
8641
|
+
"src/services/ipfs/client.js"(exports2, module2) {
|
|
8642
|
+
var ipfs = require_ipfs();
|
|
8643
|
+
var { SimpleCache } = require_cache();
|
|
8644
|
+
var { retryWithBackoff } = require_retry();
|
|
8645
|
+
var { IPFSError } = require_errors2();
|
|
8646
|
+
var IPFSService = class {
|
|
8647
|
+
/**
|
|
8648
|
+
* @param {object} config - Service configuration
|
|
8649
|
+
* @param {string} config.workerBaseUrl - IPFS worker base URL
|
|
8650
|
+
* @param {string} config.gateway - Primary IPFS gateway URL
|
|
8651
|
+
* @param {object} [config.signer] - ethers v6 signer for worker auth
|
|
8652
|
+
* @param {Function} [config.getAuth] - Function to get auth credentials
|
|
8653
|
+
* @param {string} [config.workerToken] - Bearer token for worker auth
|
|
8654
|
+
* @param {number} [config.timeout=15000] - Request timeout in ms
|
|
8655
|
+
* @param {number} [config.retries=2] - Number of retry attempts
|
|
8656
|
+
* @param {object} [config.cache] - Cache configuration
|
|
8657
|
+
* @param {boolean} [config.cache.enabled=true] - Enable caching
|
|
8658
|
+
* @param {number} [config.cache.ttl=300000] - Cache TTL in ms (default 5min for immutable CIDs)
|
|
8659
|
+
* @param {number} [config.cache.maxSize=50] - Max cache entries
|
|
8660
|
+
*/
|
|
8661
|
+
constructor(config) {
|
|
8662
|
+
if (!config) {
|
|
8663
|
+
throw new Error("IPFSService requires a config object");
|
|
8664
|
+
}
|
|
8665
|
+
this.workerBaseUrl = config.workerBaseUrl;
|
|
8666
|
+
this.gateway = config.gateway;
|
|
8667
|
+
this.timeout = config.timeout || 15e3;
|
|
8668
|
+
this.retries = config.retries || 2;
|
|
8669
|
+
this._client = ipfs.createClient({
|
|
8670
|
+
workerBaseUrl: config.workerBaseUrl,
|
|
8671
|
+
gateway: config.gateway,
|
|
8672
|
+
workerSigner: config.signer,
|
|
8673
|
+
workerGetAuth: config.getAuth,
|
|
8674
|
+
workerToken: config.workerToken,
|
|
8675
|
+
timeoutMs: this.timeout,
|
|
8676
|
+
retries: this.retries
|
|
8677
|
+
});
|
|
8678
|
+
this._cache = new SimpleCache({
|
|
8679
|
+
enabled: config.cache?.enabled !== false,
|
|
8680
|
+
ttl: config.cache?.ttl || 3e5,
|
|
8681
|
+
// 5 minutes
|
|
8682
|
+
maxSize: config.cache?.maxSize || 50
|
|
8683
|
+
});
|
|
8684
|
+
}
|
|
8685
|
+
/**
|
|
8686
|
+
* Upload content to IPFS worker
|
|
8687
|
+
* @param {any} content - Content to upload (will be JSON stringified)
|
|
8688
|
+
* @param {object} [options] - Upload options
|
|
8689
|
+
* @param {string} [options.name] - Content name
|
|
8690
|
+
* @param {boolean} [options.warm=false] - Warm gateways after upload
|
|
8691
|
+
* @returns {Promise<string>} - CID of uploaded content
|
|
8692
|
+
*/
|
|
8693
|
+
async upload(content, options = {}) {
|
|
8694
|
+
const { name = "upload", warm = false } = options;
|
|
8695
|
+
try {
|
|
8696
|
+
const result = await retryWithBackoff(
|
|
8697
|
+
() => this._client.uploadJson(content, name, {
|
|
8698
|
+
provider: "worker",
|
|
8699
|
+
warm
|
|
8700
|
+
}),
|
|
8701
|
+
{ attempts: this.retries }
|
|
8702
|
+
);
|
|
8703
|
+
return result.cid;
|
|
8704
|
+
} catch (error) {
|
|
8705
|
+
throw new IPFSError(
|
|
8706
|
+
`Failed to upload content: ${error.message}`,
|
|
8707
|
+
"UPLOAD_FAILED",
|
|
8708
|
+
true,
|
|
8709
|
+
error
|
|
8710
|
+
);
|
|
8711
|
+
}
|
|
8712
|
+
}
|
|
8713
|
+
/**
|
|
8714
|
+
* Fetch JSON content by CID with parallel gateway fetching
|
|
8715
|
+
* @param {string} cid - IPFS CID
|
|
8716
|
+
* @param {object} [options] - Fetch options
|
|
8717
|
+
* @param {boolean} [options.cache=true] - Use cache
|
|
8718
|
+
* @param {number} [options.timeout=5000] - Timeout per gateway in ms
|
|
8719
|
+
* @param {string[]} [options.extraGateways] - Additional gateways to try
|
|
8720
|
+
* @returns {Promise<any|null>} - Parsed JSON content or null
|
|
8721
|
+
*/
|
|
8722
|
+
async fetchByCID(cid, options = {}) {
|
|
8723
|
+
if (!cid) {
|
|
8724
|
+
throw new IPFSError("CID is required", "INVALID_CID", false);
|
|
8725
|
+
}
|
|
8726
|
+
const { cache = true, timeout = 5e3, extraGateways = [] } = options;
|
|
8727
|
+
if (cache) {
|
|
8728
|
+
const cached = this._cache.get(cid);
|
|
8729
|
+
if (cached) return cached;
|
|
8730
|
+
}
|
|
8731
|
+
const urls = this._client.buildGatewayUrls(cid, extraGateways);
|
|
8732
|
+
try {
|
|
8733
|
+
const result = await this._parallelFetch(urls, timeout);
|
|
8734
|
+
if (result === null) {
|
|
8735
|
+
throw new IPFSError(
|
|
8736
|
+
`All gateways failed for CID ${cid}`,
|
|
8737
|
+
"GATEWAY_FAILED",
|
|
8738
|
+
true
|
|
8739
|
+
);
|
|
8740
|
+
}
|
|
8741
|
+
if (cache) {
|
|
8742
|
+
this._cache.set(cid, result);
|
|
8743
|
+
}
|
|
8744
|
+
return result;
|
|
8745
|
+
} catch (error) {
|
|
8746
|
+
if (error instanceof IPFSError) throw error;
|
|
8747
|
+
throw new IPFSError(
|
|
8748
|
+
`Failed to fetch CID ${cid}: ${error.message}`,
|
|
8749
|
+
"GATEWAY_FAILED",
|
|
8750
|
+
true,
|
|
8751
|
+
error
|
|
8752
|
+
);
|
|
8753
|
+
}
|
|
8754
|
+
}
|
|
8755
|
+
/**
|
|
8756
|
+
* Pin CIDs to IPFS worker
|
|
8757
|
+
* @param {string|string[]} cids - CID or array of CIDs to pin
|
|
8758
|
+
* @param {object} [options] - Pin options
|
|
8759
|
+
* @param {boolean} [options.warm=false] - Warm gateways after pinning
|
|
8760
|
+
* @returns {Promise<void>}
|
|
8761
|
+
*/
|
|
8762
|
+
async pin(cids, options = {}) {
|
|
8763
|
+
const { warm = false } = options;
|
|
8764
|
+
const cidList = Array.isArray(cids) ? cids : [cids];
|
|
8765
|
+
if (cidList.length === 0) {
|
|
8766
|
+
throw new IPFSError("At least one CID is required", "INVALID_CID", false);
|
|
8767
|
+
}
|
|
8768
|
+
try {
|
|
8769
|
+
await retryWithBackoff(
|
|
8770
|
+
() => this._client.pin({ cids: cidList, warm }),
|
|
8771
|
+
{ attempts: this.retries }
|
|
8772
|
+
);
|
|
8773
|
+
} catch (error) {
|
|
8774
|
+
throw new IPFSError(
|
|
8775
|
+
`Failed to pin CIDs: ${error.message}`,
|
|
8776
|
+
"PIN_FAILED",
|
|
8777
|
+
true,
|
|
8778
|
+
error
|
|
8779
|
+
);
|
|
8780
|
+
}
|
|
8781
|
+
}
|
|
8782
|
+
/**
|
|
8783
|
+
* Warm gateways for a CID (prefetch)
|
|
8784
|
+
* @param {string} cid - CID to warm
|
|
8785
|
+
* @param {object} [options] - Warm options
|
|
8786
|
+
* @param {string[]} [options.gateways] - Specific gateways to warm
|
|
8787
|
+
* @returns {Promise<object>} - Warm result
|
|
8788
|
+
*/
|
|
8789
|
+
async warm(cid, options = {}) {
|
|
8790
|
+
const { gateways } = options;
|
|
8791
|
+
try {
|
|
8792
|
+
return await this._client.warmGateways(cid, { gateways });
|
|
8793
|
+
} catch (error) {
|
|
8794
|
+
return { warmed: [] };
|
|
8795
|
+
}
|
|
8796
|
+
}
|
|
8797
|
+
/**
|
|
8798
|
+
* Clear cache
|
|
8799
|
+
*/
|
|
8800
|
+
clearCache() {
|
|
8801
|
+
this._cache.clear();
|
|
8802
|
+
}
|
|
8803
|
+
/**
|
|
8804
|
+
* Get cache statistics
|
|
8805
|
+
* @returns {object} - Cache stats
|
|
8806
|
+
*/
|
|
8807
|
+
getCacheStats() {
|
|
8808
|
+
return this._cache.stats();
|
|
8809
|
+
}
|
|
8810
|
+
// Private methods
|
|
8811
|
+
/**
|
|
8812
|
+
* Fetch from multiple gateways in parallel
|
|
8813
|
+
* @param {string[]} urls - Gateway URLs to try
|
|
8814
|
+
* @param {number} timeout - Timeout per gateway in ms
|
|
8815
|
+
* @returns {Promise<any|null>} - First successful result or null
|
|
8816
|
+
* @private
|
|
8817
|
+
*/
|
|
8818
|
+
async _parallelFetch(urls, timeout) {
|
|
8819
|
+
if (!urls || urls.length === 0) {
|
|
8820
|
+
return null;
|
|
8821
|
+
}
|
|
8822
|
+
const fetchOne = async (url) => {
|
|
8823
|
+
const controller = new AbortController();
|
|
8824
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
8825
|
+
try {
|
|
8826
|
+
const response = await fetch(url, {
|
|
8827
|
+
signal: controller.signal,
|
|
8828
|
+
headers: { Accept: "application/json" }
|
|
8829
|
+
});
|
|
8830
|
+
clearTimeout(timeoutId);
|
|
8831
|
+
if (!response.ok) {
|
|
8832
|
+
return null;
|
|
8833
|
+
}
|
|
8834
|
+
const text = await response.text();
|
|
8835
|
+
try {
|
|
8836
|
+
return JSON.parse(text);
|
|
8837
|
+
} catch {
|
|
8838
|
+
return text;
|
|
8839
|
+
}
|
|
8840
|
+
} catch (error) {
|
|
8841
|
+
clearTimeout(timeoutId);
|
|
8842
|
+
return null;
|
|
8843
|
+
}
|
|
8844
|
+
};
|
|
8845
|
+
const results = await Promise.allSettled(urls.map((url) => fetchOne(url)));
|
|
8846
|
+
for (const result of results) {
|
|
8847
|
+
if (result.status === "fulfilled" && result.value !== null) {
|
|
8848
|
+
return result.value;
|
|
8849
|
+
}
|
|
8850
|
+
}
|
|
8851
|
+
return null;
|
|
8852
|
+
}
|
|
8853
|
+
};
|
|
8854
|
+
module2.exports = { IPFSService };
|
|
8855
|
+
}
|
|
8856
|
+
});
|
|
8857
|
+
|
|
8858
|
+
// src/hooks/useSubDAOs.js
|
|
8859
|
+
var require_useSubDAOs = __commonJS({
|
|
8860
|
+
"src/hooks/useSubDAOs.js"(exports2, module2) {
|
|
8861
|
+
var useSWR = __require("swr");
|
|
8862
|
+
function useSubDAOs(subgraphService, options = {}) {
|
|
8863
|
+
const {
|
|
8864
|
+
limit = 50,
|
|
8865
|
+
skip = 0,
|
|
8866
|
+
cache = true,
|
|
8867
|
+
refreshInterval,
|
|
8868
|
+
revalidateOnFocus = true,
|
|
8869
|
+
revalidateOnReconnect = true
|
|
8870
|
+
} = options;
|
|
8871
|
+
const cacheKey = subgraphService ? ["subdaos", subgraphService.url, limit, skip] : null;
|
|
8872
|
+
const fetcher = async () => {
|
|
8873
|
+
if (!subgraphService) {
|
|
8874
|
+
throw new Error("SubgraphService is required");
|
|
8875
|
+
}
|
|
8876
|
+
return await subgraphService.getSubDAOs({ limit, skip, cache });
|
|
8877
|
+
};
|
|
8878
|
+
return useSWR(cacheKey, fetcher, {
|
|
8879
|
+
refreshInterval,
|
|
8880
|
+
revalidateOnFocus,
|
|
8881
|
+
revalidateOnReconnect,
|
|
8882
|
+
dedupingInterval: cache ? 3e4 : 0
|
|
8883
|
+
// Match service cache TTL
|
|
8884
|
+
});
|
|
8885
|
+
}
|
|
8886
|
+
module2.exports = { useSubDAOs };
|
|
8887
|
+
}
|
|
8888
|
+
});
|
|
8889
|
+
|
|
8890
|
+
// src/hooks/useProposals.js
|
|
8891
|
+
var require_useProposals = __commonJS({
|
|
8892
|
+
"src/hooks/useProposals.js"(exports2, module2) {
|
|
8893
|
+
var useSWR = __require("swr");
|
|
8894
|
+
function useProposals(subgraphService, options = {}) {
|
|
8895
|
+
const {
|
|
8896
|
+
governor,
|
|
8897
|
+
subdao,
|
|
8898
|
+
states,
|
|
8899
|
+
fromTimestamp,
|
|
8900
|
+
toTimestamp,
|
|
8901
|
+
limit = 20,
|
|
8902
|
+
skip = 0,
|
|
8903
|
+
cache = true,
|
|
8904
|
+
refreshInterval,
|
|
8905
|
+
revalidateOnFocus = true,
|
|
8906
|
+
revalidateOnReconnect = true
|
|
8907
|
+
} = options;
|
|
8908
|
+
const cacheKey = subgraphService ? [
|
|
8909
|
+
"proposals",
|
|
8910
|
+
subgraphService.url,
|
|
8911
|
+
governor,
|
|
8912
|
+
subdao,
|
|
8913
|
+
states?.join(","),
|
|
8914
|
+
fromTimestamp,
|
|
8915
|
+
toTimestamp,
|
|
8916
|
+
limit,
|
|
8917
|
+
skip
|
|
8918
|
+
] : null;
|
|
8919
|
+
const fetcher = async () => {
|
|
8920
|
+
if (!subgraphService) {
|
|
8921
|
+
throw new Error("SubgraphService is required");
|
|
8922
|
+
}
|
|
8923
|
+
return await subgraphService.getProposals({
|
|
8924
|
+
governor,
|
|
8925
|
+
subdao,
|
|
8926
|
+
states,
|
|
8927
|
+
fromTimestamp,
|
|
8928
|
+
toTimestamp,
|
|
8929
|
+
limit,
|
|
8930
|
+
skip,
|
|
8931
|
+
cache
|
|
8932
|
+
});
|
|
8933
|
+
};
|
|
8934
|
+
return useSWR(cacheKey, fetcher, {
|
|
8935
|
+
refreshInterval,
|
|
8936
|
+
revalidateOnFocus,
|
|
8937
|
+
revalidateOnReconnect,
|
|
8938
|
+
dedupingInterval: cache ? 3e4 : 0
|
|
8939
|
+
// Match service cache TTL
|
|
8940
|
+
});
|
|
8941
|
+
}
|
|
8942
|
+
module2.exports = { useProposals };
|
|
8943
|
+
}
|
|
8944
|
+
});
|
|
8945
|
+
|
|
8946
|
+
// src/hooks/useFetchCID.js
|
|
8947
|
+
var require_useFetchCID = __commonJS({
|
|
8948
|
+
"src/hooks/useFetchCID.js"(exports2, module2) {
|
|
8949
|
+
var useSWR = __require("swr");
|
|
8950
|
+
function useFetchCID(ipfsService, cid, options = {}) {
|
|
8951
|
+
const {
|
|
8952
|
+
cache = true,
|
|
8953
|
+
timeout = 5e3,
|
|
8954
|
+
extraGateways = [],
|
|
8955
|
+
refreshInterval,
|
|
8956
|
+
revalidateOnFocus = false,
|
|
8957
|
+
// CIDs are immutable
|
|
8958
|
+
revalidateOnReconnect = false
|
|
8959
|
+
// CIDs are immutable
|
|
8960
|
+
} = options;
|
|
8961
|
+
const cacheKey = ipfsService && cid ? ["ipfs-cid", cid] : null;
|
|
8962
|
+
const fetcher = async () => {
|
|
8963
|
+
if (!ipfsService) {
|
|
8964
|
+
throw new Error("IPFSService is required");
|
|
8965
|
+
}
|
|
8966
|
+
if (!cid) {
|
|
8967
|
+
throw new Error("CID is required");
|
|
8968
|
+
}
|
|
8969
|
+
return await ipfsService.fetchByCID(cid, {
|
|
8970
|
+
cache,
|
|
8971
|
+
timeout,
|
|
8972
|
+
extraGateways
|
|
8973
|
+
});
|
|
8974
|
+
};
|
|
8975
|
+
return useSWR(cacheKey, fetcher, {
|
|
8976
|
+
refreshInterval,
|
|
8977
|
+
revalidateOnFocus,
|
|
8978
|
+
revalidateOnReconnect,
|
|
8979
|
+
dedupingInterval: cache ? 3e5 : 0,
|
|
8980
|
+
// Match service cache TTL (5min)
|
|
8981
|
+
// CIDs are immutable, so we can cache errors too
|
|
8982
|
+
shouldRetryOnError: true,
|
|
8983
|
+
errorRetryInterval: 5e3,
|
|
8984
|
+
errorRetryCount: 3
|
|
8985
|
+
});
|
|
8986
|
+
}
|
|
8987
|
+
module2.exports = { useFetchCID };
|
|
8988
|
+
}
|
|
8989
|
+
});
|
|
8990
|
+
|
|
8991
|
+
// src/hooks/useUpload.js
|
|
8992
|
+
var require_useUpload = __commonJS({
|
|
8993
|
+
"src/hooks/useUpload.js"(exports2, module2) {
|
|
8994
|
+
var { useState, useCallback } = __require("react");
|
|
8995
|
+
function useUpload(ipfsService) {
|
|
8996
|
+
const [isUploading, setIsUploading] = useState(false);
|
|
8997
|
+
const [error, setError] = useState(null);
|
|
8998
|
+
const [data, setData] = useState(null);
|
|
8999
|
+
const upload = useCallback(
|
|
9000
|
+
async (content, options = {}) => {
|
|
9001
|
+
if (!ipfsService) {
|
|
9002
|
+
const err = new Error("IPFSService is required");
|
|
9003
|
+
setError(err);
|
|
9004
|
+
throw err;
|
|
9005
|
+
}
|
|
9006
|
+
setIsUploading(true);
|
|
9007
|
+
setError(null);
|
|
9008
|
+
try {
|
|
9009
|
+
const cid = await ipfsService.upload(content, options);
|
|
9010
|
+
setData(cid);
|
|
9011
|
+
return cid;
|
|
9012
|
+
} catch (err) {
|
|
9013
|
+
setError(err);
|
|
9014
|
+
throw err;
|
|
9015
|
+
} finally {
|
|
9016
|
+
setIsUploading(false);
|
|
9017
|
+
}
|
|
9018
|
+
},
|
|
9019
|
+
[ipfsService]
|
|
9020
|
+
);
|
|
9021
|
+
const reset = useCallback(() => {
|
|
9022
|
+
setIsUploading(false);
|
|
9023
|
+
setError(null);
|
|
9024
|
+
setData(null);
|
|
9025
|
+
}, []);
|
|
9026
|
+
return {
|
|
9027
|
+
upload,
|
|
9028
|
+
isUploading,
|
|
9029
|
+
error,
|
|
9030
|
+
data,
|
|
9031
|
+
reset
|
|
9032
|
+
};
|
|
9033
|
+
}
|
|
9034
|
+
module2.exports = { useUpload };
|
|
9035
|
+
}
|
|
9036
|
+
});
|
|
9037
|
+
|
|
9038
|
+
// src/hooks/index.js
|
|
9039
|
+
var require_hooks = __commonJS({
|
|
9040
|
+
"src/hooks/index.js"(exports2, module2) {
|
|
9041
|
+
var { useSubDAOs } = require_useSubDAOs();
|
|
9042
|
+
var { useProposals } = require_useProposals();
|
|
9043
|
+
var { useFetchCID } = require_useFetchCID();
|
|
9044
|
+
var { useUpload } = require_useUpload();
|
|
9045
|
+
module2.exports = {
|
|
9046
|
+
useSubDAOs,
|
|
9047
|
+
useProposals,
|
|
9048
|
+
useFetchCID,
|
|
9049
|
+
useUpload
|
|
9050
|
+
};
|
|
9051
|
+
}
|
|
9052
|
+
});
|
|
9053
|
+
|
|
8096
9054
|
// src/index.js
|
|
8097
9055
|
var require_src = __commonJS({
|
|
8098
9056
|
"src/index.js"(exports2, module2) {
|
|
@@ -8133,6 +9091,16 @@ var require_src = __commonJS({
|
|
|
8133
9091
|
openzeppelin: require_openzeppelin()
|
|
8134
9092
|
}
|
|
8135
9093
|
};
|
|
9094
|
+
var { SubgraphService } = require_client();
|
|
9095
|
+
var { IPFSService } = require_client2();
|
|
9096
|
+
var serviceErrors = require_errors2();
|
|
9097
|
+
var { SimpleCache } = require_cache();
|
|
9098
|
+
var { retryWithBackoff } = require_retry();
|
|
9099
|
+
var hooks = null;
|
|
9100
|
+
try {
|
|
9101
|
+
hooks = require_hooks();
|
|
9102
|
+
} catch (err) {
|
|
9103
|
+
}
|
|
8136
9104
|
module2.exports = {
|
|
8137
9105
|
version: pkg.version,
|
|
8138
9106
|
getProvider: utils.getProvider,
|
|
@@ -8163,6 +9131,18 @@ var require_src = __commonJS({
|
|
|
8163
9131
|
errors,
|
|
8164
9132
|
doppler,
|
|
8165
9133
|
adapters,
|
|
9134
|
+
// New service layer exports
|
|
9135
|
+
services: {
|
|
9136
|
+
SubgraphService,
|
|
9137
|
+
IPFSService
|
|
9138
|
+
},
|
|
9139
|
+
serviceErrors,
|
|
9140
|
+
serviceUtils: {
|
|
9141
|
+
SimpleCache,
|
|
9142
|
+
retryWithBackoff
|
|
9143
|
+
},
|
|
9144
|
+
// React hooks (optional - requires react + swr peer dependencies)
|
|
9145
|
+
hooks,
|
|
8166
9146
|
// Legacy exports (deprecated): maintain compatibility while consumers migrate
|
|
8167
9147
|
resolveGovernanceContext: async function legacyResolveGovernanceContext(args) {
|
|
8168
9148
|
console.warn("[@sage-protocol/sdk] resolveGovernanceContext is deprecated. Use governance helpers instead.");
|