@seedprotocol/sdk 0.6.5 → 0.6.7
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/dist/{BasePathResolver-BGxb4Qod.js → BasePathResolver-qV0apdVY.js} +124 -2
- package/dist/BasePathResolver-qV0apdVY.js.map +1 -0
- package/dist/{index-BMeU7u_g.js → ClientManager-Y5EWQuJj.js} +1517 -5491
- package/dist/ClientManager-Y5EWQuJj.js.map +1 -0
- package/dist/{ModelProperty-BKvUJGud.js → ModelProperty-DuB5nk6A.js} +33 -30
- package/dist/ModelProperty-DuB5nk6A.js.map +1 -0
- package/dist/{Schema-BBh7ZhRk.js → Schema-BYM-GNdE.js} +40 -39
- package/dist/{Schema-BBh7ZhRk.js.map → Schema-BYM-GNdE.js.map} +1 -1
- package/dist/{SchemaValidationService-BnJ43zov.js → SchemaValidationService-Dr6nmwPm.js} +12 -9
- package/dist/{SchemaValidationService-BnJ43zov.js.map → SchemaValidationService-Dr6nmwPm.js.map} +1 -1
- package/dist/{backfillMetadataPropertyIds-BEryy6Mu.js → backfillMetadataPropertyIds-BzbAB-C5.js} +4 -3
- package/dist/{backfillMetadataPropertyIds-BEryy6Mu.js.map → backfillMetadataPropertyIds-BzbAB-C5.js.map} +1 -1
- package/dist/cjs/{ModelProperty-CGjXNHFW.js → ModelProperty-CDTOx5zi.js} +21 -20
- package/dist/cjs/{ModelProperty-CGjXNHFW.js.map → ModelProperty-CDTOx5zi.js.map} +1 -1
- package/dist/cjs/{Schema-CLMqGTHn.js → Schema-XXcwj3hV.js} +24 -23
- package/dist/cjs/{Schema-CLMqGTHn.js.map → Schema-XXcwj3hV.js.map} +1 -1
- package/dist/cjs/{SchemaValidationService-BnS8o5FK.js → SchemaValidationService-Z7OhtGn5.js} +6 -5
- package/dist/cjs/{SchemaValidationService-BnS8o5FK.js.map → SchemaValidationService-Z7OhtGn5.js.map} +1 -1
- package/dist/cjs/{backfillMetadataPropertyIds-B3xNIFjQ.js → backfillMetadataPropertyIds-Bt4fzfIV.js} +6 -5
- package/dist/cjs/{backfillMetadataPropertyIds-B3xNIFjQ.js.map → backfillMetadataPropertyIds-Bt4fzfIV.js.map} +1 -1
- package/dist/cjs/{eas-wqfFyiti.js → eas-DdI-4vwl.js} +31 -6
- package/dist/cjs/eas-DdI-4vwl.js.map +1 -0
- package/dist/cjs/{getItem-BbEWSf4-.js → getItem-B_2GeTQf.js} +5 -4
- package/dist/cjs/{getItem-BbEWSf4-.js.map → getItem-B_2GeTQf.js.map} +1 -1
- package/dist/cjs/{getPublishPayload-i3UrBhza.js → getPublishPayload-Jmgi1RUj.js} +13 -12
- package/dist/cjs/getPublishPayload-Jmgi1RUj.js.map +1 -0
- package/dist/cjs/{getPublishUploads-CHhrLQmE.js → getPublishUploads-DNULcrN5.js} +8 -7
- package/dist/cjs/{getPublishUploads-CHhrLQmE.js.map → getPublishUploads-DNULcrN5.js.map} +1 -1
- package/dist/cjs/{getSegmentedItemProperties-ZlOTqI0r.js → getSegmentedItemProperties-DK82kjri.js} +3 -3
- package/dist/cjs/{getSegmentedItemProperties-ZlOTqI0r.js.map → getSegmentedItemProperties-DK82kjri.js.map} +1 -1
- package/dist/cjs/{index-Bx8ReSFI.js → index-DM1WR83P.js} +5148 -4967
- package/dist/cjs/index-DM1WR83P.js.map +1 -0
- package/dist/cjs/{index-DBGNShZR.js → index-tpO1Nj4v.js} +7 -6
- package/dist/cjs/index-tpO1Nj4v.js.map +1 -0
- package/dist/cjs/{property-CWZoijFu.js → property-DmvgA8FD.js} +8 -7
- package/dist/cjs/{property-CWZoijFu.js.map → property-DmvgA8FD.js.map} +1 -1
- package/dist/cjs/{syncDbWithEas-DRbf_wzk.js → syncDbWithEas-3hOoPdu1.js} +9 -8
- package/dist/cjs/{syncDbWithEas-DRbf_wzk.js.map → syncDbWithEas-3hOoPdu1.js.map} +1 -1
- package/dist/{configurePlatform-bGvKBBQy.js → configurePlatform-CCYQruij.js} +6 -3
- package/dist/{configurePlatform-bGvKBBQy.js.map → configurePlatform-CCYQruij.js.map} +1 -1
- package/dist/constants-DSqHBPNe.js +1347 -0
- package/dist/constants-DSqHBPNe.js.map +1 -0
- package/dist/{db-CbeA8ykN.js → db-CceyWksv.js} +29 -35
- package/dist/{db-CbeA8ykN.js.map → db-CceyWksv.js.map} +1 -1
- package/dist/{eas-CDANhqoz.js → eas-CH8iFIST.js} +9 -6
- package/dist/eas-CH8iFIST.js.map +1 -0
- package/dist/generateId-BopUwD8F.js +9 -0
- package/dist/generateId-BopUwD8F.js.map +1 -0
- package/dist/{getItem-CUOCDTe8.js → getItem-C7Mtfz6O.js} +11 -9
- package/dist/{getItem-CUOCDTe8.js.map → getItem-C7Mtfz6O.js.map} +1 -1
- package/dist/{getPublishPayload-0ntIkzv8.js → getPublishPayload-2gnnDdMh.js} +23 -20
- package/dist/getPublishPayload-2gnnDdMh.js.map +1 -0
- package/dist/{getPublishUploads-DLeAkS2m.js → getPublishUploads-AKISt3JA.js} +14 -11
- package/dist/{getPublishUploads-DLeAkS2m.js.map → getPublishUploads-AKISt3JA.js.map} +1 -1
- package/dist/{getSegmentedItemProperties-C0rMewcl.js → getSegmentedItemProperties-eIjvhCP_.js} +5 -5
- package/dist/{getSegmentedItemProperties-C0rMewcl.js.map → getSegmentedItemProperties-eIjvhCP_.js.map} +1 -1
- package/dist/index-BFpbtEsg.js +3492 -0
- package/dist/index-BFpbtEsg.js.map +1 -0
- package/dist/{index-CQIlolE1.js → index-CHi212ST.js} +15 -12
- package/dist/index-CHi212ST.js.map +1 -0
- package/dist/{index-C_5xwOPQ.js → index-CsmiM0RI.js} +2 -2
- package/dist/index-CsmiM0RI.js.map +1 -0
- package/dist/{index.node-DkpiPLbZ.js → index.node-CbWB4MDz.js} +4 -5
- package/dist/{index.node-DkpiPLbZ.js.map → index.node-CbWB4MDz.js.map} +1 -1
- package/dist/main.cjs +5 -4
- package/dist/main.cjs.map +1 -1
- package/dist/main.js +33 -31
- package/dist/main.js.map +1 -1
- package/dist/model-DGMe-TTm.js +25 -0
- package/dist/model-DGMe-TTm.js.map +1 -0
- package/dist/node.js +19 -16
- package/dist/node.js.map +1 -1
- package/dist/platform.browser.js +177 -43
- package/dist/platform.browser.js.map +1 -1
- package/dist/platform.node.js +4 -5
- package/dist/platform.node.js.map +1 -1
- package/dist/{property-B-9szEcZ.js → property-CWKcZ5YZ.js} +18 -16
- package/dist/property-CWKcZ5YZ.js.map +1 -0
- package/dist/src/Item/Item.d.ts.map +1 -1
- package/dist/src/ItemProperty/ItemProperty.d.ts.map +1 -1
- package/dist/src/ItemProperty/service/actors/hydrateFromDb.d.ts.map +1 -1
- package/dist/src/ItemProperty/service/actors/loadOrCreateProperty.d.ts.map +1 -1
- package/dist/src/Model/service/actors/loadOrCreateModel.d.ts.map +1 -1
- package/dist/src/browser/db/Db.d.ts +1 -1
- package/dist/src/browser/db/Db.d.ts.map +1 -1
- package/dist/src/browser/db/createSqlocalDrizzle.d.ts +21 -0
- package/dist/src/browser/db/createSqlocalDrizzle.d.ts.map +1 -0
- package/dist/src/browser/db/embeddedMigrations.d.ts +17 -0
- package/dist/src/browser/db/embeddedMigrations.d.ts.map +1 -0
- package/dist/src/client/clientManagerMachine.d.ts.map +1 -1
- package/dist/src/eas.d.ts +2 -2
- package/dist/src/eas.d.ts.map +1 -1
- package/dist/src/helpers/ArweaveClient/selectReadGateway.d.ts +1 -1
- package/dist/src/helpers/ArweaveClient/selectReadGateway.d.ts.map +1 -1
- package/dist/src/helpers/constants.d.ts +1 -2
- package/dist/src/helpers/constants.d.ts.map +1 -1
- package/dist/src/helpers/crypto.d.ts +3 -2
- package/dist/src/helpers/crypto.d.ts.map +1 -1
- package/dist/src/helpers/ensureImageLocal.d.ts +24 -0
- package/dist/src/helpers/ensureImageLocal.d.ts.map +1 -0
- package/dist/src/helpers/ethereumUtils.d.ts.map +1 -1
- package/dist/src/helpers/index.d.ts +10 -2
- package/dist/src/helpers/index.d.ts.map +1 -1
- package/dist/src/imports/json.d.ts.map +1 -1
- package/dist/src/index.d.ts +3 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/{syncDbWithEas-CNBuYKo_.js → syncDbWithEas-5kZneTEL.js} +19 -17
- package/dist/syncDbWithEas-5kZneTEL.js.map +1 -0
- package/dist/{updateSeedRevokedAt-D5ZbBehZ.js → updateSeedRevokedAt-CgfOBtqJ.js} +2 -2
- package/dist/{updateSeedRevokedAt-D5ZbBehZ.js.map → updateSeedRevokedAt-CgfOBtqJ.js.map} +1 -1
- package/package.json +6 -6
- package/dist/BasePathResolver-BGxb4Qod.js.map +0 -1
- package/dist/ModelProperty-BKvUJGud.js.map +0 -1
- package/dist/cjs/eas-wqfFyiti.js.map +0 -1
- package/dist/cjs/getPublishPayload-i3UrBhza.js.map +0 -1
- package/dist/cjs/index-Bx8ReSFI.js.map +0 -1
- package/dist/cjs/index-DBGNShZR.js.map +0 -1
- package/dist/constants-Bre6dTJR.js +0 -122
- package/dist/constants-Bre6dTJR.js.map +0 -1
- package/dist/constants-CTs2RUrM.js +0 -657
- package/dist/constants-CTs2RUrM.js.map +0 -1
- package/dist/eas-CDANhqoz.js.map +0 -1
- package/dist/getPublishPayload-0ntIkzv8.js.map +0 -1
- package/dist/index-BMeU7u_g.js.map +0 -1
- package/dist/index-CQIlolE1.js.map +0 -1
- package/dist/index-C_5xwOPQ.js.map +0 -1
- package/dist/property-B-9szEcZ.js.map +0 -1
- package/dist/syncDbWithEas-CNBuYKo_.js.map +0 -1
|
@@ -0,0 +1,1347 @@
|
|
|
1
|
+
import debug from 'debug';
|
|
2
|
+
import { a as BaseDb, b as metadata, B as BaseFileManager, c as appState, C as ImageSize } from './BasePathResolver-qV0apdVY.js';
|
|
3
|
+
import { isEasAttestationExplorerUrl, BaseArweaveClient, ensureReadGatewaySelected } from '@seedprotocol/arweave';
|
|
4
|
+
import { BaseQueryClient, BaseEasClient, GET_FILES_METADATA } from '@seedprotocol/eas';
|
|
5
|
+
import { waitFor } from 'xstate';
|
|
6
|
+
import 'pluralize';
|
|
7
|
+
import { Type } from '@sinclair/typebox';
|
|
8
|
+
import { and, eq, or, isNotNull } from 'drizzle-orm';
|
|
9
|
+
import { a as getAllAddressesFromDb } from './db-CceyWksv.js';
|
|
10
|
+
import 'nanoid';
|
|
11
|
+
import 'nanoid-dictionary';
|
|
12
|
+
import '@seedprotocol/query';
|
|
13
|
+
import { throttle } from 'lodash-es';
|
|
14
|
+
|
|
15
|
+
/** Default maximum length for Text properties when not explicitly set. Html holds arbitrary-length content and does not use this limit. */
|
|
16
|
+
const DEFAULT_TEXT_MAX_LENGTH = 255;
|
|
17
|
+
/**
|
|
18
|
+
* TypeBox schema for ValidationRules
|
|
19
|
+
*/
|
|
20
|
+
const TValidationRules = Type.Object({
|
|
21
|
+
pattern: Type.Optional(Type.String()),
|
|
22
|
+
minLength: Type.Optional(Type.Integer({ minimum: 0 })),
|
|
23
|
+
maxLength: Type.Optional(Type.Integer({ minimum: 1 })),
|
|
24
|
+
enum: Type.Optional(Type.Array(Type.Any())),
|
|
25
|
+
custom: Type.Optional(Type.String()),
|
|
26
|
+
}, { additionalProperties: true });
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Enum for all property data types
|
|
30
|
+
*/
|
|
31
|
+
var ModelPropertyDataTypes;
|
|
32
|
+
(function (ModelPropertyDataTypes) {
|
|
33
|
+
ModelPropertyDataTypes["Text"] = "Text";
|
|
34
|
+
ModelPropertyDataTypes["Number"] = "Number";
|
|
35
|
+
ModelPropertyDataTypes["List"] = "List";
|
|
36
|
+
ModelPropertyDataTypes["Relation"] = "Relation";
|
|
37
|
+
ModelPropertyDataTypes["Image"] = "Image";
|
|
38
|
+
ModelPropertyDataTypes["Json"] = "Json";
|
|
39
|
+
ModelPropertyDataTypes["File"] = "File";
|
|
40
|
+
ModelPropertyDataTypes["Boolean"] = "Boolean";
|
|
41
|
+
ModelPropertyDataTypes["Date"] = "Date";
|
|
42
|
+
ModelPropertyDataTypes["Html"] = "Html";
|
|
43
|
+
})(ModelPropertyDataTypes || (ModelPropertyDataTypes = {}));
|
|
44
|
+
/** Known data types in lowercase for normalization */
|
|
45
|
+
const DATA_TYPE_LOWER_MAP = {
|
|
46
|
+
text: 'Text',
|
|
47
|
+
string: 'Text', // JSON Schema convention
|
|
48
|
+
number: 'Number',
|
|
49
|
+
list: 'List',
|
|
50
|
+
relation: 'Relation',
|
|
51
|
+
image: 'Image',
|
|
52
|
+
json: 'Json',
|
|
53
|
+
file: 'File',
|
|
54
|
+
boolean: 'Boolean',
|
|
55
|
+
date: 'Date',
|
|
56
|
+
html: 'Html',
|
|
57
|
+
blob: 'Blob',
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Normalize dataType to canonical PascalCase form.
|
|
61
|
+
* Accepts lowercase (e.g. 'text') or mixed case and returns 'Text'.
|
|
62
|
+
* Use when ingesting dataType from JSON schema files.
|
|
63
|
+
*/
|
|
64
|
+
function normalizeDataType(value) {
|
|
65
|
+
if (!value || typeof value !== 'string')
|
|
66
|
+
return value ?? '';
|
|
67
|
+
const lower = value.toLowerCase().trim();
|
|
68
|
+
return DATA_TYPE_LOWER_MAP[lower] ?? value.charAt(0).toUpperCase() + value.slice(1).toLowerCase();
|
|
69
|
+
}
|
|
70
|
+
const TPropertyDataType = Type.Union([
|
|
71
|
+
Type.Literal(ModelPropertyDataTypes.Text),
|
|
72
|
+
Type.Literal(ModelPropertyDataTypes.Number),
|
|
73
|
+
Type.Literal(ModelPropertyDataTypes.List),
|
|
74
|
+
Type.Literal(ModelPropertyDataTypes.Relation),
|
|
75
|
+
Type.Literal(ModelPropertyDataTypes.Image),
|
|
76
|
+
Type.Literal(ModelPropertyDataTypes.Json),
|
|
77
|
+
Type.Literal(ModelPropertyDataTypes.File),
|
|
78
|
+
Type.Literal(ModelPropertyDataTypes.Boolean),
|
|
79
|
+
Type.Literal(ModelPropertyDataTypes.Date),
|
|
80
|
+
Type.Literal(ModelPropertyDataTypes.Html),
|
|
81
|
+
]);
|
|
82
|
+
const TStorageType = Type.Union([
|
|
83
|
+
Type.Literal('ItemStorage'), // Looks for a storageTransactionId property on the item
|
|
84
|
+
Type.Literal('PropertyStorage'), // Looks for a storageTransactionId value on the property
|
|
85
|
+
]);
|
|
86
|
+
/** How publish handles `data:image/*` URIs inside Html storage. Only applies when `dataType === 'Html'`. */
|
|
87
|
+
const THtmlEmbeddedDataUriPolicy = Type.Union([
|
|
88
|
+
Type.Literal('materialize'),
|
|
89
|
+
Type.Literal('preserve'),
|
|
90
|
+
]);
|
|
91
|
+
const TProperty = Type.Object({
|
|
92
|
+
id: Type.Optional(Type.String()), // schemaFileId (string) - public ID
|
|
93
|
+
_dbId: Type.Optional(Type.Number()), // Database integer ID - internal only
|
|
94
|
+
name: Type.Optional(Type.String()),
|
|
95
|
+
dataType: TPropertyDataType,
|
|
96
|
+
ref: Type.Optional(Type.String()),
|
|
97
|
+
// modelId: string (modelFileId) in code; number only for DB foreign keys when resolved
|
|
98
|
+
modelId: Type.Optional(Type.Union([Type.Number(), Type.String()])),
|
|
99
|
+
modelName: Type.Optional(Type.String()),
|
|
100
|
+
refModelId: Type.Optional(Type.Number()),
|
|
101
|
+
refModelName: Type.Optional(Type.String()),
|
|
102
|
+
refValueType: Type.Optional(TPropertyDataType),
|
|
103
|
+
storageType: Type.Optional(TStorageType),
|
|
104
|
+
localStorageDir: Type.Optional(Type.String()),
|
|
105
|
+
filenameSuffix: Type.Optional(Type.String()),
|
|
106
|
+
validation: Type.Optional(TValidationRules),
|
|
107
|
+
required: Type.Optional(Type.Boolean()),
|
|
108
|
+
/** When set on an Html property: `materialize` uploads embedded images as Image seeds and rewrites src; `preserve` leaves data URIs. */
|
|
109
|
+
htmlEmbeddedDataUriPolicy: Type.Optional(THtmlEmbeddedDataUriPolicy),
|
|
110
|
+
});
|
|
111
|
+
const TPropertyConstructor = Type.Function([
|
|
112
|
+
Type.Optional(Type.Union([Type.String(), TStorageType, Type.Undefined()])),
|
|
113
|
+
Type.Optional(Type.Union([Type.String(), TPropertyDataType])),
|
|
114
|
+
Type.Optional(Type.Union([Type.String(), Type.Boolean()])),
|
|
115
|
+
], TProperty);
|
|
116
|
+
const TPropertyDefs = Type.Record(TPropertyDataType, TPropertyConstructor);
|
|
117
|
+
function htmlPropertyFactory(opts) {
|
|
118
|
+
return {
|
|
119
|
+
dataType: ModelPropertyDataTypes.Html,
|
|
120
|
+
...(opts?.htmlEmbeddedDataUriPolicy
|
|
121
|
+
? { htmlEmbeddedDataUriPolicy: opts.htmlEmbeddedDataUriPolicy }
|
|
122
|
+
: {}),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
const Property = {
|
|
126
|
+
Text: (storageType, localStorageDir, filenameSuffix) => ({
|
|
127
|
+
dataType: ModelPropertyDataTypes.Text,
|
|
128
|
+
storageType,
|
|
129
|
+
localStorageDir,
|
|
130
|
+
filenameSuffix,
|
|
131
|
+
TObject: Type.String(),
|
|
132
|
+
}),
|
|
133
|
+
Json: () => ({ dataType: ModelPropertyDataTypes.Json }),
|
|
134
|
+
File: () => ({ dataType: ModelPropertyDataTypes.File }),
|
|
135
|
+
Number: () => ({ dataType: ModelPropertyDataTypes.Number }),
|
|
136
|
+
/**
|
|
137
|
+
* Create a List property. refValueType is required; ref is required only for list of relations.
|
|
138
|
+
* @param refValueType - Element type: 'Text', 'Number', 'Relation', etc.
|
|
139
|
+
* @param ref - Model name when refValueType is 'Relation' (optional for primitives)
|
|
140
|
+
* @example List('Text') - list of strings
|
|
141
|
+
* @example List('Relation', 'Tag') - list of relations to Tag model
|
|
142
|
+
*/
|
|
143
|
+
List: (refValueType, ref) => ({
|
|
144
|
+
dataType: ModelPropertyDataTypes.List,
|
|
145
|
+
ref,
|
|
146
|
+
refValueType,
|
|
147
|
+
}),
|
|
148
|
+
Relation: (ref, refValueType, required) => ({
|
|
149
|
+
dataType: ModelPropertyDataTypes.Relation,
|
|
150
|
+
ref,
|
|
151
|
+
refValueType,
|
|
152
|
+
required,
|
|
153
|
+
}),
|
|
154
|
+
Image: () => ({ dataType: ModelPropertyDataTypes.Image }),
|
|
155
|
+
Boolean: () => ({ dataType: ModelPropertyDataTypes.Boolean }),
|
|
156
|
+
Date: () => ({ dataType: ModelPropertyDataTypes.Date }),
|
|
157
|
+
Html: htmlPropertyFactory,
|
|
158
|
+
/** `Html` opts are not representable in `TPropertyConstructor`; keep runtime shape via double assert. */
|
|
159
|
+
};
|
|
160
|
+
const PropertyMetadataKey = Symbol('property');
|
|
161
|
+
const PropertyConstructor = (propertyType) => {
|
|
162
|
+
return function (parentClassPrototype, propertyKey) {
|
|
163
|
+
const existingProperties = Reflect.getMetadata(PropertyMetadataKey, parentClassPrototype) || [];
|
|
164
|
+
existingProperties.push({ propertyKey, propertyType });
|
|
165
|
+
Reflect.defineMetadata(PropertyMetadataKey, existingProperties, parentClassPrototype);
|
|
166
|
+
};
|
|
167
|
+
};
|
|
168
|
+
const Text = (storageType, srcDir, filenameSuffix) => PropertyConstructor(Property.Text(storageType, srcDir, filenameSuffix));
|
|
169
|
+
const Number$1 = () => PropertyConstructor(Property.Number());
|
|
170
|
+
const Json = () => PropertyConstructor(Property.Json());
|
|
171
|
+
const File = () => PropertyConstructor(Property.File());
|
|
172
|
+
const Image = () => PropertyConstructor(Property.Image());
|
|
173
|
+
const Relation = (ref, refValueType, required) => PropertyConstructor(Property.Relation(ref, refValueType, required)); // Adjust for actual relation type
|
|
174
|
+
const List = (refValueType, ref) => PropertyConstructor(Property.List(refValueType, ref));
|
|
175
|
+
const Boolean = () => PropertyConstructor(Property.Boolean());
|
|
176
|
+
const Date$1 = () => PropertyConstructor(Property.Date());
|
|
177
|
+
const Html = (opts) => PropertyConstructor(htmlPropertyFactory(opts));
|
|
178
|
+
|
|
179
|
+
/** Same rules as getCorrectId in helpers/index (avoid barrel circular import). */
|
|
180
|
+
function parseLocalIdOrUid(localIdOrUid) {
|
|
181
|
+
const id = { localId: undefined, uid: undefined };
|
|
182
|
+
if (!localIdOrUid)
|
|
183
|
+
return id;
|
|
184
|
+
if (localIdOrUid.length === 10) {
|
|
185
|
+
id.localId = localIdOrUid;
|
|
186
|
+
}
|
|
187
|
+
if (localIdOrUid.startsWith('0x') && localIdOrUid.length === 66) {
|
|
188
|
+
id.uid = localIdOrUid;
|
|
189
|
+
}
|
|
190
|
+
return id;
|
|
191
|
+
}
|
|
192
|
+
/** Relation/image refs may be string ids or `{ seedLocalId?, seedUid? }`. */
|
|
193
|
+
function normalizeRelationPropertyValue(value) {
|
|
194
|
+
if (value == null)
|
|
195
|
+
return undefined;
|
|
196
|
+
if (typeof value === 'string') {
|
|
197
|
+
const t = value.trim();
|
|
198
|
+
return t !== '' ? t : undefined;
|
|
199
|
+
}
|
|
200
|
+
if (typeof value === 'object' && value !== null) {
|
|
201
|
+
const o = value;
|
|
202
|
+
if (typeof o.seedLocalId === 'string' && o.seedLocalId.trim() !== '') {
|
|
203
|
+
return o.seedLocalId.trim();
|
|
204
|
+
}
|
|
205
|
+
if (typeof o.seedUid === 'string' && o.seedUid.trim() !== '') {
|
|
206
|
+
return o.seedUid.trim();
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return undefined;
|
|
210
|
+
}
|
|
211
|
+
/** getCorrectId only accepts 10-char local ids and 0x66 uids; local seed refs can be up to 21 chars. */
|
|
212
|
+
const LOCAL_SEED_REF = /^[a-zA-Z0-9_-]{10,21}$/;
|
|
213
|
+
/** Resolve seed ids from a single string (context propertyValue or DB row). */
|
|
214
|
+
function resolveSeedIdsFromRefString(s) {
|
|
215
|
+
const fromCorrect = parseLocalIdOrUid(s);
|
|
216
|
+
if (fromCorrect.localId || fromCorrect.uid) {
|
|
217
|
+
return { seedLocalId: fromCorrect.localId, seedUid: fromCorrect.uid };
|
|
218
|
+
}
|
|
219
|
+
if (LOCAL_SEED_REF.test(s) && !s.startsWith('0x')) {
|
|
220
|
+
return { seedLocalId: s };
|
|
221
|
+
}
|
|
222
|
+
return {};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const seedUidToStorageTransactionId = new Map();
|
|
226
|
+
const getStorageTransactionIdForSeedUid = async (seedUid) => {
|
|
227
|
+
if (seedUidToStorageTransactionId.has(seedUid)) {
|
|
228
|
+
return seedUidToStorageTransactionId.get(seedUid);
|
|
229
|
+
}
|
|
230
|
+
const appDb = BaseDb.getAppDb();
|
|
231
|
+
const results = (await appDb
|
|
232
|
+
.select({
|
|
233
|
+
storageTransactionId: metadata.propertyValue,
|
|
234
|
+
})
|
|
235
|
+
.from(metadata)
|
|
236
|
+
.where(and(eq(metadata.seedUid, seedUid), eq(metadata.propertyName, 'storageTransactionId'))));
|
|
237
|
+
if (!results || results.length === 0) {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
seedUidToStorageTransactionId.set(seedUid, results[0].storageTransactionId);
|
|
241
|
+
return results[0].storageTransactionId;
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
const logger$3 = debug('seedSdk:helpers:mediaRef');
|
|
245
|
+
/** Typical Arweave transaction id: 43 URL-safe base64 characters. */
|
|
246
|
+
const ARWEAVE_TX_ID = /^[a-zA-Z0-9_-]{43}$/;
|
|
247
|
+
function trimOrEmpty(raw) {
|
|
248
|
+
if (raw == null)
|
|
249
|
+
return '';
|
|
250
|
+
return typeof raw === 'string' ? raw.trim() : String(raw).trim();
|
|
251
|
+
}
|
|
252
|
+
function coalesceStringField(item, key) {
|
|
253
|
+
const v = item[key];
|
|
254
|
+
if (v == null)
|
|
255
|
+
return '';
|
|
256
|
+
if (typeof v === 'string')
|
|
257
|
+
return v.trim();
|
|
258
|
+
if (typeof v === 'number' || typeof v === 'boolean')
|
|
259
|
+
return String(v);
|
|
260
|
+
return '';
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Try JSON relation payloads and plain relation id strings.
|
|
264
|
+
*/
|
|
265
|
+
function unwrapRelationString(raw) {
|
|
266
|
+
const t = raw.trim();
|
|
267
|
+
if (!t)
|
|
268
|
+
return t;
|
|
269
|
+
try {
|
|
270
|
+
const parsed = JSON.parse(t);
|
|
271
|
+
const fromObj = normalizeRelationPropertyValue(parsed);
|
|
272
|
+
if (fromObj)
|
|
273
|
+
return fromObj;
|
|
274
|
+
}
|
|
275
|
+
catch {
|
|
276
|
+
// not JSON
|
|
277
|
+
}
|
|
278
|
+
return t;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Pure, sync classification of a media-related string from feeds or XML.
|
|
282
|
+
*/
|
|
283
|
+
function classifyMediaRef(raw, options) {
|
|
284
|
+
const s = trimOrEmpty(raw);
|
|
285
|
+
if (!s) {
|
|
286
|
+
return { kind: 'empty' };
|
|
287
|
+
}
|
|
288
|
+
const treatAs = options?.treatAs;
|
|
289
|
+
if (treatAs === 'url') {
|
|
290
|
+
return { kind: 'url', href: s };
|
|
291
|
+
}
|
|
292
|
+
if (treatAs === 'arweaveTx') {
|
|
293
|
+
return { kind: 'arweaveTxId', txId: s };
|
|
294
|
+
}
|
|
295
|
+
if (treatAs === 'seedUid') {
|
|
296
|
+
if (s.startsWith('0x') && s.length === 66) {
|
|
297
|
+
return { kind: 'seedUid', uid: s };
|
|
298
|
+
}
|
|
299
|
+
return { kind: 'unknown', raw: s };
|
|
300
|
+
}
|
|
301
|
+
if (s.startsWith('http://') || s.startsWith('https://') || s.startsWith('blob:') || s.startsWith('data:')) {
|
|
302
|
+
if (isEasAttestationExplorerUrl(s)) {
|
|
303
|
+
return { kind: 'nonMediaUrl', href: s };
|
|
304
|
+
}
|
|
305
|
+
return { kind: 'url', href: s };
|
|
306
|
+
}
|
|
307
|
+
const unwrapped = unwrapRelationString(s);
|
|
308
|
+
const ids = resolveSeedIdsFromRefString(unwrapped);
|
|
309
|
+
if (ids.seedUid) {
|
|
310
|
+
return { kind: 'seedUid', uid: ids.seedUid };
|
|
311
|
+
}
|
|
312
|
+
if (ids.seedLocalId) {
|
|
313
|
+
return { kind: 'seedLocalId', localId: ids.seedLocalId };
|
|
314
|
+
}
|
|
315
|
+
if (unwrapped.startsWith('0x') && unwrapped.length === 66) {
|
|
316
|
+
return { kind: 'seedUid', uid: unwrapped };
|
|
317
|
+
}
|
|
318
|
+
if (ARWEAVE_TX_ID.test(unwrapped) && !unwrapped.startsWith('0x')) {
|
|
319
|
+
return { kind: 'arweaveTxId', txId: unwrapped };
|
|
320
|
+
}
|
|
321
|
+
return { kind: 'unknown', raw: s };
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Same as convertTxIdToImage but local to this module to avoid circular imports with helpers/index.
|
|
325
|
+
*/
|
|
326
|
+
async function tryLocalBlobUrlFromTxId(txId) {
|
|
327
|
+
const imageFilePath = BaseFileManager.getFilesPath('images', txId);
|
|
328
|
+
const fileExists = await BaseFileManager.pathExists(imageFilePath);
|
|
329
|
+
if (!fileExists) {
|
|
330
|
+
logger$3(`[tryLocalBlobUrlFromTxId] ${imageFilePath} does not exist`);
|
|
331
|
+
return undefined;
|
|
332
|
+
}
|
|
333
|
+
const buffer = await BaseFileManager.readFileAsBuffer(imageFilePath);
|
|
334
|
+
let arrayBuffer;
|
|
335
|
+
if (buffer instanceof Blob) {
|
|
336
|
+
arrayBuffer = await buffer.arrayBuffer();
|
|
337
|
+
}
|
|
338
|
+
else if (buffer instanceof ArrayBuffer) {
|
|
339
|
+
arrayBuffer = buffer;
|
|
340
|
+
}
|
|
341
|
+
else {
|
|
342
|
+
const sliced = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
|
|
343
|
+
if (sliced instanceof SharedArrayBuffer) {
|
|
344
|
+
const uint8Array = new Uint8Array(sliced);
|
|
345
|
+
arrayBuffer = uint8Array.buffer.slice(0);
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
arrayBuffer = sliced;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
const uint = new Uint8Array(arrayBuffer);
|
|
352
|
+
const imageBlob = new Blob([uint]);
|
|
353
|
+
return URL.createObjectURL(imageBlob);
|
|
354
|
+
}
|
|
355
|
+
async function resolveTxId(txId) {
|
|
356
|
+
const local = await tryLocalBlobUrlFromTxId(txId);
|
|
357
|
+
if (local) {
|
|
358
|
+
return { status: 'ready', href: local, source: 'localBlob' };
|
|
359
|
+
}
|
|
360
|
+
return {
|
|
361
|
+
status: 'ready',
|
|
362
|
+
href: BaseArweaveClient.getRawUrl(txId),
|
|
363
|
+
source: 'gateway',
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Resolve a classified media reference to a display URL (https/blob/data), using local files when present.
|
|
368
|
+
*/
|
|
369
|
+
async function resolveMediaRef(raw, options) {
|
|
370
|
+
const classification = classifyMediaRef(raw, options);
|
|
371
|
+
switch (classification.kind) {
|
|
372
|
+
case 'empty':
|
|
373
|
+
return { status: 'empty' };
|
|
374
|
+
case 'url':
|
|
375
|
+
return {
|
|
376
|
+
status: 'ready',
|
|
377
|
+
href: classification.href,
|
|
378
|
+
source: 'direct',
|
|
379
|
+
};
|
|
380
|
+
case 'nonMediaUrl':
|
|
381
|
+
return {
|
|
382
|
+
status: 'unresolved',
|
|
383
|
+
reason: 'non_media_url',
|
|
384
|
+
classification,
|
|
385
|
+
};
|
|
386
|
+
case 'seedLocalId':
|
|
387
|
+
return {
|
|
388
|
+
status: 'unresolved',
|
|
389
|
+
reason: 'seed_local_id_not_portable',
|
|
390
|
+
classification,
|
|
391
|
+
};
|
|
392
|
+
case 'unknown':
|
|
393
|
+
return {
|
|
394
|
+
status: 'unresolved',
|
|
395
|
+
reason: 'unknown_ref',
|
|
396
|
+
classification,
|
|
397
|
+
};
|
|
398
|
+
case 'arweaveTxId':
|
|
399
|
+
return resolveTxId(classification.txId);
|
|
400
|
+
case 'seedUid': {
|
|
401
|
+
const appDb = BaseDb.getAppDb();
|
|
402
|
+
if (!appDb) {
|
|
403
|
+
return {
|
|
404
|
+
status: 'unresolved',
|
|
405
|
+
reason: 'seed_uid_requires_app_db',
|
|
406
|
+
classification,
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
const storageTransactionId = await getStorageTransactionIdForSeedUid(classification.uid);
|
|
410
|
+
if (!storageTransactionId) {
|
|
411
|
+
return {
|
|
412
|
+
status: 'unresolved',
|
|
413
|
+
reason: 'seed_uid_no_storage_transaction',
|
|
414
|
+
classification,
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
return resolveTxId(storageTransactionId);
|
|
418
|
+
}
|
|
419
|
+
default:
|
|
420
|
+
return {
|
|
421
|
+
status: 'unresolved',
|
|
422
|
+
reason: 'unknown_ref',
|
|
423
|
+
classification,
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Read a string field from a parsed item, trying camelCase and snake_case keys.
|
|
429
|
+
*/
|
|
430
|
+
function getFeedItemStringField(item, key) {
|
|
431
|
+
const direct = coalesceStringField(item, key);
|
|
432
|
+
if (direct)
|
|
433
|
+
return direct;
|
|
434
|
+
const snake = key.replace(/[A-Z]/g, (l) => `_${l.toLowerCase()}`);
|
|
435
|
+
if (snake !== key) {
|
|
436
|
+
const alt = coalesceStringField(item, snake);
|
|
437
|
+
if (alt)
|
|
438
|
+
return alt;
|
|
439
|
+
}
|
|
440
|
+
const parts = key.split('_');
|
|
441
|
+
if (parts.length > 1) {
|
|
442
|
+
const camel = parts[0] + parts.slice(1).map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join('');
|
|
443
|
+
const alt2 = coalesceStringField(item, camel);
|
|
444
|
+
if (alt2)
|
|
445
|
+
return alt2;
|
|
446
|
+
}
|
|
447
|
+
return '';
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Apply a field manifest to a plain feed item: classify media/file fields; pass through html/text.
|
|
451
|
+
*/
|
|
452
|
+
function normalizeFeedItemFields(item, manifest) {
|
|
453
|
+
const out = {};
|
|
454
|
+
for (const [fieldKey, descriptor] of Object.entries(manifest)) {
|
|
455
|
+
const raw = getFeedItemStringField(item, fieldKey);
|
|
456
|
+
if (!raw) {
|
|
457
|
+
out[fieldKey] = undefined;
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
if (descriptor.role === 'html') {
|
|
461
|
+
out[fieldKey] = { role: 'html', raw };
|
|
462
|
+
continue;
|
|
463
|
+
}
|
|
464
|
+
if (descriptor.role === 'text') {
|
|
465
|
+
out[fieldKey] = { role: 'text', raw };
|
|
466
|
+
continue;
|
|
467
|
+
}
|
|
468
|
+
const classification = classifyMediaRef(raw, { treatAs: descriptor.treatAs });
|
|
469
|
+
out[fieldKey] = {
|
|
470
|
+
role: descriptor.role,
|
|
471
|
+
raw,
|
|
472
|
+
classification,
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
return out;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
class SeedEventEmitter {
|
|
479
|
+
constructor() {
|
|
480
|
+
this.listeners = new Map();
|
|
481
|
+
}
|
|
482
|
+
on(event, listener) {
|
|
483
|
+
const set = this.listeners.get(event) ?? new Set();
|
|
484
|
+
set.add(listener);
|
|
485
|
+
this.listeners.set(event, set);
|
|
486
|
+
return this;
|
|
487
|
+
}
|
|
488
|
+
addListener(event, listener) {
|
|
489
|
+
return this.on(event, listener);
|
|
490
|
+
}
|
|
491
|
+
once(event, listener) {
|
|
492
|
+
const wrapped = (...args) => {
|
|
493
|
+
this.off(event, wrapped);
|
|
494
|
+
listener(...args);
|
|
495
|
+
};
|
|
496
|
+
return this.on(event, wrapped);
|
|
497
|
+
}
|
|
498
|
+
off(event, listener) {
|
|
499
|
+
const set = this.listeners.get(event);
|
|
500
|
+
if (!set)
|
|
501
|
+
return this;
|
|
502
|
+
set.delete(listener);
|
|
503
|
+
if (set.size === 0)
|
|
504
|
+
this.listeners.delete(event);
|
|
505
|
+
return this;
|
|
506
|
+
}
|
|
507
|
+
removeListener(event, listener) {
|
|
508
|
+
return this.off(event, listener);
|
|
509
|
+
}
|
|
510
|
+
removeAllListeners(event) {
|
|
511
|
+
if (typeof event === 'undefined') {
|
|
512
|
+
this.listeners.clear();
|
|
513
|
+
return this;
|
|
514
|
+
}
|
|
515
|
+
this.listeners.delete(event);
|
|
516
|
+
return this;
|
|
517
|
+
}
|
|
518
|
+
emit(event, ...args) {
|
|
519
|
+
const set = this.listeners.get(event);
|
|
520
|
+
if (!set || set.size === 0)
|
|
521
|
+
return false;
|
|
522
|
+
for (const listener of [...set]) {
|
|
523
|
+
listener(...args);
|
|
524
|
+
}
|
|
525
|
+
return true;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
const eventEmitter = new SeedEventEmitter();
|
|
529
|
+
|
|
530
|
+
const getMetadata = async (props) => {
|
|
531
|
+
const appDb = BaseDb.getAppDb();
|
|
532
|
+
let storageTransactionId;
|
|
533
|
+
if (props) {
|
|
534
|
+
storageTransactionId = props.storageTransactionId;
|
|
535
|
+
}
|
|
536
|
+
const whereClauses = [];
|
|
537
|
+
if (storageTransactionId) {
|
|
538
|
+
whereClauses.push(eq(metadata.propertyName, 'storageTransactionId'));
|
|
539
|
+
whereClauses.push(eq(metadata.propertyValue, storageTransactionId));
|
|
540
|
+
}
|
|
541
|
+
const metadataRows = await appDb
|
|
542
|
+
.select()
|
|
543
|
+
.from(metadata)
|
|
544
|
+
.where(and(...whereClauses));
|
|
545
|
+
if (!metadataRows || metadataRows.length === 0) {
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
return metadataRows[0];
|
|
549
|
+
};
|
|
550
|
+
|
|
551
|
+
const saveMetadata = async (metadataRecord, metadataValues) => {
|
|
552
|
+
if (!metadataRecord.localId) {
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
const appDb = BaseDb.getAppDb();
|
|
556
|
+
await appDb
|
|
557
|
+
.update(metadata)
|
|
558
|
+
.set({
|
|
559
|
+
...metadataValues,
|
|
560
|
+
updatedAt: Date.now(),
|
|
561
|
+
})
|
|
562
|
+
.where(eq(metadata.localId, metadataRecord.localId));
|
|
563
|
+
};
|
|
564
|
+
|
|
565
|
+
const isNode = () => {
|
|
566
|
+
return (typeof process !== 'undefined' &&
|
|
567
|
+
process.versions != null &&
|
|
568
|
+
process.versions.node != null);
|
|
569
|
+
};
|
|
570
|
+
const isBrowser = () => {
|
|
571
|
+
return !isElectron() && typeof document !== 'undefined' && typeof window !== 'undefined';
|
|
572
|
+
};
|
|
573
|
+
const isElectron = () => {
|
|
574
|
+
return typeof process !== 'undefined' && process.versions != null && process.versions.electron != null;
|
|
575
|
+
};
|
|
576
|
+
/**
|
|
577
|
+
* Detects if code is running in an Electron renderer process.
|
|
578
|
+
*
|
|
579
|
+
* Uses multiple detection methods for robustness:
|
|
580
|
+
* 1. Checks process.type === 'renderer' (most reliable)
|
|
581
|
+
* 2. Falls back to checking if Electron is present and window/document exist
|
|
582
|
+
*
|
|
583
|
+
* @returns true if running in Electron renderer process, false otherwise
|
|
584
|
+
*/
|
|
585
|
+
const isElectronRenderer = () => {
|
|
586
|
+
// Method 1: Check process.type (most reliable - official Electron API)
|
|
587
|
+
if (typeof process !== 'undefined' &&
|
|
588
|
+
process.type === 'renderer') {
|
|
589
|
+
return true;
|
|
590
|
+
}
|
|
591
|
+
// Method 2: Fallback - Electron + browser-like environment
|
|
592
|
+
// Renderer processes have window/document, main process doesn't (in modern Electron)
|
|
593
|
+
if (isElectron() &&
|
|
594
|
+
typeof window !== 'undefined' &&
|
|
595
|
+
typeof document !== 'undefined') {
|
|
596
|
+
return true;
|
|
597
|
+
}
|
|
598
|
+
return false;
|
|
599
|
+
};
|
|
600
|
+
/**
|
|
601
|
+
* True where OPFS-backed Arweave file downloads should run: normal browsers and
|
|
602
|
+
* Electron renderer. {@link isBrowser} is false for every Electron build (including renderer).
|
|
603
|
+
*/
|
|
604
|
+
const supportsOpfsFileDownloads = () => {
|
|
605
|
+
return isBrowser() || isElectronRenderer();
|
|
606
|
+
};
|
|
607
|
+
|
|
608
|
+
const logger$2 = debug('seedSdk:files:download');
|
|
609
|
+
const AUTO_BULK_DOWNLOAD_THROTTLE_MS = 10000;
|
|
610
|
+
/** Avoid refetch-on-every-call: transient empty indexer responses were overwriting good cache (see trailing throttle ~10s after success). */
|
|
611
|
+
const GET_FILES_METADATA_STALE_MS = 120000;
|
|
612
|
+
let bulkDownloadInFlight = null;
|
|
613
|
+
const lazyDownloadInFlightByTransactionId = new Map();
|
|
614
|
+
// syncDbFiles helper - internal service removed, functionality moved here
|
|
615
|
+
const syncDbFiles = async (endpoints) => {
|
|
616
|
+
// TODO: Implement syncDbFiles functionality if needed
|
|
617
|
+
// This was previously in @/services/internal/helpers
|
|
618
|
+
logger$2('[download] syncDbFiles called but not yet implemented');
|
|
619
|
+
return Promise.resolve();
|
|
620
|
+
};
|
|
621
|
+
const getAppDbReady = async () => {
|
|
622
|
+
if (!BaseDb.isAppDbReady()) {
|
|
623
|
+
// Wait for ClientManager to be ready (DB_INIT state or later)
|
|
624
|
+
const _mod_0 = await import('./ClientManager-Y5EWQuJj.js');
|
|
625
|
+
const _ns_0 = _mod_0.ai;
|
|
626
|
+
const clientManagerMod = _ns_0;
|
|
627
|
+
const { getClient } = clientManagerMod;
|
|
628
|
+
const clientManager = getClient();
|
|
629
|
+
const clientService = clientManager.getService();
|
|
630
|
+
await waitFor(clientService, (snapshot) => {
|
|
631
|
+
const state = snapshot.value;
|
|
632
|
+
return state === 'dbInit' ||
|
|
633
|
+
state === 'saveConfig' ||
|
|
634
|
+
state === 'processSchemaFiles' ||
|
|
635
|
+
state === 'addModelsToStore' ||
|
|
636
|
+
state === 'addModelsToDb' ||
|
|
637
|
+
state === 'idle';
|
|
638
|
+
}, { timeout: 30000 });
|
|
639
|
+
}
|
|
640
|
+
return BaseDb.getAppDb();
|
|
641
|
+
};
|
|
642
|
+
const getExcludedTransactions = async () => {
|
|
643
|
+
const appDb = await getAppDbReady();
|
|
644
|
+
if (!appDb) {
|
|
645
|
+
return new Set();
|
|
646
|
+
}
|
|
647
|
+
const excludedTransactionsQuery = await appDb
|
|
648
|
+
.select()
|
|
649
|
+
.from(appState)
|
|
650
|
+
.where(eq(appState.key, 'excludedTransactions'));
|
|
651
|
+
const excludedTransactions = new Set();
|
|
652
|
+
if (excludedTransactionsQuery && excludedTransactionsQuery.length === 1) {
|
|
653
|
+
const valueString = excludedTransactionsQuery[0].value;
|
|
654
|
+
if (valueString) {
|
|
655
|
+
const excludedTransactionsArray = JSON.parse(valueString);
|
|
656
|
+
for (const txId of excludedTransactionsArray) {
|
|
657
|
+
excludedTransactions.add(txId);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
return excludedTransactions;
|
|
662
|
+
};
|
|
663
|
+
/** Arweave transaction ids are 43 chars in the URL-safe base64 alphabet. */
|
|
664
|
+
const ARWEAVE_TX_ID_RE$1 = /^[a-z0-9_-]{43}$/i;
|
|
665
|
+
const normalizeArweaveTxId = (raw) => {
|
|
666
|
+
if (raw == null || raw === '')
|
|
667
|
+
return undefined;
|
|
668
|
+
const t = String(raw).trim();
|
|
669
|
+
if (ARWEAVE_TX_ID_RE$1.test(t))
|
|
670
|
+
return t;
|
|
671
|
+
const m = t.match(/^([a-z0-9_-]{43})(?:\.[^/]+)?$/i);
|
|
672
|
+
if (m)
|
|
673
|
+
return m[1];
|
|
674
|
+
// Values may be full gateway URLs or paths; scan for the embedded tx id.
|
|
675
|
+
const embedded = t.match(/[a-z0-9_-]{43}/gi);
|
|
676
|
+
if (embedded) {
|
|
677
|
+
for (const candidate of embedded) {
|
|
678
|
+
if (ARWEAVE_TX_ID_RE$1.test(candidate))
|
|
679
|
+
return candidate;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
return undefined;
|
|
683
|
+
};
|
|
684
|
+
const mergeAddressLists = (a, b) => {
|
|
685
|
+
const seen = new Set();
|
|
686
|
+
const out = [];
|
|
687
|
+
for (const addr of [...(a ?? []), ...(b ?? [])]) {
|
|
688
|
+
if (!addr || !addr.trim())
|
|
689
|
+
continue;
|
|
690
|
+
const k = addr.toLowerCase();
|
|
691
|
+
if (seen.has(k))
|
|
692
|
+
continue;
|
|
693
|
+
seen.add(k);
|
|
694
|
+
out.push(addr);
|
|
695
|
+
}
|
|
696
|
+
return out;
|
|
697
|
+
};
|
|
698
|
+
const collectTransactionIdsFromLocalMetadata = async () => {
|
|
699
|
+
const appDb = await getAppDbReady();
|
|
700
|
+
if (!appDb) {
|
|
701
|
+
return [];
|
|
702
|
+
}
|
|
703
|
+
const nameOr = or(eq(metadata.propertyName, 'storageTransactionId'), eq(metadata.propertyName, 'storage_transaction_id'), eq(metadata.propertyName, 'transactionId'));
|
|
704
|
+
const propRows = await appDb
|
|
705
|
+
.select({ propertyValue: metadata.propertyValue })
|
|
706
|
+
.from(metadata)
|
|
707
|
+
.where(and(nameOr, isNotNull(metadata.propertyValue)));
|
|
708
|
+
const refRows = await appDb
|
|
709
|
+
.select({ refResolvedValue: metadata.refResolvedValue })
|
|
710
|
+
.from(metadata)
|
|
711
|
+
.where(isNotNull(metadata.refResolvedValue));
|
|
712
|
+
const ids = new Set();
|
|
713
|
+
for (const row of propRows) {
|
|
714
|
+
const id = normalizeArweaveTxId(row.propertyValue ?? undefined);
|
|
715
|
+
if (id)
|
|
716
|
+
ids.add(id);
|
|
717
|
+
}
|
|
718
|
+
for (const row of refRows) {
|
|
719
|
+
const id = normalizeArweaveTxId(row.refResolvedValue ?? undefined);
|
|
720
|
+
if (id)
|
|
721
|
+
ids.add(id);
|
|
722
|
+
}
|
|
723
|
+
return Array.from(ids);
|
|
724
|
+
};
|
|
725
|
+
const parseTransactionIdsFromMetadata = (filesMetadata, excludedTransactions) => {
|
|
726
|
+
const transactionIds = [];
|
|
727
|
+
for (const fileMetadata of filesMetadata) {
|
|
728
|
+
// Validate and parse decodedDataJson
|
|
729
|
+
if (!fileMetadata.decodedDataJson || fileMetadata.decodedDataJson.trim() === '') {
|
|
730
|
+
console.warn('[events/files] [download] empty decodedDataJson for fileMetadata: ', fileMetadata.id);
|
|
731
|
+
continue;
|
|
732
|
+
}
|
|
733
|
+
let json;
|
|
734
|
+
try {
|
|
735
|
+
json = JSON.parse(fileMetadata.decodedDataJson);
|
|
736
|
+
}
|
|
737
|
+
catch (error) {
|
|
738
|
+
console.warn('[events/files] [download] failed to parse decodedDataJson for fileMetadata: ', fileMetadata.id, error);
|
|
739
|
+
continue;
|
|
740
|
+
}
|
|
741
|
+
if (!Array.isArray(json) || json.length === 0 || !json[0]?.value?.value) {
|
|
742
|
+
console.warn('[events/files] [download] invalid decodedDataJson structure for fileMetadata: ', fileMetadata.id);
|
|
743
|
+
continue;
|
|
744
|
+
}
|
|
745
|
+
const rawTx = json[0].value.value;
|
|
746
|
+
if (typeof rawTx !== 'string' || rawTx.trim().length === 0) {
|
|
747
|
+
continue;
|
|
748
|
+
}
|
|
749
|
+
const transactionId = normalizeArweaveTxId(rawTx.trim());
|
|
750
|
+
if (!transactionId) {
|
|
751
|
+
continue;
|
|
752
|
+
}
|
|
753
|
+
if (excludedTransactions.has(transactionId) || excludedTransactions.has(rawTx.trim())) {
|
|
754
|
+
continue;
|
|
755
|
+
}
|
|
756
|
+
transactionIds.push(transactionId);
|
|
757
|
+
}
|
|
758
|
+
return transactionIds;
|
|
759
|
+
};
|
|
760
|
+
const downloadTransactionIds = async (rawTransactionIds, options) => {
|
|
761
|
+
if (!supportsOpfsFileDownloads()) {
|
|
762
|
+
return false;
|
|
763
|
+
}
|
|
764
|
+
const transactionIds = Array.from(new Set(rawTransactionIds.filter((tx) => !!tx && tx.trim().length > 0)));
|
|
765
|
+
if (transactionIds.length === 0) {
|
|
766
|
+
return false;
|
|
767
|
+
}
|
|
768
|
+
const excludedTransactions = await getExcludedTransactions();
|
|
769
|
+
const queryClient = BaseQueryClient.getQueryClient();
|
|
770
|
+
const transactionIdsToDownload = [];
|
|
771
|
+
await ensureReadGatewaySelected();
|
|
772
|
+
for (const transactionId of transactionIds) {
|
|
773
|
+
if (excludedTransactions.has(transactionId)) {
|
|
774
|
+
continue;
|
|
775
|
+
}
|
|
776
|
+
try {
|
|
777
|
+
// Browser: do not call getTransactionStatus() — many gateways (e.g. g8way.io) redirect or omit
|
|
778
|
+
// CORS headers, so fetch() fails and we would wrongly exclude txs. The OPFS worker fetch decides success.
|
|
779
|
+
let tags = [];
|
|
780
|
+
try {
|
|
781
|
+
tags = await queryClient.fetchQuery({
|
|
782
|
+
queryKey: ['getTransactionTags', transactionId],
|
|
783
|
+
queryFn: async () => BaseArweaveClient.getTransactionTags(transactionId),
|
|
784
|
+
});
|
|
785
|
+
}
|
|
786
|
+
catch {
|
|
787
|
+
tags = [];
|
|
788
|
+
}
|
|
789
|
+
if (tags && tags.length > 0) {
|
|
790
|
+
for (const { name, value } of tags) {
|
|
791
|
+
if (name !== 'Content-SHA-256')
|
|
792
|
+
continue;
|
|
793
|
+
const metadataRecord = await getMetadata({
|
|
794
|
+
storageTransactionId: transactionId,
|
|
795
|
+
});
|
|
796
|
+
if (metadataRecord) {
|
|
797
|
+
await saveMetadata(metadataRecord, {
|
|
798
|
+
contentHash: value,
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
transactionIdsToDownload.push(transactionId);
|
|
804
|
+
}
|
|
805
|
+
catch (error) {
|
|
806
|
+
logger$2(error);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
if (transactionIdsToDownload.length === 0) {
|
|
810
|
+
return false;
|
|
811
|
+
}
|
|
812
|
+
if (transactionIdsToDownload.length === 1) {
|
|
813
|
+
await BaseFileManager.downloadFileByTransactionId({
|
|
814
|
+
transactionId: transactionIdsToDownload[0],
|
|
815
|
+
arweaveHost: BaseArweaveClient.getBaseUrl(),
|
|
816
|
+
excludedTransactions,
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
else {
|
|
820
|
+
await BaseFileManager.downloadAllFiles({
|
|
821
|
+
transactionIds: transactionIdsToDownload,
|
|
822
|
+
arweaveHost: BaseArweaveClient.getBaseUrl(),
|
|
823
|
+
excludedTransactions,
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
|
+
if (options?.resizeAllImages !== false) {
|
|
827
|
+
await BaseFileManager.resizeAllImages({
|
|
828
|
+
width: 480,
|
|
829
|
+
height: 480,
|
|
830
|
+
});
|
|
831
|
+
}
|
|
832
|
+
return true;
|
|
833
|
+
};
|
|
834
|
+
const runBulkDownloadWithDedupe = async (transactionIds) => {
|
|
835
|
+
if (bulkDownloadInFlight) {
|
|
836
|
+
return bulkDownloadInFlight;
|
|
837
|
+
}
|
|
838
|
+
bulkDownloadInFlight = (async () => {
|
|
839
|
+
try {
|
|
840
|
+
await downloadTransactionIds(transactionIds, { resizeAllImages: true });
|
|
841
|
+
}
|
|
842
|
+
finally {
|
|
843
|
+
bulkDownloadInFlight = null;
|
|
844
|
+
}
|
|
845
|
+
})();
|
|
846
|
+
return bulkDownloadInFlight;
|
|
847
|
+
};
|
|
848
|
+
const downloadAllFilesRequestHandler = async ({ endpoints, eventId, }) => {
|
|
849
|
+
if (!supportsOpfsFileDownloads()) {
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
await syncDbFiles();
|
|
853
|
+
eventEmitter.emit('fs.downloadAll.success', { eventId });
|
|
854
|
+
eventEmitter.emit('fs.downloadAllBinary.request', { endpoints });
|
|
855
|
+
};
|
|
856
|
+
const downloadAllFilesBinaryRequestHandler = async (addressesHintOrPayload) => {
|
|
857
|
+
if (!supportsOpfsFileDownloads()) {
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
860
|
+
const addressesHint = Array.isArray(addressesHintOrPayload)
|
|
861
|
+
? addressesHintOrPayload
|
|
862
|
+
: undefined;
|
|
863
|
+
let fromDb = [];
|
|
864
|
+
if (BaseDb.isAppDbReady()) {
|
|
865
|
+
fromDb = await getAllAddressesFromDb();
|
|
866
|
+
}
|
|
867
|
+
if (!BaseDb.isAppDbReady()) {
|
|
868
|
+
await getAppDbReady();
|
|
869
|
+
fromDb = await getAllAddressesFromDb();
|
|
870
|
+
}
|
|
871
|
+
const addresses = mergeAddressLists(addressesHint, fromDb);
|
|
872
|
+
if (!addresses || addresses.length === 0) {
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
875
|
+
const queryClient = BaseQueryClient.getQueryClient();
|
|
876
|
+
const easClient = BaseEasClient.getEasClient();
|
|
877
|
+
const filesMetadataQueryKey = ['getFilesMetadata', ...addresses];
|
|
878
|
+
const fetchFilesMetadata = () => easClient.request(GET_FILES_METADATA, {
|
|
879
|
+
where: {
|
|
880
|
+
attester: {
|
|
881
|
+
in: addresses,
|
|
882
|
+
},
|
|
883
|
+
schema: {
|
|
884
|
+
is: {
|
|
885
|
+
id: {
|
|
886
|
+
equals: '0x55fdefb36fcbbaebeb7d6b41dc3a1a9666e4e42154267c889de064faa7ede517',
|
|
887
|
+
},
|
|
888
|
+
},
|
|
889
|
+
},
|
|
890
|
+
},
|
|
891
|
+
});
|
|
892
|
+
let { filesMetadata } = await queryClient.fetchQuery({
|
|
893
|
+
queryKey: filesMetadataQueryKey,
|
|
894
|
+
queryFn: fetchFilesMetadata,
|
|
895
|
+
staleTime: GET_FILES_METADATA_STALE_MS,
|
|
896
|
+
});
|
|
897
|
+
if ((!Array.isArray(filesMetadata) || filesMetadata.length === 0) && addresses.length > 0) {
|
|
898
|
+
await queryClient.removeQueries({ queryKey: filesMetadataQueryKey });
|
|
899
|
+
({ filesMetadata } = await queryClient.fetchQuery({
|
|
900
|
+
queryKey: filesMetadataQueryKey,
|
|
901
|
+
queryFn: fetchFilesMetadata,
|
|
902
|
+
staleTime: GET_FILES_METADATA_STALE_MS,
|
|
903
|
+
}));
|
|
904
|
+
}
|
|
905
|
+
const filesRoot = BaseFileManager.getWorkingDir();
|
|
906
|
+
await BaseFileManager.createDirIfNotExists(filesRoot);
|
|
907
|
+
await BaseFileManager.createDirIfNotExists(BaseFileManager.getFilesPath('html'));
|
|
908
|
+
await BaseFileManager.createDirIfNotExists(BaseFileManager.getFilesPath('json'));
|
|
909
|
+
await BaseFileManager.createDirIfNotExists(BaseFileManager.getFilesPath('images'));
|
|
910
|
+
const excludedTransactions = await getExcludedTransactions();
|
|
911
|
+
const fromEas = parseTransactionIdsFromMetadata(filesMetadata ?? [], excludedTransactions);
|
|
912
|
+
const fromLocal = await collectTransactionIdsFromLocalMetadata();
|
|
913
|
+
const transactionIds = Array.from(new Set([...fromEas, ...fromLocal]));
|
|
914
|
+
await runBulkDownloadWithDedupe(transactionIds);
|
|
915
|
+
};
|
|
916
|
+
const downloadTransactionIdWithDedupe = async (transactionId) => {
|
|
917
|
+
if (!supportsOpfsFileDownloads() || !transactionId || transactionId.trim().length === 0) {
|
|
918
|
+
return false;
|
|
919
|
+
}
|
|
920
|
+
const normalizedTxId = normalizeArweaveTxId(transactionId) ?? transactionId.trim();
|
|
921
|
+
const existing = lazyDownloadInFlightByTransactionId.get(normalizedTxId);
|
|
922
|
+
if (existing) {
|
|
923
|
+
return existing;
|
|
924
|
+
}
|
|
925
|
+
const inFlight = (async () => {
|
|
926
|
+
try {
|
|
927
|
+
const ok = await downloadTransactionIds([normalizedTxId], { resizeAllImages: false });
|
|
928
|
+
return ok;
|
|
929
|
+
}
|
|
930
|
+
finally {
|
|
931
|
+
lazyDownloadInFlightByTransactionId.delete(normalizedTxId);
|
|
932
|
+
}
|
|
933
|
+
})();
|
|
934
|
+
lazyDownloadInFlightByTransactionId.set(normalizedTxId, inFlight);
|
|
935
|
+
return inFlight;
|
|
936
|
+
};
|
|
937
|
+
const scheduleBulkFilesDownloadFromEasSyncThrottled = throttle((addressesHint) => {
|
|
938
|
+
void downloadAllFilesBinaryRequestHandler(addressesHint).catch((error) => {
|
|
939
|
+
logger$2('[scheduleBulkFilesDownloadFromEasSync] failed', error);
|
|
940
|
+
});
|
|
941
|
+
}, AUTO_BULK_DOWNLOAD_THROTTLE_MS, { leading: true, trailing: false });
|
|
942
|
+
const scheduleBulkFilesDownloadFromEasSync = (addressesHint) => {
|
|
943
|
+
if (!supportsOpfsFileDownloads()) {
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
946
|
+
scheduleBulkFilesDownloadFromEasSyncThrottled(addressesHint);
|
|
947
|
+
};
|
|
948
|
+
|
|
949
|
+
var download = /*#__PURE__*/Object.freeze({
|
|
950
|
+
__proto__: null,
|
|
951
|
+
downloadAllFilesBinaryRequestHandler: downloadAllFilesBinaryRequestHandler,
|
|
952
|
+
downloadAllFilesRequestHandler: downloadAllFilesRequestHandler,
|
|
953
|
+
downloadTransactionIdWithDedupe: downloadTransactionIdWithDedupe,
|
|
954
|
+
scheduleBulkFilesDownloadFromEasSync: scheduleBulkFilesDownloadFromEasSync
|
|
955
|
+
});
|
|
956
|
+
|
|
957
|
+
const logger$1 = debug('seedSdk:helpers:ensureImageLocal');
|
|
958
|
+
/** Arweave transaction ids are 43 chars in the URL-safe base64 alphabet. */
|
|
959
|
+
const ARWEAVE_TX_ID_RE = /^[a-z0-9_-]{43}$/i;
|
|
960
|
+
const IMAGE_SIZES = [
|
|
961
|
+
ImageSize.EXTRA_SMALL,
|
|
962
|
+
ImageSize.SMALL,
|
|
963
|
+
ImageSize.MEDIUM,
|
|
964
|
+
ImageSize.LARGE,
|
|
965
|
+
ImageSize.EXTRA_LARGE,
|
|
966
|
+
];
|
|
967
|
+
const inFlightByKey = new Map();
|
|
968
|
+
const normalizeArweaveTxIdForEnsure = (raw) => {
|
|
969
|
+
if (raw == null || raw === '')
|
|
970
|
+
return undefined;
|
|
971
|
+
const t = String(raw).trim();
|
|
972
|
+
if (ARWEAVE_TX_ID_RE.test(t))
|
|
973
|
+
return t;
|
|
974
|
+
const m = t.match(/^([a-z0-9_-]{43})(?:\.[^/]+)?$/i);
|
|
975
|
+
if (m)
|
|
976
|
+
return m[1];
|
|
977
|
+
const embedded = t.match(/[a-z0-9_-]{43}/gi);
|
|
978
|
+
if (embedded) {
|
|
979
|
+
for (const candidate of embedded) {
|
|
980
|
+
if (ARWEAVE_TX_ID_RE.test(candidate))
|
|
981
|
+
return candidate;
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
return undefined;
|
|
985
|
+
};
|
|
986
|
+
const closestImageSize = (width) => {
|
|
987
|
+
let best = ImageSize.EXTRA_SMALL;
|
|
988
|
+
let bestDist = Math.abs(best - width);
|
|
989
|
+
for (const size of IMAGE_SIZES) {
|
|
990
|
+
const dist = Math.abs(size - width);
|
|
991
|
+
if (dist < bestDist) {
|
|
992
|
+
best = size;
|
|
993
|
+
bestDist = dist;
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
return best;
|
|
997
|
+
};
|
|
998
|
+
const mapRequestedWidthsToImageSizes = (widths) => {
|
|
999
|
+
const requested = widths && widths.length > 0 ? widths : [ImageSize.EXTRA_SMALL];
|
|
1000
|
+
const unique = new Set();
|
|
1001
|
+
for (const w of requested) {
|
|
1002
|
+
if (typeof w !== 'number' || !Number.isFinite(w) || w <= 0)
|
|
1003
|
+
continue;
|
|
1004
|
+
unique.add(closestImageSize(w));
|
|
1005
|
+
}
|
|
1006
|
+
if (unique.size === 0) {
|
|
1007
|
+
unique.add(ImageSize.EXTRA_SMALL);
|
|
1008
|
+
}
|
|
1009
|
+
return Array.from(unique).sort((a, b) => a - b);
|
|
1010
|
+
};
|
|
1011
|
+
const getBasename = (filePath) => {
|
|
1012
|
+
const path = BaseFileManager.getPathModule();
|
|
1013
|
+
return path.basename(filePath);
|
|
1014
|
+
};
|
|
1015
|
+
const getBasenameWithoutExtension = (fileName) => {
|
|
1016
|
+
const match = fileName.match(/^(.*[\/\\])?([^\/\\]+?)(\.[^.\/\\]*)?$/);
|
|
1017
|
+
return match?.[2] ?? fileName;
|
|
1018
|
+
};
|
|
1019
|
+
const resolveMatchingImagePath = async (needle) => {
|
|
1020
|
+
const dirPath = BaseFileManager.getFilesPath('images');
|
|
1021
|
+
const dirExists = await BaseFileManager.pathExists(dirPath);
|
|
1022
|
+
if (!dirExists)
|
|
1023
|
+
return undefined;
|
|
1024
|
+
const fs = await BaseFileManager.getFs();
|
|
1025
|
+
const path = BaseFileManager.getPathModule();
|
|
1026
|
+
const files = await fs.promises.readdir(dirPath);
|
|
1027
|
+
// Prefer exact basename match (files only); skip size dirs.
|
|
1028
|
+
const exact = files.find((file) => path.basename(file) === needle);
|
|
1029
|
+
if (exact) {
|
|
1030
|
+
const candidate = BaseFileManager.getFilesPath('images', exact);
|
|
1031
|
+
try {
|
|
1032
|
+
const zenfs = await BaseFileManager.getFs();
|
|
1033
|
+
const stat = await zenfs.promises.stat(candidate);
|
|
1034
|
+
if (stat.isFile())
|
|
1035
|
+
return candidate;
|
|
1036
|
+
}
|
|
1037
|
+
catch {
|
|
1038
|
+
// fall through
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
const matchingFiles = files.filter((file) => {
|
|
1042
|
+
const base = path.basename(file);
|
|
1043
|
+
return base.includes(needle);
|
|
1044
|
+
});
|
|
1045
|
+
for (const file of matchingFiles) {
|
|
1046
|
+
const candidate = BaseFileManager.getFilesPath('images', file);
|
|
1047
|
+
try {
|
|
1048
|
+
const zenfs = await BaseFileManager.getFs();
|
|
1049
|
+
const stat = await zenfs.promises.stat(candidate);
|
|
1050
|
+
if (stat.isFile())
|
|
1051
|
+
return candidate;
|
|
1052
|
+
}
|
|
1053
|
+
catch {
|
|
1054
|
+
continue;
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
return undefined;
|
|
1058
|
+
};
|
|
1059
|
+
const sizedVariantPath = (originalFilePath, size) => {
|
|
1060
|
+
const path = BaseFileManager.getPathModule();
|
|
1061
|
+
const dir = path.dirname(originalFilePath);
|
|
1062
|
+
const base = getBasenameWithoutExtension(getBasename(originalFilePath));
|
|
1063
|
+
return path.join(dir, String(size), `${base}.webp`);
|
|
1064
|
+
};
|
|
1065
|
+
const ensureKey = (txId, fileName, sizes) => {
|
|
1066
|
+
const idPart = txId ?? fileName ?? '';
|
|
1067
|
+
return `${idPart}|${sizes.join(',')}`;
|
|
1068
|
+
};
|
|
1069
|
+
const runEnsureImageLocal = async (params) => {
|
|
1070
|
+
if (!supportsOpfsFileDownloads()) {
|
|
1071
|
+
return { status: 'unsupported' };
|
|
1072
|
+
}
|
|
1073
|
+
const fileName = params.fileName?.trim() || undefined;
|
|
1074
|
+
const txId = normalizeArweaveTxIdForEnsure(params.transactionId) ??
|
|
1075
|
+
normalizeArweaveTxIdForEnsure(fileName);
|
|
1076
|
+
if (!txId && !fileName) {
|
|
1077
|
+
return { status: 'missing-id' };
|
|
1078
|
+
}
|
|
1079
|
+
const targetSizes = mapRequestedWidthsToImageSizes(params.widths);
|
|
1080
|
+
const needles = [fileName, txId].filter((n) => typeof n === 'string' && n.length > 0);
|
|
1081
|
+
try {
|
|
1082
|
+
let filePath;
|
|
1083
|
+
for (const needle of needles) {
|
|
1084
|
+
filePath = await resolveMatchingImagePath(needle);
|
|
1085
|
+
if (filePath)
|
|
1086
|
+
break;
|
|
1087
|
+
}
|
|
1088
|
+
if (!filePath && txId) {
|
|
1089
|
+
await downloadTransactionIdWithDedupe(txId);
|
|
1090
|
+
for (const needle of needles) {
|
|
1091
|
+
filePath = await resolveMatchingImagePath(needle);
|
|
1092
|
+
if (filePath)
|
|
1093
|
+
break;
|
|
1094
|
+
}
|
|
1095
|
+
// Downloads store as bare tx id
|
|
1096
|
+
if (!filePath) {
|
|
1097
|
+
const direct = BaseFileManager.getFilesPath('images', txId);
|
|
1098
|
+
if (await BaseFileManager.pathExists(direct)) {
|
|
1099
|
+
filePath = direct;
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
if (!filePath) {
|
|
1104
|
+
return { status: 'failed' };
|
|
1105
|
+
}
|
|
1106
|
+
const createdWidths = [];
|
|
1107
|
+
for (const size of targetSizes) {
|
|
1108
|
+
const variantPath = sizedVariantPath(filePath, size);
|
|
1109
|
+
const exists = await BaseFileManager.pathExists(variantPath);
|
|
1110
|
+
if (exists)
|
|
1111
|
+
continue;
|
|
1112
|
+
await BaseFileManager.resizeImage({
|
|
1113
|
+
filePath,
|
|
1114
|
+
width: size,
|
|
1115
|
+
height: size,
|
|
1116
|
+
});
|
|
1117
|
+
createdWidths.push(size);
|
|
1118
|
+
}
|
|
1119
|
+
return {
|
|
1120
|
+
status: 'ready',
|
|
1121
|
+
filePath,
|
|
1122
|
+
createdWidths,
|
|
1123
|
+
};
|
|
1124
|
+
}
|
|
1125
|
+
catch (error) {
|
|
1126
|
+
logger$1('ensureImageLocal failed', error);
|
|
1127
|
+
return { status: 'failed' };
|
|
1128
|
+
}
|
|
1129
|
+
};
|
|
1130
|
+
/**
|
|
1131
|
+
* Ensure an image original exists in OPFS (downloading from Arweave if needed)
|
|
1132
|
+
* and create only the requested ImageSize variant(s) for that one file.
|
|
1133
|
+
*/
|
|
1134
|
+
const ensureImageLocal = async (params) => {
|
|
1135
|
+
if (!supportsOpfsFileDownloads()) {
|
|
1136
|
+
return { status: 'unsupported' };
|
|
1137
|
+
}
|
|
1138
|
+
const fileName = params.fileName?.trim() || undefined;
|
|
1139
|
+
const txId = normalizeArweaveTxIdForEnsure(params.transactionId) ??
|
|
1140
|
+
normalizeArweaveTxIdForEnsure(fileName);
|
|
1141
|
+
if (!txId && !fileName) {
|
|
1142
|
+
return { status: 'missing-id' };
|
|
1143
|
+
}
|
|
1144
|
+
const targetSizes = mapRequestedWidthsToImageSizes(params.widths);
|
|
1145
|
+
const key = ensureKey(txId, fileName, targetSizes);
|
|
1146
|
+
const existing = inFlightByKey.get(key);
|
|
1147
|
+
if (existing) {
|
|
1148
|
+
return existing;
|
|
1149
|
+
}
|
|
1150
|
+
const inFlight = (async () => {
|
|
1151
|
+
try {
|
|
1152
|
+
return await runEnsureImageLocal({
|
|
1153
|
+
transactionId: txId,
|
|
1154
|
+
fileName,
|
|
1155
|
+
widths: targetSizes,
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
1158
|
+
finally {
|
|
1159
|
+
inFlightByKey.delete(key);
|
|
1160
|
+
}
|
|
1161
|
+
})();
|
|
1162
|
+
inFlightByKey.set(key, inFlight);
|
|
1163
|
+
return inFlight;
|
|
1164
|
+
};
|
|
1165
|
+
|
|
1166
|
+
const logger = debug('seedSdk:shared:helpers');
|
|
1167
|
+
const toSnakeCase = (str) => {
|
|
1168
|
+
return str.replace(/([a-z])([A-Z])/g, '$1_$2').toLowerCase();
|
|
1169
|
+
};
|
|
1170
|
+
const getMimeType = (base64) => {
|
|
1171
|
+
if (!base64) {
|
|
1172
|
+
return null;
|
|
1173
|
+
}
|
|
1174
|
+
const result = base64.match(/^data:([a-zA-Z0-9]+\/[a-zA-Z0-9-.+]+).*,/);
|
|
1175
|
+
if (result && result.length > 1) {
|
|
1176
|
+
return result[1];
|
|
1177
|
+
}
|
|
1178
|
+
else {
|
|
1179
|
+
return null; // MIME type could not be determined
|
|
1180
|
+
}
|
|
1181
|
+
};
|
|
1182
|
+
const getCorrectId = (localIdOrUid) => {
|
|
1183
|
+
const id = {
|
|
1184
|
+
localId: undefined,
|
|
1185
|
+
uid: undefined,
|
|
1186
|
+
};
|
|
1187
|
+
if (!localIdOrUid) {
|
|
1188
|
+
return id;
|
|
1189
|
+
}
|
|
1190
|
+
if (localIdOrUid.length === 10) {
|
|
1191
|
+
id.localId = localIdOrUid;
|
|
1192
|
+
}
|
|
1193
|
+
if (localIdOrUid.startsWith('0x') && localIdOrUid.length === 66) {
|
|
1194
|
+
id.uid = localIdOrUid;
|
|
1195
|
+
}
|
|
1196
|
+
return id;
|
|
1197
|
+
};
|
|
1198
|
+
const getDataTypeFromString = (data) => {
|
|
1199
|
+
const nonImageBase64Regex = /^(?!data:image\/(?:jpeg|png|gif|bmp|webp);base64,)[A-Za-z0-9+/=]+$/;
|
|
1200
|
+
if (nonImageBase64Regex.test(data)) {
|
|
1201
|
+
return 'base64';
|
|
1202
|
+
}
|
|
1203
|
+
// Regular expression for base64 (simple version, checking for base64 format)
|
|
1204
|
+
const imageBase64Regex = /^data:image\/[a-zA-Z]+;base64,[A-Za-z0-9+/]+={0,2}$/;
|
|
1205
|
+
if (imageBase64Regex.test(data)) {
|
|
1206
|
+
return 'imageBase64';
|
|
1207
|
+
}
|
|
1208
|
+
// Blob URLs are fetchable but session-scoped; recognize them as url type
|
|
1209
|
+
if (data.startsWith('blob:')) {
|
|
1210
|
+
return 'url';
|
|
1211
|
+
}
|
|
1212
|
+
// Regular expression for URL (simple version, checking for common URL format)
|
|
1213
|
+
const urlRegex = /^(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?$/;
|
|
1214
|
+
if (urlRegex.test(data)) {
|
|
1215
|
+
return 'url';
|
|
1216
|
+
}
|
|
1217
|
+
return null;
|
|
1218
|
+
};
|
|
1219
|
+
const convertTxIdToImage = async (txId) => {
|
|
1220
|
+
const imageFilePath = BaseFileManager.getFilesPath('images', txId);
|
|
1221
|
+
const fileExists = await BaseFileManager.pathExists(imageFilePath);
|
|
1222
|
+
if (!fileExists) {
|
|
1223
|
+
logger(`[ItemView] [updateImage] ${imageFilePath} does not exist`);
|
|
1224
|
+
return;
|
|
1225
|
+
}
|
|
1226
|
+
const buffer = await BaseFileManager.readFileAsBuffer(imageFilePath);
|
|
1227
|
+
// Convert Blob to ArrayBuffer if needed, or use Buffer directly
|
|
1228
|
+
let arrayBuffer;
|
|
1229
|
+
if (buffer instanceof Blob) {
|
|
1230
|
+
arrayBuffer = await buffer.arrayBuffer();
|
|
1231
|
+
}
|
|
1232
|
+
else if (buffer instanceof ArrayBuffer) {
|
|
1233
|
+
arrayBuffer = buffer;
|
|
1234
|
+
}
|
|
1235
|
+
else {
|
|
1236
|
+
// Node.js Buffer
|
|
1237
|
+
const sliced = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
|
|
1238
|
+
// Handle SharedArrayBuffer case by creating a new ArrayBuffer
|
|
1239
|
+
if (sliced instanceof SharedArrayBuffer) {
|
|
1240
|
+
const uint8Array = new Uint8Array(sliced);
|
|
1241
|
+
arrayBuffer = uint8Array.buffer.slice(0);
|
|
1242
|
+
}
|
|
1243
|
+
else {
|
|
1244
|
+
arrayBuffer = sliced;
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
const uint = new Uint8Array(arrayBuffer);
|
|
1248
|
+
const imageBlob = new Blob([uint]);
|
|
1249
|
+
return URL.createObjectURL(imageBlob);
|
|
1250
|
+
};
|
|
1251
|
+
/**
|
|
1252
|
+
* Constructs an Arweave URL for a storage transaction ID
|
|
1253
|
+
* @param storageTransactionId - The Arweave transaction ID
|
|
1254
|
+
* @returns The full URL to access the transaction data on Arweave (e.g., https://arweave.net/raw/{transactionId})
|
|
1255
|
+
* @deprecated Use BaseArweaveClient.getRawUrl() instead for better consistency and testability
|
|
1256
|
+
*/
|
|
1257
|
+
const getArweaveUrlForTransaction = (storageTransactionId) => {
|
|
1258
|
+
return BaseArweaveClient.getRawUrl(storageTransactionId);
|
|
1259
|
+
};
|
|
1260
|
+
|
|
1261
|
+
const ENV = (typeof process !== 'undefined' && process.env)
|
|
1262
|
+
? process.env
|
|
1263
|
+
: (typeof window !== 'undefined' && window.env)
|
|
1264
|
+
? window.env
|
|
1265
|
+
: {};
|
|
1266
|
+
var MachineIds;
|
|
1267
|
+
(function (MachineIds) {
|
|
1268
|
+
MachineIds["CLIENT_MANAGER"] = "@seedSdk/clientManager";
|
|
1269
|
+
MachineIds["EAS_SYNC_MANAGER"] = "@seedSdk/easSyncManager";
|
|
1270
|
+
MachineIds["GLOBAL"] = "@seedSdk/global";
|
|
1271
|
+
MachineIds["INTERNAL"] = "@seedSdk/internal";
|
|
1272
|
+
MachineIds["EAS"] = "@seedSdk/eas";
|
|
1273
|
+
MachineIds["ITEM"] = "@seedSdk/item";
|
|
1274
|
+
MachineIds["ALL_ITEMS"] = "@seedSdk/allItems";
|
|
1275
|
+
MachineIds["MODEL"] = "@seedSdk/model";
|
|
1276
|
+
MachineIds["FILE_SYSTEM"] = "@seedSdk/fileSystem";
|
|
1277
|
+
})(MachineIds || (MachineIds = {}));
|
|
1278
|
+
const { INTERNAL, GLOBAL, EAS, MODEL } = MachineIds;
|
|
1279
|
+
var ClientManagerState;
|
|
1280
|
+
(function (ClientManagerState) {
|
|
1281
|
+
ClientManagerState["UNINITIALIZED"] = "uninitialized";
|
|
1282
|
+
ClientManagerState["PLATFORM_CLASSES_INIT"] = "platformClassesInit";
|
|
1283
|
+
ClientManagerState["FILE_SYSTEM_INIT"] = "fileSystemInit";
|
|
1284
|
+
ClientManagerState["DB_INIT"] = "dbInit";
|
|
1285
|
+
ClientManagerState["SAVE_CONFIG"] = "saveConfig";
|
|
1286
|
+
ClientManagerState["ADD_MODELS_TO_STORE"] = "addModelsToStore";
|
|
1287
|
+
ClientManagerState["ADD_MODELS_TO_DB"] = "addModelsToDb";
|
|
1288
|
+
ClientManagerState["PROCESS_SCHEMA_FILES"] = "processSchemaFiles";
|
|
1289
|
+
ClientManagerState["IDLE"] = "idle";
|
|
1290
|
+
ClientManagerState["INIT_FAILED"] = "initFailed";
|
|
1291
|
+
})(ClientManagerState || (ClientManagerState = {}));
|
|
1292
|
+
var ClientManagerEvents;
|
|
1293
|
+
(function (ClientManagerEvents) {
|
|
1294
|
+
ClientManagerEvents["UPDATE_CONTEXT"] = "updateContext";
|
|
1295
|
+
ClientManagerEvents["PLATFORM_CLASSES_READY"] = "platformClassesReady";
|
|
1296
|
+
ClientManagerEvents["FILE_SYSTEM_READY"] = "fileSystemReady";
|
|
1297
|
+
ClientManagerEvents["DB_READY"] = "dbReady";
|
|
1298
|
+
ClientManagerEvents["SAVE_CONFIG_SUCCESS"] = "saveConfigSuccess";
|
|
1299
|
+
ClientManagerEvents["SAVE_APP_STATE_SUCCESS"] = "saveAppStateSuccess";
|
|
1300
|
+
ClientManagerEvents["SAVE_APP_STATE_ERROR"] = "saveAppStateError";
|
|
1301
|
+
ClientManagerEvents["SET_ADDRESSES"] = "setAddresses";
|
|
1302
|
+
ClientManagerEvents["ADD_MODELS_TO_STORE_SUCCESS"] = "addModelsToStoreSuccess";
|
|
1303
|
+
ClientManagerEvents["ADD_MODELS_TO_DB_SUCCESS"] = "addModelsToDbSuccess";
|
|
1304
|
+
ClientManagerEvents["PROCESS_SCHEMA_FILES_SUCCESS"] = "processSchemaFilesSuccess";
|
|
1305
|
+
})(ClientManagerEvents || (ClientManagerEvents = {}));
|
|
1306
|
+
var GlobalState;
|
|
1307
|
+
(function (GlobalState) {
|
|
1308
|
+
GlobalState["UNINITIALIZED"] = "uninitialized";
|
|
1309
|
+
GlobalState["INITIALIZING"] = "initializing";
|
|
1310
|
+
GlobalState["INITIALIZED"] = "initialized";
|
|
1311
|
+
GlobalState["PUBLISHING_ITEM"] = "publishingItem";
|
|
1312
|
+
GlobalState["ADDING_MODELS_TO_DB"] = "addingModelsToDb";
|
|
1313
|
+
})(GlobalState || (GlobalState = {}));
|
|
1314
|
+
const { UNINITIALIZED: GLOBAL_UNINITIALIZED, INITIALIZING, INITIALIZED, PUBLISHING_ITEM, ADDING_MODELS_TO_DB, } = GlobalState;
|
|
1315
|
+
var InternalState;
|
|
1316
|
+
(function (InternalState) {
|
|
1317
|
+
InternalState["IDLE"] = "idle";
|
|
1318
|
+
InternalState["INITIALIZING"] = "initializing";
|
|
1319
|
+
InternalState["VALIDATING_INPUT"] = "validatingInput";
|
|
1320
|
+
InternalState["CONFIGURING_FS"] = "configuringFs";
|
|
1321
|
+
InternalState["LOADING_SEED_DB"] = "loadingSeedDb";
|
|
1322
|
+
InternalState["SAVING_CONFIG"] = "savingConfig";
|
|
1323
|
+
InternalState["LOADING_APP_DB"] = "loadingAppDb";
|
|
1324
|
+
InternalState["LOADING_SDK_DB"] = "loadingSdkDb";
|
|
1325
|
+
})(InternalState || (InternalState = {}));
|
|
1326
|
+
const { VALIDATING_INPUT, CONFIGURING_FS, LOADING_SEED_DB, LOADING_APP_DB } = InternalState;
|
|
1327
|
+
var PublishMachineStates;
|
|
1328
|
+
(function (PublishMachineStates) {
|
|
1329
|
+
PublishMachineStates["VALIDATING_ITEM_DATA"] = "validatingItemData";
|
|
1330
|
+
PublishMachineStates["CREATING_PUBLISH_ATTEMPT"] = "creatingPublishAttempt";
|
|
1331
|
+
PublishMachineStates["UPLOADING"] = "uploading";
|
|
1332
|
+
PublishMachineStates["PREPARING_PUBLISH_REQUEST_DATA"] = "preparingPublishRequestData";
|
|
1333
|
+
PublishMachineStates["PUBLISHING"] = "publishing";
|
|
1334
|
+
PublishMachineStates["IDLE"] = "idle";
|
|
1335
|
+
})(PublishMachineStates || (PublishMachineStates = {}));
|
|
1336
|
+
const EAS_ENDPOINT = ENV.NEXT_PUBLIC_EAS_ENDPOINT ||
|
|
1337
|
+
ENV.EAS_ENDPOINT ||
|
|
1338
|
+
'https://optimism-sepolia.easscan.org/graphql';
|
|
1339
|
+
/**
|
|
1340
|
+
* @deprecated Use BaseArweaveClient.getHost() instead.
|
|
1341
|
+
* This constant is kept for backward compatibility but will be removed in a future version.
|
|
1342
|
+
* Migration: import { BaseArweaveClient } from '@/helpers' and use BaseArweaveClient.getHost()
|
|
1343
|
+
*/
|
|
1344
|
+
ENV.NEXT_PUBLIC_ARWEAVE_HOST || 'ar.seedprotocol.io';
|
|
1345
|
+
|
|
1346
|
+
export { getStorageTransactionIdForSeedUid as A, Boolean as B, ClientManagerState as C, DEFAULT_TEXT_MAX_LENGTH as D, EAS_ENDPOINT as E, File as F, convertTxIdToImage as G, Html as H, Image as I, Json as J, downloadTransactionIdWithDedupe as K, List as L, ModelPropertyDataTypes as M, Number$1 as N, getDataTypeFromString as O, Property as P, getMimeType as Q, Relation as R, isBrowser as S, THtmlEmbeddedDataUriPolicy as T, isNode as U, downloadAllFilesRequestHandler as V, downloadAllFilesBinaryRequestHandler as W, ClientManagerEvents as X, download as Y, Date$1 as a, PropertyConstructor as b, PropertyMetadataKey as c, TProperty as d, TPropertyConstructor as e, TPropertyDataType as f, TPropertyDefs as g, TStorageType as h, TValidationRules as i, Text as j, normalizeRelationPropertyValue as k, getCorrectId as l, eventEmitter as m, normalizeDataType as n, getArweaveUrlForTransaction as o, MachineIds as p, classifyMediaRef as q, resolveSeedIdsFromRefString as r, scheduleBulkFilesDownloadFromEasSync as s, toSnakeCase as t, closestImageSize as u, ensureImageLocal as v, getFeedItemStringField as w, normalizeArweaveTxIdForEnsure as x, normalizeFeedItemFields as y, resolveMediaRef as z };
|
|
1347
|
+
//# sourceMappingURL=constants-DSqHBPNe.js.map
|