@nibgate/sdk 0.2.9 → 0.2.11
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/nibgate.js +1456 -922
- package/dist/nibgate.js.map +4 -4
- package/dist/nibgate.min.js +67 -2
- package/dist/nibgate.min.js.map +4 -4
- package/package.json +1 -1
- package/src/browser/default-ui.js +418 -0
- package/src/browser/index.js +1 -0
- package/src/browser/reputation.js +2 -2
- package/src/core/resource.js +0 -1
- package/src/index.d.ts +1 -1
- package/src/index.js +1 -0
- package/src/server/gateway.js +9 -0
package/dist/nibgate.js
CHANGED
|
@@ -20,317 +20,21 @@ var Nibgate = (() => {
|
|
|
20
20
|
};
|
|
21
21
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
22
22
|
|
|
23
|
-
// src/browser/json.js
|
|
24
|
-
function jsonReplacer(_key, value) {
|
|
25
|
-
if (typeof value === "bigint") return value.toString();
|
|
26
|
-
return value;
|
|
27
|
-
}
|
|
28
|
-
function stringifyJson(value) {
|
|
29
|
-
return JSON.stringify(value, jsonReplacer);
|
|
30
|
-
}
|
|
31
|
-
var init_json = __esm({
|
|
32
|
-
"src/browser/json.js"() {
|
|
33
|
-
}
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
// src/browser/schemes/batch-scheme.js
|
|
37
|
-
function supportsBatching(requirements) {
|
|
38
|
-
const extra = requirements.extra;
|
|
39
|
-
if (!extra) return false;
|
|
40
|
-
return extra.name === CIRCLE_BATCHING_NAME && extra.version === CIRCLE_BATCHING_VERSION;
|
|
41
|
-
}
|
|
42
|
-
function getVerifyingContract(requirements) {
|
|
43
|
-
if (!supportsBatching(requirements)) return void 0;
|
|
44
|
-
return requirements.extra?.verifyingContract;
|
|
45
|
-
}
|
|
46
|
-
function createNonce() {
|
|
47
|
-
const bytes = new Uint8Array(32);
|
|
48
|
-
crypto.getRandomValues(bytes);
|
|
49
|
-
return "0x" + Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
50
|
-
}
|
|
51
|
-
function getAddress(value) {
|
|
52
|
-
return value && typeof value === "string" && value.startsWith("0x") ? value.toLowerCase() : value;
|
|
53
|
-
}
|
|
54
|
-
var CIRCLE_BATCHING_NAME, CIRCLE_BATCHING_VERSION, GATEWAY_MIN_AUTH_VALIDITY_SECONDS, GATEWAY_AUTH_VALIDITY_WINDOW_SECONDS, authorizationTypes, BatchEvmScheme;
|
|
55
|
-
var init_batch_scheme = __esm({
|
|
56
|
-
"src/browser/schemes/batch-scheme.js"() {
|
|
57
|
-
CIRCLE_BATCHING_NAME = "GatewayWalletBatched";
|
|
58
|
-
CIRCLE_BATCHING_VERSION = "1";
|
|
59
|
-
GATEWAY_MIN_AUTH_VALIDITY_SECONDS = 7 * 24 * 60 * 60;
|
|
60
|
-
GATEWAY_AUTH_VALIDITY_WINDOW_SECONDS = GATEWAY_MIN_AUTH_VALIDITY_SECONDS + 100;
|
|
61
|
-
authorizationTypes = {
|
|
62
|
-
TransferWithAuthorization: [
|
|
63
|
-
{ name: "from", type: "address" },
|
|
64
|
-
{ name: "to", type: "address" },
|
|
65
|
-
{ name: "value", type: "uint256" },
|
|
66
|
-
{ name: "validAfter", type: "uint256" },
|
|
67
|
-
{ name: "validBefore", type: "uint256" },
|
|
68
|
-
{ name: "nonce", type: "bytes32" }
|
|
69
|
-
]
|
|
70
|
-
};
|
|
71
|
-
BatchEvmScheme = class {
|
|
72
|
-
constructor(signer) {
|
|
73
|
-
this.signer = signer;
|
|
74
|
-
this.scheme = CIRCLE_BATCHING_NAME;
|
|
75
|
-
this._beforeHooks = [];
|
|
76
|
-
this._afterHooks = [];
|
|
77
|
-
this._failureHooks = [];
|
|
78
|
-
}
|
|
79
|
-
onBeforePaymentCreation(hook) {
|
|
80
|
-
this._beforeHooks.push(hook);
|
|
81
|
-
return this;
|
|
82
|
-
}
|
|
83
|
-
onAfterPaymentCreation(hook) {
|
|
84
|
-
this._afterHooks.push(hook);
|
|
85
|
-
return this;
|
|
86
|
-
}
|
|
87
|
-
onPaymentCreationFailure(hook) {
|
|
88
|
-
this._failureHooks.push(hook);
|
|
89
|
-
return this;
|
|
90
|
-
}
|
|
91
|
-
async createPaymentPayload(x402Version, paymentRequirements) {
|
|
92
|
-
const context = {
|
|
93
|
-
paymentRequired: { x402Version, accepts: [paymentRequirements] },
|
|
94
|
-
selectedRequirements: paymentRequirements
|
|
95
|
-
};
|
|
96
|
-
for (const hook of this._beforeHooks) {
|
|
97
|
-
const result = await hook(context);
|
|
98
|
-
if (result && result.abort) {
|
|
99
|
-
throw new Error("Payment creation aborted: " + result.reason);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
try {
|
|
103
|
-
if (!supportsBatching(paymentRequirements)) {
|
|
104
|
-
throw new Error(
|
|
105
|
-
'BatchEvmScheme can only handle Circle batching options. Expected extra.name="' + CIRCLE_BATCHING_NAME + '" and extra.version="' + CIRCLE_BATCHING_VERSION + '"'
|
|
106
|
-
);
|
|
107
|
-
}
|
|
108
|
-
const verifyingContract = getVerifyingContract(paymentRequirements);
|
|
109
|
-
if (!verifyingContract) {
|
|
110
|
-
throw new Error(
|
|
111
|
-
"Circle batching option missing extra.verifyingContract (GatewayWallet address)"
|
|
112
|
-
);
|
|
113
|
-
}
|
|
114
|
-
const nonce = createNonce();
|
|
115
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
116
|
-
const validityWindowSeconds = Math.max(
|
|
117
|
-
paymentRequirements.maxTimeoutSeconds || 0,
|
|
118
|
-
GATEWAY_AUTH_VALIDITY_WINDOW_SECONDS
|
|
119
|
-
);
|
|
120
|
-
const authorization = {
|
|
121
|
-
from: getAddress(this.signer.address),
|
|
122
|
-
to: getAddress(paymentRequirements.payTo),
|
|
123
|
-
value: paymentRequirements.amount,
|
|
124
|
-
validAfter: String(now - 600),
|
|
125
|
-
validBefore: String(now + validityWindowSeconds),
|
|
126
|
-
nonce
|
|
127
|
-
};
|
|
128
|
-
const signature = await this.signAuthorization(authorization, paymentRequirements, verifyingContract);
|
|
129
|
-
const payload = { authorization, signature };
|
|
130
|
-
const result = { x402Version, payload };
|
|
131
|
-
const createdContext = { ...context, paymentPayload: result };
|
|
132
|
-
for (const hook of this._afterHooks) {
|
|
133
|
-
try {
|
|
134
|
-
await hook(createdContext);
|
|
135
|
-
} catch {
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
return result;
|
|
139
|
-
} catch (error) {
|
|
140
|
-
const failureContext = { ...context, error };
|
|
141
|
-
for (const hook of this._failureHooks) {
|
|
142
|
-
const result = await hook(failureContext);
|
|
143
|
-
if (result && result.recovered) {
|
|
144
|
-
return { x402Version: result.payload.x402Version, payload: result.payload.payload };
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
throw error;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
async signAuthorization(authorization, requirements, verifyingContract) {
|
|
151
|
-
if (!requirements.network.startsWith("eip155:")) {
|
|
152
|
-
throw new Error('BatchEvmScheme: unsupported network format "' + requirements.network + '". Expected "eip155:<chainId>"');
|
|
153
|
-
}
|
|
154
|
-
const chainId = parseInt(requirements.network.split(":")[1], 10);
|
|
155
|
-
const domain = {
|
|
156
|
-
name: CIRCLE_BATCHING_NAME,
|
|
157
|
-
version: CIRCLE_BATCHING_VERSION,
|
|
158
|
-
chainId,
|
|
159
|
-
verifyingContract: getAddress(verifyingContract)
|
|
160
|
-
};
|
|
161
|
-
const message = {
|
|
162
|
-
from: getAddress(authorization.from),
|
|
163
|
-
to: getAddress(authorization.to),
|
|
164
|
-
value: BigInt(authorization.value),
|
|
165
|
-
validAfter: BigInt(authorization.validAfter),
|
|
166
|
-
validBefore: BigInt(authorization.validBefore),
|
|
167
|
-
nonce: authorization.nonce
|
|
168
|
-
};
|
|
169
|
-
const typedData = {
|
|
170
|
-
domain,
|
|
171
|
-
types: authorizationTypes,
|
|
172
|
-
primaryType: "TransferWithAuthorization",
|
|
173
|
-
message
|
|
174
|
-
};
|
|
175
|
-
return this.signer.signTypedData(typedData);
|
|
176
|
-
}
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
// src/browser/gateway.js
|
|
182
|
-
var gateway_exports = {};
|
|
183
|
-
__export(gateway_exports, {
|
|
184
|
-
createCircleGatewayBrowserAdapter: () => createCircleGatewayBrowserAdapter
|
|
185
|
-
});
|
|
186
|
-
function encodeBase64(value) {
|
|
187
|
-
const text = typeof value === "string" ? value : stringifyJson(value);
|
|
188
|
-
if (typeof Buffer !== "undefined") return Buffer.from(text).toString("base64");
|
|
189
|
-
return btoa(unescape(encodeURIComponent(text)));
|
|
190
|
-
}
|
|
191
|
-
function decodeBase64(value) {
|
|
192
|
-
if (typeof Buffer !== "undefined") return Buffer.from(value, "base64").toString("utf8");
|
|
193
|
-
return decodeURIComponent(escape(atob(value)));
|
|
194
|
-
}
|
|
195
|
-
async function createCircleGatewayBrowserAdapter(options = {}) {
|
|
196
|
-
const signer = options.signer || await options.getSigner?.();
|
|
197
|
-
if (!signer?.address || typeof signer.signTypedData !== "function") {
|
|
198
|
-
throw new Error("Circle Gateway browser adapter requires an EVM signer with address and signTypedData.");
|
|
199
|
-
}
|
|
200
|
-
const scheme = options.clientModule?.BatchEvmScheme ? new options.clientModule.BatchEvmScheme(signer) : new BatchEvmScheme(signer);
|
|
201
|
-
const network = options.network || options.chainId && `eip155:${options.chainId}` || "eip155:5042002";
|
|
202
|
-
function parsePaymentRequired(input) {
|
|
203
|
-
if (input && typeof input === "object") return input;
|
|
204
|
-
if (!input || typeof input !== "string") throw new Error("Missing PAYMENT-REQUIRED header.");
|
|
205
|
-
return JSON.parse(decodeBase64(input));
|
|
206
|
-
}
|
|
207
|
-
function selectGatewayRequirement(paymentRequired) {
|
|
208
|
-
const accepts = Array.isArray(paymentRequired?.accepts) ? paymentRequired.accepts : [];
|
|
209
|
-
const selected = accepts.find((option) => {
|
|
210
|
-
const extra = option.extra || {};
|
|
211
|
-
return option.network === network && extra.name === "GatewayWalletBatched" && extra.version === "1" && typeof extra.verifyingContract === "string";
|
|
212
|
-
}) || accepts.find((option) => {
|
|
213
|
-
const extra = option.extra || {};
|
|
214
|
-
return extra.name === "GatewayWalletBatched" && extra.version === "1" && typeof extra.verifyingContract === "string";
|
|
215
|
-
});
|
|
216
|
-
if (!selected) {
|
|
217
|
-
const networks = accepts.map((option) => option.network).filter(Boolean).join(", ") || "none";
|
|
218
|
-
const hasGatewayExtra = accepts.some((option) => {
|
|
219
|
-
const extra = option.extra || {};
|
|
220
|
-
return extra.name === "GatewayWalletBatched" && extra.version === "1" && typeof extra.verifyingContract === "string";
|
|
221
|
-
});
|
|
222
|
-
throw new Error(
|
|
223
|
-
hasGatewayExtra ? `No Circle Gateway batching payment option found for ${network}. Server returned networks: ${networks}.` : `The payment challenge is not a Circle Gateway batching challenge. Configure the creator access route with createCircleGatewayServer(...) or createNibgateServer({ paymentMode: 'circle-gateway', network: '${network}' }).`
|
|
224
|
-
);
|
|
225
|
-
}
|
|
226
|
-
return selected;
|
|
227
|
-
}
|
|
228
|
-
return {
|
|
229
|
-
signer,
|
|
230
|
-
network,
|
|
231
|
-
async pay({ paymentRequiredHeader, challenge }) {
|
|
232
|
-
const paymentRequired = parsePaymentRequired(paymentRequiredHeader || challenge);
|
|
233
|
-
const accepted = selectGatewayRequirement(paymentRequired);
|
|
234
|
-
const x402Version = paymentRequired.x402Version ?? 2;
|
|
235
|
-
const paymentPayload = await scheme.createPaymentPayload(x402Version, accepted);
|
|
236
|
-
const paymentSignature = encodeBase64({
|
|
237
|
-
...paymentPayload,
|
|
238
|
-
resource: paymentRequired.resource,
|
|
239
|
-
accepted
|
|
240
|
-
});
|
|
241
|
-
return {
|
|
242
|
-
paymentSignature,
|
|
243
|
-
signature: paymentSignature,
|
|
244
|
-
metadata: {
|
|
245
|
-
paymentProvider: "circle-gateway",
|
|
246
|
-
network: accepted.network,
|
|
247
|
-
payer: signer.address,
|
|
248
|
-
recipient: accepted.payTo || accepted.recipient,
|
|
249
|
-
amount: accepted.amount,
|
|
250
|
-
currency: accepted.asset
|
|
251
|
-
}
|
|
252
|
-
};
|
|
253
|
-
}
|
|
254
|
-
};
|
|
255
|
-
}
|
|
256
|
-
var init_gateway = __esm({
|
|
257
|
-
"src/browser/gateway.js"() {
|
|
258
|
-
init_batch_scheme();
|
|
259
|
-
init_json();
|
|
260
|
-
}
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
// src/browser/index.js
|
|
264
|
-
var index_exports = {};
|
|
265
|
-
__export(index_exports, {
|
|
266
|
-
ACCESS_MODES: () => ACCESS_MODES,
|
|
267
|
-
CONTENT_TYPES: () => CONTENT_TYPES,
|
|
268
|
-
NIBGATE_CONTENT_HASH_NAMESPACE: () => NIBGATE_CONTENT_HASH_NAMESPACE,
|
|
269
|
-
NIBGATE_CONTENT_SETTING_FIELDS: () => NIBGATE_CONTENT_SETTING_FIELDS,
|
|
270
|
-
NIBGATE_REPUTATION_ABI: () => NIBGATE_REPUTATION_ABI,
|
|
271
|
-
NIBGATE_REPUTATION_CHAIN_ID: () => NIBGATE_REPUTATION_CHAIN_ID,
|
|
272
|
-
NIBGATE_REPUTATION_CHAIN_NAME: () => NIBGATE_REPUTATION_CHAIN_NAME,
|
|
273
|
-
NIBGATE_REPUTATION_CONTRACT: () => NIBGATE_REPUTATION_CONTRACT,
|
|
274
|
-
NIBGATE_REPUTATION_RPC_URL: () => NIBGATE_REPUTATION_RPC_URL,
|
|
275
|
-
PAYMENT_RAILS: () => PAYMENT_RAILS,
|
|
276
|
-
UNLOCK_MODES: () => UNLOCK_MODES,
|
|
277
|
-
checkResourceAccess: () => checkResourceAccess,
|
|
278
|
-
contentRatingHash: () => contentRatingHash,
|
|
279
|
-
createCircleGatewayBrowserAdapter: () => createCircleGatewayBrowserAdapter2,
|
|
280
|
-
createEvmGatewayUnlock: () => createEvmGatewayUnlock,
|
|
281
|
-
createGate: () => createGate,
|
|
282
|
-
createHostedUnlock: () => createHostedUnlock,
|
|
283
|
-
createNibgate: () => createNibgate,
|
|
284
|
-
createNibgateContentSettings: () => createNibgateContentSettings,
|
|
285
|
-
createOnchainRating: () => createOnchainRating,
|
|
286
|
-
createTransferCheckout: () => createTransferCheckout,
|
|
287
|
-
createWalletCheckout: () => createWalletCheckout,
|
|
288
|
-
mountRatingUI: () => mountRatingUI,
|
|
289
|
-
nibgate: () => nibgate,
|
|
290
|
-
normalizeAccessPolicy: () => normalizeAccessPolicy,
|
|
291
|
-
normalizeContentType: () => normalizeContentType,
|
|
292
|
-
normalizePaymentRail: () => normalizePaymentRail,
|
|
293
|
-
normalizeResource: () => normalizeResource,
|
|
294
|
-
normalizeUnlockPolicy: () => normalizeUnlockPolicy,
|
|
295
|
-
payAndUnlockResource: () => payAndUnlockResource,
|
|
296
|
-
payWithPaymentSignature: () => payWithPaymentSignature,
|
|
297
|
-
payWithTransfer: () => payWithTransfer,
|
|
298
|
-
rateContentOnchain: () => rateContentOnchain,
|
|
299
|
-
rateResource: () => rateResource,
|
|
300
|
-
reviewTextHash: () => reviewTextHash,
|
|
301
|
-
settingsToAccessPolicy: () => settingsToAccessPolicy,
|
|
302
|
-
settingsToUnlockPolicy: () => settingsToUnlockPolicy,
|
|
303
|
-
setupResourcePage: () => setupResourcePage,
|
|
304
|
-
trackResourcePage: () => trackResourcePage,
|
|
305
|
-
validateResourceMetadata: () => validateResourceMetadata
|
|
306
|
-
});
|
|
307
|
-
|
|
308
23
|
// src/core/payment.js
|
|
309
|
-
var PAYMENT_RAILS = ["gateway", "transfer"];
|
|
310
24
|
function normalizePaymentRail(value, fallback = "gateway") {
|
|
311
25
|
const rail = String(value || "").trim().toLowerCase().replace(/[-\s]+/g, "_");
|
|
312
26
|
if (rail === "circle_gateway" || rail === "x402") return "gateway";
|
|
313
27
|
if (rail === "direct_transfer" || rail === "wallet_transfer") return "transfer";
|
|
314
28
|
return PAYMENT_RAILS.includes(rail) ? rail : fallback;
|
|
315
29
|
}
|
|
30
|
+
var PAYMENT_RAILS;
|
|
31
|
+
var init_payment = __esm({
|
|
32
|
+
"src/core/payment.js"() {
|
|
33
|
+
PAYMENT_RAILS = ["gateway", "transfer"];
|
|
34
|
+
}
|
|
35
|
+
});
|
|
316
36
|
|
|
317
37
|
// src/core/resource.js
|
|
318
|
-
var CONTENT_TYPES = ["music", "video", "article", "image"];
|
|
319
|
-
var TYPE_ALIASES = {
|
|
320
|
-
audio: "music",
|
|
321
|
-
song: "music",
|
|
322
|
-
track: "music",
|
|
323
|
-
album: "music",
|
|
324
|
-
playlist: "music",
|
|
325
|
-
photo: "image",
|
|
326
|
-
picture: "image",
|
|
327
|
-
illustration: "image",
|
|
328
|
-
art: "image",
|
|
329
|
-
movie: "video",
|
|
330
|
-
clip: "video"
|
|
331
|
-
};
|
|
332
|
-
var ACCESS_MODES = ["free", "paid", "blocked"];
|
|
333
|
-
var UNLOCK_MODES = ["one_time", "metered_stream", "metered_read", "time_pass", "agent_quota"];
|
|
334
38
|
function normalizeContentType(value) {
|
|
335
39
|
const type = String(value || "").trim().toLowerCase();
|
|
336
40
|
if (CONTENT_TYPES.includes(type)) return type;
|
|
@@ -474,7 +178,6 @@ var Nibgate = (() => {
|
|
|
474
178
|
}
|
|
475
179
|
if (isPaidResource(normalized)) {
|
|
476
180
|
if (!hasValue(normalized.price)) errors.push("Paid content requires price.");
|
|
477
|
-
if (!hasValue(normalized.recipient || normalized.payTo)) warnings.push("Paid content should include recipient/payTo (server value from 402 challenge is used for actual payment).");
|
|
478
181
|
}
|
|
479
182
|
const score = Math.max(0, 100 - errors.length * 20 - warnings.length * 8);
|
|
480
183
|
return {
|
|
@@ -485,11 +188,37 @@ var Nibgate = (() => {
|
|
|
485
188
|
resource: normalized
|
|
486
189
|
};
|
|
487
190
|
}
|
|
191
|
+
var CONTENT_TYPES, TYPE_ALIASES, ACCESS_MODES, UNLOCK_MODES;
|
|
192
|
+
var init_resource = __esm({
|
|
193
|
+
"src/core/resource.js"() {
|
|
194
|
+
init_payment();
|
|
195
|
+
CONTENT_TYPES = ["music", "video", "article", "image"];
|
|
196
|
+
TYPE_ALIASES = {
|
|
197
|
+
audio: "music",
|
|
198
|
+
song: "music",
|
|
199
|
+
track: "music",
|
|
200
|
+
album: "music",
|
|
201
|
+
playlist: "music",
|
|
202
|
+
photo: "image",
|
|
203
|
+
picture: "image",
|
|
204
|
+
illustration: "image",
|
|
205
|
+
art: "image",
|
|
206
|
+
movie: "video",
|
|
207
|
+
clip: "video"
|
|
208
|
+
};
|
|
209
|
+
ACCESS_MODES = ["free", "paid", "blocked"];
|
|
210
|
+
UNLOCK_MODES = ["one_time", "metered_stream", "metered_read", "time_pass", "agent_quota"];
|
|
211
|
+
}
|
|
212
|
+
});
|
|
488
213
|
|
|
489
214
|
// src/browser/env.js
|
|
490
215
|
function browserWindow() {
|
|
491
216
|
return typeof window === "undefined" ? null : window;
|
|
492
217
|
}
|
|
218
|
+
var init_env = __esm({
|
|
219
|
+
"src/browser/env.js"() {
|
|
220
|
+
}
|
|
221
|
+
});
|
|
493
222
|
|
|
494
223
|
// src/browser/events.js
|
|
495
224
|
function queueEvent(eventName, payload) {
|
|
@@ -540,6 +269,12 @@ var Nibgate = (() => {
|
|
|
540
269
|
resource: normalizeResource(resource)
|
|
541
270
|
};
|
|
542
271
|
}
|
|
272
|
+
var init_events = __esm({
|
|
273
|
+
"src/browser/events.js"() {
|
|
274
|
+
init_env();
|
|
275
|
+
init_resource();
|
|
276
|
+
}
|
|
277
|
+
});
|
|
543
278
|
|
|
544
279
|
// src/browser/storage.js
|
|
545
280
|
function unlockStorageKey(resource) {
|
|
@@ -602,9 +337,14 @@ var Nibgate = (() => {
|
|
|
602
337
|
return false;
|
|
603
338
|
}
|
|
604
339
|
}
|
|
340
|
+
var init_storage = __esm({
|
|
341
|
+
"src/browser/storage.js"() {
|
|
342
|
+
init_env();
|
|
343
|
+
init_resource();
|
|
344
|
+
}
|
|
345
|
+
});
|
|
605
346
|
|
|
606
347
|
// src/browser/gate.js
|
|
607
|
-
var defaultClient = null;
|
|
608
348
|
function setDefaultClient(client) {
|
|
609
349
|
defaultClient = client;
|
|
610
350
|
}
|
|
@@ -654,708 +394,1057 @@ var Nibgate = (() => {
|
|
|
654
394
|
}
|
|
655
395
|
};
|
|
656
396
|
}
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
397
|
+
var defaultClient;
|
|
398
|
+
var init_gate = __esm({
|
|
399
|
+
"src/browser/gate.js"() {
|
|
400
|
+
init_resource();
|
|
401
|
+
init_events();
|
|
402
|
+
init_storage();
|
|
403
|
+
defaultClient = null;
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
// src/browser/json.js
|
|
408
|
+
function jsonReplacer(_key, value) {
|
|
409
|
+
if (typeof value === "bigint") return value.toString();
|
|
410
|
+
return value;
|
|
411
|
+
}
|
|
412
|
+
function stringifyJson(value) {
|
|
413
|
+
return JSON.stringify(value, jsonReplacer);
|
|
414
|
+
}
|
|
415
|
+
var init_json = __esm({
|
|
416
|
+
"src/browser/json.js"() {
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
// src/core/rating.js
|
|
421
|
+
function normalizeRating(input = {}) {
|
|
422
|
+
const value = typeof input === "number" ? input : input.rating ?? input.stars ?? input.ratingValue ?? input.score;
|
|
423
|
+
const numeric = Number.parseFloat(value);
|
|
424
|
+
const ratingValue = Number.isFinite(numeric) ? Math.max(1, Math.min(50, numeric <= 5 ? Math.round(numeric * 10) : Math.round(numeric))) : null;
|
|
425
|
+
return {
|
|
426
|
+
...input,
|
|
427
|
+
rating: ratingValue ? ratingValue / 10 : void 0,
|
|
428
|
+
ratingValue: ratingValue || void 0
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
function ratingMessage(resource, rating = {}, options = {}) {
|
|
432
|
+
const normalized = normalizeResource(resource);
|
|
433
|
+
const normalizedRating = normalizeRating(rating);
|
|
434
|
+
const value = normalizedRating.ratingValue || 0;
|
|
435
|
+
return [
|
|
436
|
+
"Nibgate content rating",
|
|
437
|
+
`site:${options.siteDomain || options.domain || normalized.siteDomain || normalized.domain || ""}`,
|
|
438
|
+
`content:${normalized.externalId || normalized.id}`,
|
|
439
|
+
`url:${normalized.url || options.url || ""}`,
|
|
440
|
+
`rating:${value}`,
|
|
441
|
+
"I confirm this rating is tied to my unlock/payment proof."
|
|
442
|
+
].join("\n");
|
|
443
|
+
}
|
|
444
|
+
var init_rating = __esm({
|
|
445
|
+
"src/core/rating.js"() {
|
|
446
|
+
init_resource();
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
// src/browser/schemes/batch-scheme.js
|
|
451
|
+
function supportsBatching(requirements) {
|
|
452
|
+
const extra = requirements.extra;
|
|
453
|
+
if (!extra) return false;
|
|
454
|
+
return extra.name === CIRCLE_BATCHING_NAME && extra.version === CIRCLE_BATCHING_VERSION;
|
|
455
|
+
}
|
|
456
|
+
function getVerifyingContract(requirements) {
|
|
457
|
+
if (!supportsBatching(requirements)) return void 0;
|
|
458
|
+
return requirements.extra?.verifyingContract;
|
|
459
|
+
}
|
|
460
|
+
function createNonce() {
|
|
461
|
+
const bytes = new Uint8Array(32);
|
|
462
|
+
crypto.getRandomValues(bytes);
|
|
463
|
+
return "0x" + Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
464
|
+
}
|
|
465
|
+
function getAddress(value) {
|
|
466
|
+
return value && typeof value === "string" && value.startsWith("0x") ? value.toLowerCase() : value;
|
|
467
|
+
}
|
|
468
|
+
var CIRCLE_BATCHING_NAME, CIRCLE_BATCHING_VERSION, GATEWAY_MIN_AUTH_VALIDITY_SECONDS, GATEWAY_AUTH_VALIDITY_WINDOW_SECONDS, authorizationTypes, BatchEvmScheme;
|
|
469
|
+
var init_batch_scheme = __esm({
|
|
470
|
+
"src/browser/schemes/batch-scheme.js"() {
|
|
471
|
+
CIRCLE_BATCHING_NAME = "GatewayWalletBatched";
|
|
472
|
+
CIRCLE_BATCHING_VERSION = "1";
|
|
473
|
+
GATEWAY_MIN_AUTH_VALIDITY_SECONDS = 7 * 24 * 60 * 60;
|
|
474
|
+
GATEWAY_AUTH_VALIDITY_WINDOW_SECONDS = GATEWAY_MIN_AUTH_VALIDITY_SECONDS + 100;
|
|
475
|
+
authorizationTypes = {
|
|
476
|
+
TransferWithAuthorization: [
|
|
477
|
+
{ name: "from", type: "address" },
|
|
478
|
+
{ name: "to", type: "address" },
|
|
479
|
+
{ name: "value", type: "uint256" },
|
|
480
|
+
{ name: "validAfter", type: "uint256" },
|
|
481
|
+
{ name: "validBefore", type: "uint256" },
|
|
482
|
+
{ name: "nonce", type: "bytes32" }
|
|
483
|
+
]
|
|
484
|
+
};
|
|
485
|
+
BatchEvmScheme = class {
|
|
486
|
+
constructor(signer) {
|
|
487
|
+
this.signer = signer;
|
|
488
|
+
this.scheme = CIRCLE_BATCHING_NAME;
|
|
489
|
+
this._beforeHooks = [];
|
|
490
|
+
this._afterHooks = [];
|
|
491
|
+
this._failureHooks = [];
|
|
492
|
+
}
|
|
493
|
+
onBeforePaymentCreation(hook) {
|
|
494
|
+
this._beforeHooks.push(hook);
|
|
495
|
+
return this;
|
|
496
|
+
}
|
|
497
|
+
onAfterPaymentCreation(hook) {
|
|
498
|
+
this._afterHooks.push(hook);
|
|
499
|
+
return this;
|
|
500
|
+
}
|
|
501
|
+
onPaymentCreationFailure(hook) {
|
|
502
|
+
this._failureHooks.push(hook);
|
|
503
|
+
return this;
|
|
504
|
+
}
|
|
505
|
+
async createPaymentPayload(x402Version, paymentRequirements) {
|
|
506
|
+
const context = {
|
|
507
|
+
paymentRequired: { x402Version, accepts: [paymentRequirements] },
|
|
508
|
+
selectedRequirements: paymentRequirements
|
|
509
|
+
};
|
|
510
|
+
for (const hook of this._beforeHooks) {
|
|
511
|
+
const result = await hook(context);
|
|
512
|
+
if (result && result.abort) {
|
|
513
|
+
throw new Error("Payment creation aborted: " + result.reason);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
try {
|
|
517
|
+
if (!supportsBatching(paymentRequirements)) {
|
|
518
|
+
throw new Error(
|
|
519
|
+
'BatchEvmScheme can only handle Circle batching options. Expected extra.name="' + CIRCLE_BATCHING_NAME + '" and extra.version="' + CIRCLE_BATCHING_VERSION + '"'
|
|
520
|
+
);
|
|
521
|
+
}
|
|
522
|
+
const verifyingContract = getVerifyingContract(paymentRequirements);
|
|
523
|
+
if (!verifyingContract) {
|
|
524
|
+
throw new Error(
|
|
525
|
+
"Circle batching option missing extra.verifyingContract (GatewayWallet address)"
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
const nonce = createNonce();
|
|
529
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
530
|
+
const validityWindowSeconds = Math.max(
|
|
531
|
+
paymentRequirements.maxTimeoutSeconds || 0,
|
|
532
|
+
GATEWAY_AUTH_VALIDITY_WINDOW_SECONDS
|
|
533
|
+
);
|
|
534
|
+
const authorization = {
|
|
535
|
+
from: getAddress(this.signer.address),
|
|
536
|
+
to: getAddress(paymentRequirements.payTo),
|
|
537
|
+
value: paymentRequirements.amount,
|
|
538
|
+
validAfter: String(now - 600),
|
|
539
|
+
validBefore: String(now + validityWindowSeconds),
|
|
540
|
+
nonce
|
|
541
|
+
};
|
|
542
|
+
const signature = await this.signAuthorization(authorization, paymentRequirements, verifyingContract);
|
|
543
|
+
const payload = { authorization, signature };
|
|
544
|
+
const result = { x402Version, payload };
|
|
545
|
+
const createdContext = { ...context, paymentPayload: result };
|
|
546
|
+
for (const hook of this._afterHooks) {
|
|
547
|
+
try {
|
|
548
|
+
await hook(createdContext);
|
|
549
|
+
} catch {
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
return result;
|
|
553
|
+
} catch (error) {
|
|
554
|
+
const failureContext = { ...context, error };
|
|
555
|
+
for (const hook of this._failureHooks) {
|
|
556
|
+
const result = await hook(failureContext);
|
|
557
|
+
if (result && result.recovered) {
|
|
558
|
+
return { x402Version: result.payload.x402Version, payload: result.payload.payload };
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
throw error;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
async signAuthorization(authorization, requirements, verifyingContract) {
|
|
565
|
+
if (!requirements.network.startsWith("eip155:")) {
|
|
566
|
+
throw new Error('BatchEvmScheme: unsupported network format "' + requirements.network + '". Expected "eip155:<chainId>"');
|
|
567
|
+
}
|
|
568
|
+
const chainId = parseInt(requirements.network.split(":")[1], 10);
|
|
569
|
+
const domain = {
|
|
570
|
+
name: CIRCLE_BATCHING_NAME,
|
|
571
|
+
version: CIRCLE_BATCHING_VERSION,
|
|
572
|
+
chainId,
|
|
573
|
+
verifyingContract: getAddress(verifyingContract)
|
|
574
|
+
};
|
|
575
|
+
const message = {
|
|
576
|
+
from: getAddress(authorization.from),
|
|
577
|
+
to: getAddress(authorization.to),
|
|
578
|
+
value: BigInt(authorization.value),
|
|
579
|
+
validAfter: BigInt(authorization.validAfter),
|
|
580
|
+
validBefore: BigInt(authorization.validBefore),
|
|
581
|
+
nonce: authorization.nonce
|
|
582
|
+
};
|
|
583
|
+
const typedData = {
|
|
584
|
+
domain,
|
|
585
|
+
types: authorizationTypes,
|
|
586
|
+
primaryType: "TransferWithAuthorization",
|
|
587
|
+
message
|
|
588
|
+
};
|
|
589
|
+
return this.signer.signTypedData(typedData);
|
|
590
|
+
}
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
// src/browser/gateway.js
|
|
596
|
+
var gateway_exports = {};
|
|
597
|
+
__export(gateway_exports, {
|
|
598
|
+
createCircleGatewayBrowserAdapter: () => createCircleGatewayBrowserAdapter
|
|
599
|
+
});
|
|
600
|
+
function encodeBase64(value) {
|
|
601
|
+
const text = typeof value === "string" ? value : stringifyJson(value);
|
|
602
|
+
if (typeof Buffer !== "undefined") return Buffer.from(text).toString("base64");
|
|
603
|
+
return btoa(unescape(encodeURIComponent(text)));
|
|
604
|
+
}
|
|
605
|
+
function decodeBase64(value) {
|
|
606
|
+
if (typeof Buffer !== "undefined") return Buffer.from(value, "base64").toString("utf8");
|
|
607
|
+
return decodeURIComponent(escape(atob(value)));
|
|
608
|
+
}
|
|
609
|
+
async function createCircleGatewayBrowserAdapter(options = {}) {
|
|
610
|
+
const signer = options.signer || await options.getSigner?.();
|
|
611
|
+
if (!signer?.address || typeof signer.signTypedData !== "function") {
|
|
612
|
+
throw new Error("Circle Gateway browser adapter requires an EVM signer with address and signTypedData.");
|
|
613
|
+
}
|
|
614
|
+
const scheme = options.clientModule?.BatchEvmScheme ? new options.clientModule.BatchEvmScheme(signer) : new BatchEvmScheme(signer);
|
|
615
|
+
const network = options.network || options.chainId && `eip155:${options.chainId}` || "eip155:5042002";
|
|
616
|
+
function parsePaymentRequired(input) {
|
|
617
|
+
if (input && typeof input === "object") return input;
|
|
618
|
+
if (!input || typeof input !== "string") throw new Error("Missing PAYMENT-REQUIRED header.");
|
|
619
|
+
return JSON.parse(decodeBase64(input));
|
|
620
|
+
}
|
|
621
|
+
function selectGatewayRequirement(paymentRequired) {
|
|
622
|
+
const accepts = Array.isArray(paymentRequired?.accepts) ? paymentRequired.accepts : [];
|
|
623
|
+
const selected = accepts.find((option) => {
|
|
624
|
+
const extra = option.extra || {};
|
|
625
|
+
return option.network === network && extra.name === "GatewayWalletBatched" && extra.version === "1" && typeof extra.verifyingContract === "string";
|
|
626
|
+
}) || accepts.find((option) => {
|
|
627
|
+
const extra = option.extra || {};
|
|
628
|
+
return extra.name === "GatewayWalletBatched" && extra.version === "1" && typeof extra.verifyingContract === "string";
|
|
629
|
+
});
|
|
630
|
+
if (!selected) {
|
|
631
|
+
const networks = accepts.map((option) => option.network).filter(Boolean).join(", ") || "none";
|
|
632
|
+
const hasGatewayExtra = accepts.some((option) => {
|
|
633
|
+
const extra = option.extra || {};
|
|
634
|
+
return extra.name === "GatewayWalletBatched" && extra.version === "1" && typeof extra.verifyingContract === "string";
|
|
635
|
+
});
|
|
636
|
+
throw new Error(
|
|
637
|
+
hasGatewayExtra ? `No Circle Gateway batching payment option found for ${network}. Server returned networks: ${networks}.` : `The payment challenge is not a Circle Gateway batching challenge. Configure the creator access route with createCircleGatewayServer(...) or createNibgateServer({ paymentMode: 'circle-gateway', network: '${network}' }).`
|
|
638
|
+
);
|
|
639
|
+
}
|
|
640
|
+
return selected;
|
|
641
|
+
}
|
|
642
|
+
return {
|
|
643
|
+
signer,
|
|
644
|
+
network,
|
|
645
|
+
async pay({ paymentRequiredHeader, challenge }) {
|
|
646
|
+
const paymentRequired = parsePaymentRequired(paymentRequiredHeader || challenge);
|
|
647
|
+
const accepted = selectGatewayRequirement(paymentRequired);
|
|
648
|
+
const x402Version = paymentRequired.x402Version ?? 2;
|
|
649
|
+
const paymentPayload = await scheme.createPaymentPayload(x402Version, accepted);
|
|
650
|
+
const paymentSignature = encodeBase64({
|
|
651
|
+
...paymentPayload,
|
|
652
|
+
resource: paymentRequired.resource,
|
|
653
|
+
accepted
|
|
654
|
+
});
|
|
655
|
+
return {
|
|
656
|
+
paymentSignature,
|
|
657
|
+
signature: paymentSignature,
|
|
658
|
+
metadata: {
|
|
659
|
+
paymentProvider: "circle-gateway",
|
|
660
|
+
network: accepted.network,
|
|
661
|
+
payer: signer.address,
|
|
662
|
+
recipient: accepted.payTo || accepted.recipient,
|
|
663
|
+
amount: accepted.amount,
|
|
664
|
+
currency: accepted.asset
|
|
665
|
+
}
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
var init_gateway = __esm({
|
|
671
|
+
"src/browser/gateway.js"() {
|
|
672
|
+
init_batch_scheme();
|
|
673
|
+
init_json();
|
|
667
674
|
}
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
...options.view || {}
|
|
674
|
-
});
|
|
675
|
-
return item;
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
// src/browser/reputation.js
|
|
678
|
+
function stripHex(value = "") {
|
|
679
|
+
return String(value || "").replace(/^0x/i, "").replace(/[^0-9a-fA-F]/g, "").toLowerCase();
|
|
676
680
|
}
|
|
677
|
-
function
|
|
678
|
-
const
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
681
|
+
function wordRight(hex = "") {
|
|
682
|
+
const clean = stripHex(hex);
|
|
683
|
+
if (clean.length > 64) throw new Error("ABI word is too long.");
|
|
684
|
+
return clean.padEnd(64, "0");
|
|
685
|
+
}
|
|
686
|
+
function numberWord(value = 0) {
|
|
687
|
+
return Number(value || 0).toString(16).padStart(64, "0");
|
|
688
|
+
}
|
|
689
|
+
function utf8Hex(value = "") {
|
|
690
|
+
return Array.from(new TextEncoder().encode(String(value || ""))).map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
691
|
+
}
|
|
692
|
+
function encodeString(value = "") {
|
|
693
|
+
const hex = utf8Hex(value);
|
|
694
|
+
const byteLength = hex.length / 2;
|
|
695
|
+
const paddedLength = Math.ceil(byteLength / 32) * 64;
|
|
696
|
+
return numberWord(byteLength) + hex.padEnd(paddedLength, "0");
|
|
697
|
+
}
|
|
698
|
+
function encodeRateContent({ contentId, ratingValue, reviewHash, unlockRef }) {
|
|
699
|
+
return RATE_CONTENT_SELECTOR + wordRight(contentId) + numberWord(ratingValue) + wordRight(reviewHash || ZERO_HASH) + numberWord(128) + encodeString(unlockRef || "");
|
|
700
|
+
}
|
|
701
|
+
function contentRatingHash(_resource, options = {}) {
|
|
702
|
+
const contentId = options.contentId || options.contentHash;
|
|
703
|
+
if (!contentId) {
|
|
704
|
+
throw new Error("contentId/contentHash is required. Use the Nibgate backend prepare endpoint or pass a known content hash.");
|
|
695
705
|
}
|
|
696
|
-
return
|
|
706
|
+
return contentId;
|
|
697
707
|
}
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
708
|
+
function reviewTextHash(review = "") {
|
|
709
|
+
if (!review) return ZERO_HASH;
|
|
710
|
+
throw new Error("Text review hashing is not available in direct-browser mode. Pass reviewHash from your app/backend.");
|
|
711
|
+
}
|
|
712
|
+
async function prepareOnchainRating(resource, options = {}) {
|
|
713
|
+
if (options.contentId || options.contentHash) return { contentId: options.contentId || options.contentHash };
|
|
714
|
+
const prepareUrl = options.prepareUrl || options.indexUrl?.replace(/\/index$/, "/prepare");
|
|
715
|
+
if (!prepareUrl) throw new Error("contentId/contentHash or prepareUrl is required for onchain rating.");
|
|
716
|
+
const response = await fetch(prepareUrl, {
|
|
717
|
+
method: "POST",
|
|
718
|
+
headers: { "content-type": "application/json", ...options.indexHeaders || {} },
|
|
719
|
+
body: JSON.stringify({
|
|
720
|
+
siteId: options.siteId,
|
|
721
|
+
token: options.token,
|
|
722
|
+
resource,
|
|
723
|
+
url: resource.url,
|
|
724
|
+
path: resource.path
|
|
725
|
+
})
|
|
715
726
|
});
|
|
716
727
|
const payload = await response.json().catch(() => ({}));
|
|
717
|
-
if (response.
|
|
718
|
-
|
|
719
|
-
status(options.challengeMessage || "Payment challenge returned. Continue with checkout.");
|
|
720
|
-
if (typeof options.createPaymentSignature === "function" || typeof options.checkout === "function") {
|
|
721
|
-
return payWithPaymentSignature(resource, {
|
|
722
|
-
...options,
|
|
723
|
-
challenge: payload,
|
|
724
|
-
paymentRequiredHeader: response.headers.get("PAYMENT-REQUIRED") || response.headers.get("payment-required") || ""
|
|
725
|
-
});
|
|
726
|
-
}
|
|
727
|
-
if (options.autoPay && options.payPath) {
|
|
728
|
-
const paymentResult = await payAndUnlockResource(resource, options);
|
|
729
|
-
if (paymentResult.ok && options.retryAfterPay !== false) {
|
|
730
|
-
return checkResourceAccess(resource, { ...options, autoPay: false });
|
|
731
|
-
}
|
|
732
|
-
return paymentResult;
|
|
733
|
-
}
|
|
734
|
-
return { ok: false, status: response.status, challenge: payload, resource: item.resource, response };
|
|
735
|
-
}
|
|
736
|
-
if (!response.ok) {
|
|
737
|
-
status(payload.error || options.errorMessage || "Access check failed");
|
|
738
|
-
return { ok: false, status: response.status, error: payload.error || "Access check failed", payload, resource: item.resource, response };
|
|
739
|
-
}
|
|
740
|
-
const payment = options.payment || payload.payment || null;
|
|
741
|
-
if (payment) {
|
|
742
|
-
item.unlockCompleted(payment);
|
|
743
|
-
item.paymentCompleted(payment);
|
|
744
|
-
}
|
|
745
|
-
status(options.successMessage || "Access allowed and Nibgate events emitted.");
|
|
746
|
-
return { ok: true, status: response.status, payload, payment, resource: item.resource, response };
|
|
728
|
+
if (!response.ok || !payload.contentHash) throw new Error(payload.error || "Could not prepare Nibgate onchain rating.");
|
|
729
|
+
return payload;
|
|
747
730
|
}
|
|
748
|
-
async function
|
|
749
|
-
const
|
|
750
|
-
const
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
if (!
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
}
|
|
772
|
-
if (!paymentSignature) {
|
|
773
|
-
const error = "Wallet did not return a payment signature.";
|
|
774
|
-
item.track("payment_failed", { source: options.source, error });
|
|
775
|
-
status(error);
|
|
776
|
-
return { ok: false, status: 400, error, resource: item.resource };
|
|
777
|
-
}
|
|
778
|
-
const response = await fetch(accessPath, {
|
|
779
|
-
method: options.method || "GET",
|
|
780
|
-
headers: {
|
|
781
|
-
accept: "application/json",
|
|
782
|
-
"payment-signature": paymentSignature,
|
|
783
|
-
...paymentMemo ? { "payment-memo": paymentMemo } : {},
|
|
784
|
-
...options.headers || {}
|
|
785
|
-
}
|
|
731
|
+
async function rateContentOnchain(resource, options = {}) {
|
|
732
|
+
const normalized = normalizeResource(resource);
|
|
733
|
+
const rating = normalizeRating(options.rating ?? options.stars ?? options);
|
|
734
|
+
if (!rating.ratingValue) throw new Error("Rating must be between 0.1 and 5 stars.");
|
|
735
|
+
const provider = options.provider || globalThis?.ethereum;
|
|
736
|
+
if (!provider?.request) throw new Error("Connect an EVM wallet to rate this content onchain.");
|
|
737
|
+
const contractAddress = options.contractAddress || options.reputationContract || NIBGATE_REPUTATION_CONTRACT;
|
|
738
|
+
if (!contractAddress) throw new Error("Nibgate reputation contract address is not configured.");
|
|
739
|
+
const accounts = await provider.request({ method: "eth_requestAccounts" });
|
|
740
|
+
const walletAddress = Array.isArray(accounts) ? accounts[0] || "" : "";
|
|
741
|
+
if (!walletAddress) throw new Error("No wallet account selected.");
|
|
742
|
+
const prepared = await prepareOnchainRating(normalized, options);
|
|
743
|
+
const contentId = prepared.contentHash || prepared.contentId || contentRatingHash(normalized, options);
|
|
744
|
+
const reviewHash = options.reviewHash || ZERO_HASH;
|
|
745
|
+
const unlockRef = String(options.unlockRef || options.paymentId || options.txHash || "");
|
|
746
|
+
const data = encodeRateContent({ contentId, ratingValue: rating.ratingValue, reviewHash, unlockRef });
|
|
747
|
+
const txHash = await provider.request({
|
|
748
|
+
method: "eth_sendTransaction",
|
|
749
|
+
params: [{
|
|
750
|
+
from: walletAddress,
|
|
751
|
+
to: contractAddress,
|
|
752
|
+
data
|
|
753
|
+
}]
|
|
786
754
|
});
|
|
787
|
-
const
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
status(options.paymentErrorMessage || error);
|
|
799
|
-
return { ok: false, status: response.status, error, payload, resource: item.resource, response };
|
|
800
|
-
}
|
|
801
|
-
const payment = payload.payment || {
|
|
802
|
-
paymentProvider: options.paymentProvider || "wallet-gateway",
|
|
803
|
-
paymentId: paymentSignature,
|
|
804
|
-
memo: paymentMemo,
|
|
805
|
-
amount: Number(item.resource.price || 0),
|
|
806
|
-
revenue: Number(item.resource.price || 0),
|
|
807
|
-
currency: item.resource.currency || "USDC",
|
|
808
|
-
...paymentMetadata
|
|
809
|
-
};
|
|
810
|
-
storePaymentProof(item.resource, payload.unlockProof);
|
|
811
|
-
item.markUnlocked(payment);
|
|
812
|
-
status(options.paymentSuccessMessage || "Payment verified. Content unlocked.");
|
|
813
|
-
return { ok: true, status: response.status, payload, payment, resource: item.resource, response };
|
|
814
|
-
}
|
|
815
|
-
async function payAndUnlockResource(resource, options = {}) {
|
|
816
|
-
const item = createGate(resource, options.gateOptions || {});
|
|
817
|
-
const payPath = options.payPath || item.resource.payPath || "/api/nibgate/pay";
|
|
818
|
-
const status = typeof options.onStatus === "function" ? options.onStatus : () => {
|
|
819
|
-
};
|
|
820
|
-
status(options.paymentMessage || "Starting payment...");
|
|
821
|
-
item.unlockStarted({ source: options.source, paymentProvider: options.paymentProvider || "circle-gateway" });
|
|
822
|
-
const response = await fetch(payPath, {
|
|
823
|
-
method: options.payMethod || "POST",
|
|
824
|
-
headers: {
|
|
825
|
-
accept: "application/json",
|
|
826
|
-
"content-type": "application/json",
|
|
827
|
-
...options.payHeaders || {}
|
|
828
|
-
},
|
|
829
|
-
body: JSON.stringify({ resource: item.resource, ...options.payPayload || {} })
|
|
755
|
+
const payload = payloadWithResource(normalized, {
|
|
756
|
+
rating: rating.rating,
|
|
757
|
+
ratingValue: rating.ratingValue,
|
|
758
|
+
walletAddress,
|
|
759
|
+
txHash,
|
|
760
|
+
contentHash: contentId,
|
|
761
|
+
reviewHash,
|
|
762
|
+
proofType: "onchain_pending",
|
|
763
|
+
proof: unlockRef,
|
|
764
|
+
paymentId: options.paymentId,
|
|
765
|
+
actor: options.actor || "human"
|
|
830
766
|
});
|
|
831
|
-
|
|
832
|
-
if (
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
767
|
+
emit("content_rating", payload);
|
|
768
|
+
if (options.indexUrl) {
|
|
769
|
+
await fetch(options.indexUrl, {
|
|
770
|
+
method: "POST",
|
|
771
|
+
headers: { "content-type": "application/json", ...options.indexHeaders || {} },
|
|
772
|
+
body: JSON.stringify({
|
|
773
|
+
siteId: options.siteId,
|
|
774
|
+
token: options.token,
|
|
775
|
+
txHash,
|
|
776
|
+
resource: normalized,
|
|
777
|
+
url: normalized.url,
|
|
778
|
+
path: normalized.path,
|
|
779
|
+
actor: options.actor || "human"
|
|
780
|
+
})
|
|
781
|
+
}).catch(() => null);
|
|
836
782
|
}
|
|
837
|
-
|
|
838
|
-
paymentProvider: options.paymentProvider || "circle-gateway",
|
|
839
|
-
paymentId: payload.paymentId || `nibgate_payment_${Date.now()}`,
|
|
840
|
-
amount: Number(item.resource.price || 0),
|
|
841
|
-
revenue: Number(item.resource.price || 0),
|
|
842
|
-
currency: item.resource.currency || "USDC"
|
|
843
|
-
};
|
|
844
|
-
storePaymentProof(item.resource, payload.unlockProof);
|
|
845
|
-
item.markUnlocked(payment);
|
|
846
|
-
status(options.paymentSuccessMessage || "Payment verified. Content unlocked.");
|
|
847
|
-
return { ok: true, status: response.status, payload, payment, resource: item.resource, response };
|
|
783
|
+
return { txHash, walletAddress, contentId, ratingValue: rating.ratingValue, reviewHash };
|
|
848
784
|
}
|
|
785
|
+
var RATE_CONTENT_SELECTOR, ZERO_HASH, NIBGATE_CONTENT_HASH_NAMESPACE, NIBGATE_REPUTATION_CHAIN_ID, NIBGATE_REPUTATION_CHAIN_NAME, NIBGATE_REPUTATION_RPC_URL, NIBGATE_REPUTATION_CONTRACT, NIBGATE_REPUTATION_ABI;
|
|
786
|
+
var init_reputation = __esm({
|
|
787
|
+
"src/browser/reputation.js"() {
|
|
788
|
+
init_rating();
|
|
789
|
+
init_resource();
|
|
790
|
+
init_events();
|
|
791
|
+
RATE_CONTENT_SELECTOR = "0xc62fad09";
|
|
792
|
+
ZERO_HASH = `0x${"0".repeat(64)}`;
|
|
793
|
+
NIBGATE_CONTENT_HASH_NAMESPACE = "nibgate:content:v1";
|
|
794
|
+
NIBGATE_REPUTATION_CHAIN_ID = 5042002;
|
|
795
|
+
NIBGATE_REPUTATION_CHAIN_NAME = "Arc Testnet";
|
|
796
|
+
NIBGATE_REPUTATION_RPC_URL = "https://rpc.testnet.arc.io";
|
|
797
|
+
NIBGATE_REPUTATION_CONTRACT = "0x9f27fd62e75f86a3c7addfdba443aab1f930e281";
|
|
798
|
+
NIBGATE_REPUTATION_ABI = [
|
|
799
|
+
{
|
|
800
|
+
type: "function",
|
|
801
|
+
name: "rateContent",
|
|
802
|
+
stateMutability: "nonpayable",
|
|
803
|
+
inputs: [
|
|
804
|
+
{ name: "contentId", type: "bytes32" },
|
|
805
|
+
{ name: "rating", type: "uint8" },
|
|
806
|
+
{ name: "reviewHash", type: "bytes32" },
|
|
807
|
+
{ name: "unlockRef", type: "string" }
|
|
808
|
+
],
|
|
809
|
+
outputs: []
|
|
810
|
+
}
|
|
811
|
+
];
|
|
812
|
+
}
|
|
813
|
+
});
|
|
849
814
|
|
|
850
|
-
// src/browser/
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
815
|
+
// src/browser/rating-ui.js
|
|
816
|
+
var rating_ui_exports = {};
|
|
817
|
+
__export(rating_ui_exports, {
|
|
818
|
+
createOnchainRating: () => createOnchainRating,
|
|
819
|
+
mountRatingUI: () => mountRatingUI,
|
|
820
|
+
rateResource: () => rateResource
|
|
821
|
+
});
|
|
822
|
+
function rateResource(resource, rating = {}, extra = {}) {
|
|
823
|
+
const normalized = normalizeResource(resource);
|
|
824
|
+
const normalizedRating = normalizeRating(rating);
|
|
825
|
+
const payload = {
|
|
826
|
+
...extra,
|
|
827
|
+
...normalizedRating,
|
|
828
|
+
ratingMessage: extra.ratingMessage || rating.message || rating.ratingMessage || ratingMessage(normalized, normalizedRating, extra),
|
|
829
|
+
ratingSignature: extra.ratingSignature || rating.signature || rating.ratingSignature || void 0,
|
|
830
|
+
resource: normalized
|
|
831
|
+
};
|
|
832
|
+
return emit("content_rating", payload);
|
|
856
833
|
}
|
|
857
|
-
function
|
|
834
|
+
function createOnchainRating(resource, options = {}) {
|
|
835
|
+
const item = createGate(resource, options.gateOptions || {});
|
|
858
836
|
const win = browserWindow();
|
|
859
|
-
|
|
860
|
-
const
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
const
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
if (typeof checkout !== "function") {
|
|
871
|
-
throw new Error("createWalletCheckout requires checkout/createPaymentSignature/pay callback for the active wallet or Gateway adapter.");
|
|
837
|
+
const statusTarget = typeof options.status === "string" ? win?.document.querySelector(options.status) : options.status;
|
|
838
|
+
const ratingTarget = typeof options.ratingTarget === "string" ? win?.document.querySelector(options.ratingTarget) : options.ratingTarget;
|
|
839
|
+
const buttonSelector = options.ratingButtons || options.buttons || "[data-nibgate-rating-value], [data-rating]";
|
|
840
|
+
const explicitButtons = Array.isArray(options.buttons) ? options.buttons : typeof options.buttons === "string" ? Array.from(win?.document.querySelectorAll(options.buttons) || []) : options.buttons ? [options.buttons] : null;
|
|
841
|
+
const buttons = explicitButtons || Array.from(win?.document.querySelectorAll(buttonSelector) || []);
|
|
842
|
+
const source = options.source || "nibgate-onchain-rating";
|
|
843
|
+
let busy = false;
|
|
844
|
+
let payment = options.payment || null;
|
|
845
|
+
function setStatus(message) {
|
|
846
|
+
if (typeof options.onStatus === "function") options.onStatus(message);
|
|
847
|
+
if (statusTarget) statusTarget.textContent = message || "";
|
|
872
848
|
}
|
|
873
|
-
|
|
874
|
-
|
|
849
|
+
function setBusy(value) {
|
|
850
|
+
busy = Boolean(value);
|
|
851
|
+
buttons.forEach((button) => {
|
|
852
|
+
if (button && "disabled" in button) button.disabled = busy;
|
|
853
|
+
});
|
|
854
|
+
}
|
|
855
|
+
function setPayment(nextPayment = null) {
|
|
856
|
+
payment = nextPayment || null;
|
|
857
|
+
return payment;
|
|
858
|
+
}
|
|
859
|
+
function setVisible(isVisible) {
|
|
860
|
+
if (!ratingTarget) return Boolean(isVisible);
|
|
861
|
+
if ("hidden" in ratingTarget) ratingTarget.hidden = !isVisible;
|
|
862
|
+
ratingTarget.setAttribute("aria-hidden", isVisible ? "false" : "true");
|
|
863
|
+
return Boolean(isVisible);
|
|
864
|
+
}
|
|
865
|
+
function valueFromButton(button) {
|
|
866
|
+
const raw = button?.dataset?.nibgateRatingValue || button?.dataset?.rating || button?.value || button?.textContent;
|
|
867
|
+
const numeric = Number.parseFloat(String(raw || "").replace(/[^\d.]/g, ""));
|
|
868
|
+
return Number.isFinite(numeric) ? numeric : Number(options.rating || options.stars || 0);
|
|
869
|
+
}
|
|
870
|
+
async function rate(input = {}) {
|
|
871
|
+
setBusy(true);
|
|
875
872
|
try {
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
873
|
+
const rating = Number.parseFloat(input.rating ?? input.stars ?? input.value ?? options.rating ?? options.stars);
|
|
874
|
+
if (!Number.isFinite(rating)) throw new Error("Choose a rating before sending.");
|
|
875
|
+
const paymentId = input.paymentId || options.paymentId || (typeof options.getPaymentId === "function" ? options.getPaymentId() : payment?.paymentId);
|
|
876
|
+
const unlockRef = input.unlockRef || options.unlockRef || (typeof options.getUnlockRef === "function" ? options.getUnlockRef() : null) || paymentId || payment?.txHash || payment?.transactionHash || "";
|
|
877
|
+
setStatus(options.pendingMessage || "Send the onchain rating transaction...");
|
|
878
|
+
const result = await rateContentOnchain(item.resource, { ...options, ...input, rating, paymentId, unlockRef, source });
|
|
879
|
+
setStatus(options.successMessage || "Rating sent to Nibgate reputation.");
|
|
880
|
+
if (typeof options.onRated === "function") options.onRated(result);
|
|
881
|
+
return result;
|
|
882
|
+
} catch (error) {
|
|
883
|
+
const message = error?.message || options.errorMessage || "Rating failed.";
|
|
884
|
+
setStatus(message);
|
|
885
|
+
if (typeof options.onError === "function") options.onError(error);
|
|
886
|
+
throw error;
|
|
883
887
|
} finally {
|
|
884
|
-
|
|
888
|
+
setBusy(false);
|
|
885
889
|
}
|
|
886
890
|
}
|
|
887
891
|
function mount() {
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
if (
|
|
892
|
-
return
|
|
892
|
+
buttons.forEach((button) => {
|
|
893
|
+
button?.addEventListener?.("click", () => rate({ rating: valueFromButton(button) }).catch(() => null));
|
|
894
|
+
});
|
|
895
|
+
if (options.visible !== void 0) setVisible(Boolean(options.visible));
|
|
896
|
+
return controller;
|
|
893
897
|
}
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
// src/core/rating.js
|
|
898
|
-
function normalizeRating(input = {}) {
|
|
899
|
-
const value = typeof input === "number" ? input : input.rating ?? input.stars ?? input.ratingValue ?? input.score;
|
|
900
|
-
const numeric = Number.parseFloat(value);
|
|
901
|
-
const ratingValue = Number.isFinite(numeric) ? Math.max(1, Math.min(50, numeric <= 5 ? Math.round(numeric * 10) : Math.round(numeric))) : null;
|
|
902
|
-
return {
|
|
903
|
-
...input,
|
|
904
|
-
rating: ratingValue ? ratingValue / 10 : void 0,
|
|
905
|
-
ratingValue: ratingValue || void 0
|
|
906
|
-
};
|
|
898
|
+
const controller = { resource: item.resource, rate, mount, setPayment, setVisible };
|
|
899
|
+
if (options.autoMount !== false) mount();
|
|
900
|
+
return controller;
|
|
907
901
|
}
|
|
908
|
-
function
|
|
909
|
-
const
|
|
910
|
-
const
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
902
|
+
function mountRatingUI(resource, options = {}) {
|
|
903
|
+
const item = createGate(resource, options.gateOptions || {});
|
|
904
|
+
const win = browserWindow();
|
|
905
|
+
if (!win) return null;
|
|
906
|
+
const target = typeof options.target === "string" ? win.document.querySelector(options.target) : options.target;
|
|
907
|
+
if (!target) return null;
|
|
908
|
+
const stars = [1, 2, 3, 4, 5];
|
|
909
|
+
let selectedRating = 0;
|
|
910
|
+
const container = win.document.createElement("div");
|
|
911
|
+
container.className = "nibgate-rating-ui";
|
|
912
|
+
container.style.cssText = "display:flex;align-items:center;gap:4px;padding:8px 0";
|
|
913
|
+
const starButtons = stars.map((value) => {
|
|
914
|
+
const btn = win.document.createElement("button");
|
|
915
|
+
btn.type = "button";
|
|
916
|
+
btn.dataset.nibgateRatingValue = String(value);
|
|
917
|
+
btn.setAttribute("aria-label", `${value} star${value > 1 ? "s" : ""}`);
|
|
918
|
+
btn.innerHTML = "\u2606";
|
|
919
|
+
btn.style.cssText = "background:none;border:none;font-size:24px;cursor:pointer;color:#ccc;transition:color 0.15s;padding:2px;line-height:1";
|
|
920
|
+
btn.addEventListener("mouseenter", () => {
|
|
921
|
+
starButtons.forEach((b, i) => b.style.color = i < value ? "#f5b342" : "#ccc");
|
|
922
|
+
});
|
|
923
|
+
btn.addEventListener("mouseleave", () => {
|
|
924
|
+
starButtons.forEach((b, i) => b.style.color = i < selectedRating ? "#f5b342" : "#ccc");
|
|
925
|
+
});
|
|
926
|
+
btn.addEventListener("click", () => {
|
|
927
|
+
selectedRating = value;
|
|
928
|
+
starButtons.forEach((b, i) => b.style.color = i < value ? "#f5b342" : "#ccc");
|
|
929
|
+
rateResource(item.resource, { rating: value }).catch(() => {
|
|
930
|
+
});
|
|
931
|
+
});
|
|
932
|
+
container.appendChild(btn);
|
|
933
|
+
return btn;
|
|
934
|
+
});
|
|
935
|
+
const statusEl = win.document.createElement("span");
|
|
936
|
+
statusEl.style.cssText = "font-size:13px;color:#888;margin-left:8px";
|
|
937
|
+
statusEl.textContent = options.label || "Rate this content";
|
|
938
|
+
container.appendChild(statusEl);
|
|
939
|
+
target.appendChild(container);
|
|
940
|
+
function rate(r, input = {}) {
|
|
941
|
+
return item.rate({ ...input, rating: r });
|
|
942
|
+
}
|
|
943
|
+
function setRating(value) {
|
|
944
|
+
selectedRating = value;
|
|
945
|
+
starButtons.forEach((b, i) => b.style.color = i < value ? "#f5b342" : "#ccc");
|
|
946
|
+
}
|
|
947
|
+
return { resource: item.resource, container, setRating, rate };
|
|
920
948
|
}
|
|
949
|
+
var init_rating_ui = __esm({
|
|
950
|
+
"src/browser/rating-ui.js"() {
|
|
951
|
+
init_resource();
|
|
952
|
+
init_rating();
|
|
953
|
+
init_events();
|
|
954
|
+
init_env();
|
|
955
|
+
init_reputation();
|
|
956
|
+
init_gate();
|
|
957
|
+
}
|
|
958
|
+
});
|
|
959
|
+
|
|
960
|
+
// src/browser/index.js
|
|
961
|
+
var index_exports = {};
|
|
962
|
+
__export(index_exports, {
|
|
963
|
+
ACCESS_MODES: () => ACCESS_MODES,
|
|
964
|
+
CONTENT_TYPES: () => CONTENT_TYPES,
|
|
965
|
+
NIBGATE_CONTENT_HASH_NAMESPACE: () => NIBGATE_CONTENT_HASH_NAMESPACE,
|
|
966
|
+
NIBGATE_CONTENT_SETTING_FIELDS: () => NIBGATE_CONTENT_SETTING_FIELDS,
|
|
967
|
+
NIBGATE_REPUTATION_ABI: () => NIBGATE_REPUTATION_ABI,
|
|
968
|
+
NIBGATE_REPUTATION_CHAIN_ID: () => NIBGATE_REPUTATION_CHAIN_ID,
|
|
969
|
+
NIBGATE_REPUTATION_CHAIN_NAME: () => NIBGATE_REPUTATION_CHAIN_NAME,
|
|
970
|
+
NIBGATE_REPUTATION_CONTRACT: () => NIBGATE_REPUTATION_CONTRACT,
|
|
971
|
+
NIBGATE_REPUTATION_RPC_URL: () => NIBGATE_REPUTATION_RPC_URL,
|
|
972
|
+
PAYMENT_RAILS: () => PAYMENT_RAILS,
|
|
973
|
+
UNLOCK_MODES: () => UNLOCK_MODES,
|
|
974
|
+
checkResourceAccess: () => checkResourceAccess,
|
|
975
|
+
contentRatingHash: () => contentRatingHash,
|
|
976
|
+
createCircleGatewayBrowserAdapter: () => createCircleGatewayBrowserAdapter2,
|
|
977
|
+
createEvmGatewayUnlock: () => createEvmGatewayUnlock,
|
|
978
|
+
createGate: () => createGate,
|
|
979
|
+
createHostedUnlock: () => createHostedUnlock,
|
|
980
|
+
createNibgate: () => createNibgate,
|
|
981
|
+
createNibgateContentSettings: () => createNibgateContentSettings,
|
|
982
|
+
createOnchainRating: () => createOnchainRating,
|
|
983
|
+
createTransferCheckout: () => createTransferCheckout,
|
|
984
|
+
createWalletCheckout: () => createWalletCheckout,
|
|
985
|
+
mountRatingUI: () => mountRatingUI,
|
|
986
|
+
nibgate: () => nibgate,
|
|
987
|
+
normalizeAccessPolicy: () => normalizeAccessPolicy,
|
|
988
|
+
normalizeContentType: () => normalizeContentType,
|
|
989
|
+
normalizePaymentRail: () => normalizePaymentRail,
|
|
990
|
+
normalizeResource: () => normalizeResource,
|
|
991
|
+
normalizeUnlockPolicy: () => normalizeUnlockPolicy,
|
|
992
|
+
payAndUnlockResource: () => payAndUnlockResource,
|
|
993
|
+
payWithPaymentSignature: () => payWithPaymentSignature,
|
|
994
|
+
payWithTransfer: () => payWithTransfer,
|
|
995
|
+
rateContentOnchain: () => rateContentOnchain,
|
|
996
|
+
rateResource: () => rateResource,
|
|
997
|
+
renderDefaultGatewayWalletUI: () => renderDefaultGatewayWalletUI,
|
|
998
|
+
renderDefaultRatingUI: () => renderDefaultRatingUI,
|
|
999
|
+
renderDefaultUnlockUI: () => renderDefaultUnlockUI,
|
|
1000
|
+
reviewTextHash: () => reviewTextHash,
|
|
1001
|
+
settingsToAccessPolicy: () => settingsToAccessPolicy,
|
|
1002
|
+
settingsToUnlockPolicy: () => settingsToUnlockPolicy,
|
|
1003
|
+
setupResourcePage: () => setupResourcePage,
|
|
1004
|
+
trackResourcePage: () => trackResourcePage,
|
|
1005
|
+
validateResourceMetadata: () => validateResourceMetadata
|
|
1006
|
+
});
|
|
1007
|
+
init_gate();
|
|
921
1008
|
|
|
922
|
-
// src/browser/
|
|
1009
|
+
// src/browser/access.js
|
|
1010
|
+
init_events();
|
|
1011
|
+
init_storage();
|
|
923
1012
|
init_json();
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
return options.accessPath || resource.accessPath || "/api/nibgate/access";
|
|
932
|
-
}
|
|
933
|
-
function createEvmGatewayUnlock(resource, options = {}) {
|
|
1013
|
+
init_gate();
|
|
1014
|
+
|
|
1015
|
+
// src/browser/track.js
|
|
1016
|
+
init_resource();
|
|
1017
|
+
init_env();
|
|
1018
|
+
init_gate();
|
|
1019
|
+
function trackResourcePage(resource, options = {}) {
|
|
934
1020
|
const item = createGate(resource, options.gateOptions || {});
|
|
935
|
-
const
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
const network = options.network || "eip155:5042002";
|
|
939
|
-
const statusTarget = typeof options.status === "string" ? win?.document.querySelector(options.status) : options.status;
|
|
940
|
-
const connectButton = typeof options.connectButton === "string" ? win?.document.querySelector(options.connectButton) : options.connectButton;
|
|
941
|
-
const disconnectButton = typeof options.disconnectButton === "string" ? win?.document.querySelector(options.disconnectButton) : options.disconnectButton;
|
|
942
|
-
const unlockButton = typeof options.unlockButton === "string" ? win?.document.querySelector(options.unlockButton) : options.unlockButton;
|
|
943
|
-
const clearButton = typeof options.clearButton === "string" ? win?.document.querySelector(options.clearButton) : options.clearButton;
|
|
944
|
-
const walletLabel = typeof options.walletLabel === "string" ? win?.document.querySelector(options.walletLabel) : options.walletLabel;
|
|
945
|
-
const unlockedTarget = typeof options.unlockedTarget === "string" ? win?.document.querySelector(options.unlockedTarget) : options.unlockedTarget;
|
|
946
|
-
let walletAddress = "";
|
|
947
|
-
let busy = false;
|
|
948
|
-
function setStatus(message) {
|
|
949
|
-
if (typeof options.onStatus === "function") options.onStatus(message);
|
|
950
|
-
if (statusTarget) statusTarget.textContent = message || "";
|
|
951
|
-
}
|
|
952
|
-
function shortAddress(address) {
|
|
953
|
-
return address ? `${address.slice(0, 6)}...${address.slice(-4)}` : "";
|
|
954
|
-
}
|
|
955
|
-
function provider() {
|
|
956
|
-
return win?.ethereum || options.provider || null;
|
|
1021
|
+
const validation = validateResourceMetadata(item.resource, options.validation || {});
|
|
1022
|
+
if ((validation.warnings.length || validation.errors.length) && options.warn !== false && browserWindow()?.console?.warn) {
|
|
1023
|
+
browserWindow().console.warn("Nibgate content metadata needs attention", validation);
|
|
957
1024
|
}
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
1025
|
+
item.content({ source: options.source, metadataQuality: { score: validation.score, warnings: validation.warnings, errors: validation.errors }, ...options.content || {} });
|
|
1026
|
+
item.view({
|
|
1027
|
+
source: options.source,
|
|
1028
|
+
path: options.path || browserWindow()?.location?.pathname || item.resource.path,
|
|
1029
|
+
referrer: options.referrer ?? browserWindow()?.document?.referrer ?? "",
|
|
1030
|
+
...options.view || {}
|
|
1031
|
+
});
|
|
1032
|
+
return item;
|
|
1033
|
+
}
|
|
1034
|
+
function setupResourcePage(resource, options = {}) {
|
|
1035
|
+
const item = trackResourcePage(resource, options);
|
|
1036
|
+
const win = browserWindow();
|
|
1037
|
+
if (!win) return item;
|
|
1038
|
+
const button = typeof options.button === "string" ? win.document.querySelector(options.button) : options.button;
|
|
1039
|
+
const statusElement = typeof options.status === "string" ? win.document.querySelector(options.status) : options.status;
|
|
1040
|
+
const setStatus = options.onStatus || ((message) => {
|
|
1041
|
+
if (statusElement) statusElement.textContent = message || "";
|
|
1042
|
+
});
|
|
1043
|
+
if (button) {
|
|
1044
|
+
button.addEventListener("click", async () => {
|
|
1045
|
+
button.disabled = true;
|
|
1046
|
+
try {
|
|
1047
|
+
await checkResourceAccess(resource, { ...options, onStatus: setStatus });
|
|
1048
|
+
} finally {
|
|
1049
|
+
button.disabled = false;
|
|
963
1050
|
}
|
|
964
1051
|
});
|
|
965
1052
|
}
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
1053
|
+
return item;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
// src/browser/access.js
|
|
1057
|
+
async function checkResourceAccess(resource, options = {}) {
|
|
1058
|
+
const item = createGate(resource, options.gateOptions || {});
|
|
1059
|
+
const accessPath = options.accessPath || item.resource.accessPath || "/api/nibgate/access";
|
|
1060
|
+
const status2 = typeof options.onStatus === "function" ? options.onStatus : () => {
|
|
1061
|
+
};
|
|
1062
|
+
status2(options.checkingMessage || "Checking access route...");
|
|
1063
|
+
item.unlockStarted({ source: options.source, paymentProvider: options.paymentProvider || "nibgate-access-route" });
|
|
1064
|
+
const response = await fetch(accessPath, {
|
|
1065
|
+
method: options.method || "GET",
|
|
1066
|
+
headers: {
|
|
1067
|
+
accept: "application/json",
|
|
1068
|
+
...getPaymentProof(item.resource) ? { "x-nibgate-payment-proof": getPaymentProof(item.resource) } : {},
|
|
1069
|
+
...options.headers || {}
|
|
1070
|
+
},
|
|
1071
|
+
body: options.body
|
|
1072
|
+
});
|
|
1073
|
+
const payload = await response.json().catch(() => ({}));
|
|
1074
|
+
if (response.status === 402) {
|
|
1075
|
+
item.track("payment_challenge_returned", { source: options.source, challenge: payload, resource: item.resource });
|
|
1076
|
+
status2(options.challengeMessage || "Payment challenge returned. Continue with checkout.");
|
|
1077
|
+
if (typeof options.createPaymentSignature === "function" || typeof options.checkout === "function") {
|
|
1078
|
+
return payWithPaymentSignature(resource, {
|
|
1079
|
+
...options,
|
|
1080
|
+
challenge: payload,
|
|
1081
|
+
paymentRequiredHeader: response.headers.get("PAYMENT-REQUIRED") || response.headers.get("payment-required") || ""
|
|
1082
|
+
});
|
|
996
1083
|
}
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
const evm = provider();
|
|
1002
|
-
if (evm?.request && walletAddress) {
|
|
1003
|
-
try {
|
|
1004
|
-
await evm.request({ method: "wallet_revokePermissions", params: [{ eth_accounts: {} }] });
|
|
1005
|
-
} catch (_error) {
|
|
1006
|
-
}
|
|
1084
|
+
if (options.autoPay && options.payPath) {
|
|
1085
|
+
const paymentResult = await payAndUnlockResource(resource, options);
|
|
1086
|
+
if (paymentResult.ok && options.retryAfterPay !== false) {
|
|
1087
|
+
return checkResourceAccess(resource, { ...options, autoPay: false });
|
|
1007
1088
|
}
|
|
1008
|
-
|
|
1009
|
-
renderWallet();
|
|
1010
|
-
setStatus(options.disconnectMessage || "Wallet disconnected for this page.");
|
|
1011
|
-
return true;
|
|
1012
|
-
} finally {
|
|
1013
|
-
setBusy(false);
|
|
1089
|
+
return paymentResult;
|
|
1014
1090
|
}
|
|
1091
|
+
return { ok: false, status: response.status, challenge: payload, resource: item.resource, response };
|
|
1015
1092
|
}
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
if (!walletAddress) await connect();
|
|
1020
|
-
const gatewayWallet = await createCircleGatewayBrowserAdapter2({
|
|
1021
|
-
network,
|
|
1022
|
-
signer: {
|
|
1023
|
-
address: walletAddress,
|
|
1024
|
-
signTypedData: (typedData) => evm.request({ method: "eth_signTypedData_v4", params: [walletAddress, stringifyJson(typedData)] })
|
|
1025
|
-
},
|
|
1026
|
-
clientModule: options.circleClientModule
|
|
1027
|
-
});
|
|
1028
|
-
return gatewayWallet.pay(input);
|
|
1093
|
+
if (!response.ok) {
|
|
1094
|
+
status2(payload.error || options.errorMessage || "Access check failed");
|
|
1095
|
+
return { ok: false, status: response.status, error: payload.error || "Access check failed", payload, resource: item.resource, response };
|
|
1029
1096
|
}
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
setBusy(true);
|
|
1035
|
-
setStatus("Requesting Gateway unlock...");
|
|
1036
|
-
const result = await checkResourceAccess(item.resource, {
|
|
1037
|
-
accessPath,
|
|
1038
|
-
source,
|
|
1039
|
-
paymentProvider: options.paymentProvider || "circle-gateway-browser",
|
|
1040
|
-
challengeMessage: options.challengeMessage || "Gateway payment required. Connect your wallet to continue...",
|
|
1041
|
-
paymentMessage: options.paymentMessage || "Approve the Gateway payment proof in your wallet...",
|
|
1042
|
-
successMessage: options.successMessage || `Unlocked ${item.resource.title || "content"}.`,
|
|
1043
|
-
method: options.method,
|
|
1044
|
-
headers: options.headers,
|
|
1045
|
-
body: options.body,
|
|
1046
|
-
checkout,
|
|
1047
|
-
onStatus: setStatus
|
|
1048
|
-
});
|
|
1049
|
-
if (result.ok) {
|
|
1050
|
-
setUnlocked(true, result.payment || {});
|
|
1051
|
-
if (typeof options.onUnlock === "function") options.onUnlock(result);
|
|
1052
|
-
}
|
|
1053
|
-
return result;
|
|
1054
|
-
} catch (error) {
|
|
1055
|
-
const message = error?.message || "Unlock failed. Please try again.";
|
|
1056
|
-
setStatus(message);
|
|
1057
|
-
return { ok: false, status: 0, error: message, resource: item.resource };
|
|
1058
|
-
} finally {
|
|
1059
|
-
setBusy(false);
|
|
1060
|
-
renderWallet();
|
|
1061
|
-
}
|
|
1097
|
+
const payment = options.payment || payload.payment || null;
|
|
1098
|
+
if (payment) {
|
|
1099
|
+
item.unlockCompleted(payment);
|
|
1100
|
+
item.paymentCompleted(payment);
|
|
1062
1101
|
}
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1102
|
+
status2(options.successMessage || "Access allowed and Nibgate events emitted.");
|
|
1103
|
+
return { ok: true, status: response.status, payload, payment, resource: item.resource, response };
|
|
1104
|
+
}
|
|
1105
|
+
async function payWithPaymentSignature(resource, options = {}) {
|
|
1106
|
+
const item = createGate(resource, options.gateOptions || {});
|
|
1107
|
+
const accessPath = options.accessPath || item.resource.accessPath || "/api/nibgate/access";
|
|
1108
|
+
const status2 = typeof options.onStatus === "function" ? options.onStatus : () => {
|
|
1109
|
+
};
|
|
1110
|
+
status2(options.paymentMessage || "Waiting for wallet payment approval...");
|
|
1111
|
+
item.unlockStarted({ source: options.source, paymentProvider: options.paymentProvider || "wallet-gateway" });
|
|
1112
|
+
let paymentSignature = options.paymentSignature || "";
|
|
1113
|
+
let paymentMemo = options.memo || "";
|
|
1114
|
+
let paymentMetadata = options.payment || {};
|
|
1115
|
+
if (!paymentSignature) {
|
|
1116
|
+
const paymentRequiredHeader = options.paymentRequiredHeader || "";
|
|
1117
|
+
const challenge = options.challenge || null;
|
|
1118
|
+
const checkout = options.createPaymentSignature || options.checkout;
|
|
1119
|
+
const result = await checkout({
|
|
1120
|
+
resource: item.resource,
|
|
1121
|
+
challenge,
|
|
1122
|
+
paymentRequiredHeader,
|
|
1123
|
+
accessPath
|
|
1124
|
+
});
|
|
1125
|
+
paymentSignature = result?.paymentSignature || result?.signature || result?.payment || "";
|
|
1126
|
+
paymentMemo = result?.memo || result?.paymentMemo || "";
|
|
1127
|
+
paymentMetadata = result?.metadata || result?.paymentMetadata || result || {};
|
|
1067
1128
|
}
|
|
1068
|
-
|
|
1069
|
-
const
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1129
|
+
if (!paymentSignature) {
|
|
1130
|
+
const error = "Wallet did not return a payment signature.";
|
|
1131
|
+
item.track("payment_failed", { source: options.source, error });
|
|
1132
|
+
status2(error);
|
|
1133
|
+
return { ok: false, status: 400, error, resource: item.resource };
|
|
1134
|
+
}
|
|
1135
|
+
const response = await fetch(accessPath, {
|
|
1136
|
+
method: options.method || "GET",
|
|
1137
|
+
headers: {
|
|
1138
|
+
accept: "application/json",
|
|
1139
|
+
"payment-signature": paymentSignature,
|
|
1140
|
+
...paymentMemo ? { "payment-memo": paymentMemo } : {},
|
|
1141
|
+
...options.headers || {}
|
|
1074
1142
|
}
|
|
1075
|
-
|
|
1076
|
-
|
|
1143
|
+
});
|
|
1144
|
+
const responseText = await response.text();
|
|
1145
|
+
let payload = {};
|
|
1146
|
+
try {
|
|
1147
|
+
payload = responseText ? JSON.parse(responseText) : {};
|
|
1148
|
+
} catch (_error) {
|
|
1149
|
+
payload = { error: responseText || "Payment verification failed" };
|
|
1077
1150
|
}
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
trackResourcePage(item.resource, { source });
|
|
1085
|
-
return controller;
|
|
1151
|
+
if (!response.ok) {
|
|
1152
|
+
const detail = payload.detail || payload.reason || payload.invalidReason || payload.error || responseText || "Payment verification failed";
|
|
1153
|
+
const error = typeof detail === "string" ? detail : stringifyJson(detail);
|
|
1154
|
+
item.track("payment_failed", { source: options.source, status: response.status, error, ...paymentMetadata });
|
|
1155
|
+
status2(options.paymentErrorMessage || error);
|
|
1156
|
+
return { ok: false, status: response.status, error, payload, resource: item.resource, response };
|
|
1086
1157
|
}
|
|
1087
|
-
const
|
|
1088
|
-
|
|
1089
|
-
|
|
1158
|
+
const payment = payload.payment || {
|
|
1159
|
+
paymentProvider: options.paymentProvider || "wallet-gateway",
|
|
1160
|
+
paymentId: paymentSignature,
|
|
1161
|
+
memo: paymentMemo,
|
|
1162
|
+
amount: Number(item.resource.price || 0),
|
|
1163
|
+
revenue: Number(item.resource.price || 0),
|
|
1164
|
+
currency: item.resource.currency || "USDC",
|
|
1165
|
+
...paymentMetadata
|
|
1166
|
+
};
|
|
1167
|
+
storePaymentProof(item.resource, payload.unlockProof);
|
|
1168
|
+
item.markUnlocked(payment);
|
|
1169
|
+
status2(options.paymentSuccessMessage || "Payment verified. Content unlocked.");
|
|
1170
|
+
return { ok: true, status: response.status, payload, payment, resource: item.resource, response };
|
|
1090
1171
|
}
|
|
1091
|
-
function
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1172
|
+
async function payAndUnlockResource(resource, options = {}) {
|
|
1173
|
+
const item = createGate(resource, options.gateOptions || {});
|
|
1174
|
+
const payPath = options.payPath || item.resource.payPath || "/api/nibgate/pay";
|
|
1175
|
+
const status2 = typeof options.onStatus === "function" ? options.onStatus : () => {
|
|
1176
|
+
};
|
|
1177
|
+
status2(options.paymentMessage || "Starting payment...");
|
|
1178
|
+
item.unlockStarted({ source: options.source, paymentProvider: options.paymentProvider || "circle-gateway" });
|
|
1179
|
+
const response = await fetch(payPath, {
|
|
1180
|
+
method: options.payMethod || "POST",
|
|
1181
|
+
headers: {
|
|
1182
|
+
accept: "application/json",
|
|
1183
|
+
"content-type": "application/json",
|
|
1184
|
+
...options.payHeaders || {}
|
|
1185
|
+
},
|
|
1186
|
+
body: JSON.stringify({ resource: item.resource, ...options.payPayload || {} })
|
|
1096
1187
|
});
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
var NIBGATE_CONTENT_HASH_NAMESPACE = "nibgate:content:v1";
|
|
1103
|
-
var NIBGATE_REPUTATION_CHAIN_ID = 5042002;
|
|
1104
|
-
var NIBGATE_REPUTATION_CHAIN_NAME = "Arc Testnet";
|
|
1105
|
-
var NIBGATE_REPUTATION_RPC_URL = "https://rpc.testnet.arc.network";
|
|
1106
|
-
var NIBGATE_REPUTATION_CONTRACT = "0x9f27fd62e75f86a3c7addfdba443aab1f930e281";
|
|
1107
|
-
var NIBGATE_REPUTATION_ABI = [
|
|
1108
|
-
{
|
|
1109
|
-
type: "function",
|
|
1110
|
-
name: "rateContent",
|
|
1111
|
-
stateMutability: "nonpayable",
|
|
1112
|
-
inputs: [
|
|
1113
|
-
{ name: "contentId", type: "bytes32" },
|
|
1114
|
-
{ name: "rating", type: "uint8" },
|
|
1115
|
-
{ name: "reviewHash", type: "bytes32" },
|
|
1116
|
-
{ name: "unlockRef", type: "string" }
|
|
1117
|
-
],
|
|
1118
|
-
outputs: []
|
|
1119
|
-
}
|
|
1120
|
-
];
|
|
1121
|
-
function stripHex(value = "") {
|
|
1122
|
-
return String(value || "").replace(/^0x/i, "").toLowerCase();
|
|
1123
|
-
}
|
|
1124
|
-
function wordRight(hex = "") {
|
|
1125
|
-
const clean = stripHex(hex);
|
|
1126
|
-
if (clean.length > 64) throw new Error("ABI word is too long.");
|
|
1127
|
-
return clean.padEnd(64, "0");
|
|
1128
|
-
}
|
|
1129
|
-
function numberWord(value = 0) {
|
|
1130
|
-
return Number(value || 0).toString(16).padStart(64, "0");
|
|
1131
|
-
}
|
|
1132
|
-
function utf8Hex(value = "") {
|
|
1133
|
-
return Array.from(new TextEncoder().encode(String(value || ""))).map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
1134
|
-
}
|
|
1135
|
-
function encodeString(value = "") {
|
|
1136
|
-
const hex = utf8Hex(value);
|
|
1137
|
-
const byteLength = hex.length / 2;
|
|
1138
|
-
const paddedLength = Math.ceil(byteLength / 32) * 64;
|
|
1139
|
-
return numberWord(byteLength) + hex.padEnd(paddedLength, "0");
|
|
1140
|
-
}
|
|
1141
|
-
function encodeRateContent({ contentId, ratingValue, reviewHash, unlockRef }) {
|
|
1142
|
-
return RATE_CONTENT_SELECTOR + wordRight(contentId) + numberWord(ratingValue) + wordRight(reviewHash || ZERO_HASH) + numberWord(128) + encodeString(unlockRef || "");
|
|
1143
|
-
}
|
|
1144
|
-
function contentRatingHash(_resource, options = {}) {
|
|
1145
|
-
const contentId = options.contentId || options.contentHash;
|
|
1146
|
-
if (!contentId) {
|
|
1147
|
-
throw new Error("contentId/contentHash is required. Use the Nibgate backend prepare endpoint or pass a known content hash.");
|
|
1188
|
+
const payload = await response.json().catch(() => ({}));
|
|
1189
|
+
if (!response.ok || !payload.ok) {
|
|
1190
|
+
item.track("payment_failed", { source: options.source, status: response.status, error: payload.error || "Payment failed", detail: payload.detail || "" });
|
|
1191
|
+
status2(payload.detail || payload.error || options.paymentErrorMessage || "Payment failed.");
|
|
1192
|
+
return { ok: false, status: response.status, payload, resource: item.resource, response };
|
|
1148
1193
|
}
|
|
1149
|
-
|
|
1194
|
+
const payment = payload.payment || {
|
|
1195
|
+
paymentProvider: options.paymentProvider || "circle-gateway",
|
|
1196
|
+
paymentId: payload.paymentId || `nibgate_payment_${Date.now()}`,
|
|
1197
|
+
amount: Number(item.resource.price || 0),
|
|
1198
|
+
revenue: Number(item.resource.price || 0),
|
|
1199
|
+
currency: item.resource.currency || "USDC"
|
|
1200
|
+
};
|
|
1201
|
+
storePaymentProof(item.resource, payload.unlockProof);
|
|
1202
|
+
item.markUnlocked(payment);
|
|
1203
|
+
status2(options.paymentSuccessMessage || "Payment verified. Content unlocked.");
|
|
1204
|
+
return { ok: true, status: response.status, payload, payment, resource: item.resource, response };
|
|
1150
1205
|
}
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1206
|
+
|
|
1207
|
+
// src/browser/checkout.js
|
|
1208
|
+
init_resource();
|
|
1209
|
+
init_env();
|
|
1210
|
+
function setElementText(target, message) {
|
|
1211
|
+
const win = browserWindow();
|
|
1212
|
+
if (!target || !win) return;
|
|
1213
|
+
const element = typeof target === "string" ? win.document.querySelector(target) : target;
|
|
1214
|
+
if (element) element.textContent = message || "";
|
|
1154
1215
|
}
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
method: "POST",
|
|
1161
|
-
headers: { "content-type": "application/json", ...options.indexHeaders || {} },
|
|
1162
|
-
body: JSON.stringify({
|
|
1163
|
-
siteId: options.siteId,
|
|
1164
|
-
token: options.token,
|
|
1165
|
-
resource,
|
|
1166
|
-
url: resource.url,
|
|
1167
|
-
path: resource.path
|
|
1168
|
-
})
|
|
1169
|
-
});
|
|
1170
|
-
const payload = await response.json().catch(() => ({}));
|
|
1171
|
-
if (!response.ok || !payload.contentHash) throw new Error(payload.error || "Could not prepare Nibgate onchain rating.");
|
|
1172
|
-
return payload;
|
|
1216
|
+
function setElementDisabled(target, disabled) {
|
|
1217
|
+
const win = browserWindow();
|
|
1218
|
+
if (!target || !win) return;
|
|
1219
|
+
const element = typeof target === "string" ? win.document.querySelector(target) : target;
|
|
1220
|
+
if (element && "disabled" in element) element.disabled = Boolean(disabled);
|
|
1173
1221
|
}
|
|
1174
|
-
|
|
1222
|
+
function createWalletCheckout(resource, options = {}) {
|
|
1175
1223
|
const normalized = normalizeResource(resource);
|
|
1176
|
-
const
|
|
1177
|
-
|
|
1178
|
-
const
|
|
1179
|
-
|
|
1180
|
-
const
|
|
1181
|
-
if (
|
|
1182
|
-
|
|
1183
|
-
const walletAddress = Array.isArray(accounts) ? accounts[0] || "" : "";
|
|
1184
|
-
if (!walletAddress) throw new Error("No wallet account selected.");
|
|
1185
|
-
const prepared = await prepareOnchainRating(normalized, options);
|
|
1186
|
-
const contentId = prepared.contentHash || prepared.contentId || contentRatingHash(normalized, options);
|
|
1187
|
-
const reviewHash = options.reviewHash || ZERO_HASH;
|
|
1188
|
-
const unlockRef = String(options.unlockRef || options.paymentId || options.txHash || "");
|
|
1189
|
-
const data = encodeRateContent({ contentId, ratingValue: rating.ratingValue, reviewHash, unlockRef });
|
|
1190
|
-
const txHash = await provider.request({
|
|
1191
|
-
method: "eth_sendTransaction",
|
|
1192
|
-
params: [{
|
|
1193
|
-
from: walletAddress,
|
|
1194
|
-
to: contractAddress,
|
|
1195
|
-
data
|
|
1196
|
-
}]
|
|
1197
|
-
});
|
|
1198
|
-
const payload = payloadWithResource(normalized, {
|
|
1199
|
-
rating: rating.rating,
|
|
1200
|
-
ratingValue: rating.ratingValue,
|
|
1201
|
-
walletAddress,
|
|
1202
|
-
txHash,
|
|
1203
|
-
contentHash: contentId,
|
|
1204
|
-
reviewHash,
|
|
1205
|
-
proofType: "onchain_pending",
|
|
1206
|
-
proof: unlockRef,
|
|
1207
|
-
paymentId: options.paymentId,
|
|
1208
|
-
actor: options.actor || "human"
|
|
1209
|
-
});
|
|
1210
|
-
emit("content_rating", payload);
|
|
1211
|
-
if (options.indexUrl) {
|
|
1212
|
-
await fetch(options.indexUrl, {
|
|
1213
|
-
method: "POST",
|
|
1214
|
-
headers: { "content-type": "application/json", ...options.indexHeaders || {} },
|
|
1215
|
-
body: JSON.stringify({
|
|
1216
|
-
siteId: options.siteId,
|
|
1217
|
-
token: options.token,
|
|
1218
|
-
txHash,
|
|
1219
|
-
resource: normalized,
|
|
1220
|
-
url: normalized.url,
|
|
1221
|
-
path: normalized.path,
|
|
1222
|
-
actor: options.actor || "human"
|
|
1223
|
-
})
|
|
1224
|
-
}).catch(() => null);
|
|
1224
|
+
const accessPath = options.accessPath || normalized.accessPath || "/api/nibgate/access";
|
|
1225
|
+
const button = options.button || null;
|
|
1226
|
+
const statusTarget = options.status || null;
|
|
1227
|
+
const status2 = typeof options.onStatus === "function" ? options.onStatus : (message) => setElementText(statusTarget, message);
|
|
1228
|
+
const checkout = options.checkout || options.createPaymentSignature || options.pay;
|
|
1229
|
+
if (typeof checkout !== "function") {
|
|
1230
|
+
throw new Error("createWalletCheckout requires checkout/createPaymentSignature/pay callback for the active wallet or Gateway adapter.");
|
|
1225
1231
|
}
|
|
1226
|
-
|
|
1232
|
+
async function unlock(extra = {}) {
|
|
1233
|
+
setElementDisabled(button, true);
|
|
1234
|
+
try {
|
|
1235
|
+
return await checkResourceAccess(normalized, {
|
|
1236
|
+
...options,
|
|
1237
|
+
...extra,
|
|
1238
|
+
accessPath,
|
|
1239
|
+
createPaymentSignature: checkout,
|
|
1240
|
+
onStatus: status2
|
|
1241
|
+
});
|
|
1242
|
+
} finally {
|
|
1243
|
+
setElementDisabled(button, false);
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
function mount() {
|
|
1247
|
+
const win = browserWindow();
|
|
1248
|
+
if (!win || !button) return { unlock };
|
|
1249
|
+
const element = typeof button === "string" ? win.document.querySelector(button) : button;
|
|
1250
|
+
if (element) element.addEventListener("click", () => unlock().catch((error) => status2(error.message || "Checkout failed.")));
|
|
1251
|
+
return { unlock };
|
|
1252
|
+
}
|
|
1253
|
+
return { resource: normalized, unlock, mount };
|
|
1227
1254
|
}
|
|
1228
1255
|
|
|
1229
|
-
// src/browser/
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1256
|
+
// src/browser/client.js
|
|
1257
|
+
init_resource();
|
|
1258
|
+
init_rating();
|
|
1259
|
+
init_events();
|
|
1260
|
+
init_gate();
|
|
1261
|
+
init_gate();
|
|
1262
|
+
|
|
1263
|
+
// src/browser/evm-gateway.js
|
|
1264
|
+
init_env();
|
|
1265
|
+
init_storage();
|
|
1266
|
+
init_json();
|
|
1267
|
+
init_gate();
|
|
1268
|
+
async function createCircleGatewayBrowserAdapter2(options = {}) {
|
|
1269
|
+
const gateway = await Promise.resolve().then(() => (init_gateway(), gateway_exports));
|
|
1270
|
+
return gateway.createCircleGatewayBrowserAdapter(options);
|
|
1241
1271
|
}
|
|
1242
|
-
|
|
1272
|
+
var HOSTED_PAY_URL = "https://api.nibgate.xyz/api/hub/pay";
|
|
1273
|
+
function resolveAccessPath(resource, options) {
|
|
1274
|
+
if (options.hosted || options.accessPath === "hosted") return HOSTED_PAY_URL;
|
|
1275
|
+
return options.accessPath || resource.accessPath || "/api/nibgate/access";
|
|
1276
|
+
}
|
|
1277
|
+
function createEvmGatewayUnlock(resource, options = {}) {
|
|
1243
1278
|
const item = createGate(resource, options.gateOptions || {});
|
|
1244
1279
|
const win = browserWindow();
|
|
1280
|
+
const accessPath = resolveAccessPath(item.resource, options);
|
|
1281
|
+
const source = options.source || "nibgate-evm-gateway";
|
|
1282
|
+
const network = options.network || "eip155:5042002";
|
|
1245
1283
|
const statusTarget = typeof options.status === "string" ? win?.document.querySelector(options.status) : options.status;
|
|
1246
|
-
const
|
|
1247
|
-
const
|
|
1248
|
-
const
|
|
1249
|
-
const
|
|
1250
|
-
const
|
|
1284
|
+
const connectButton = typeof options.connectButton === "string" ? win?.document.querySelector(options.connectButton) : options.connectButton;
|
|
1285
|
+
const disconnectButton = typeof options.disconnectButton === "string" ? win?.document.querySelector(options.disconnectButton) : options.disconnectButton;
|
|
1286
|
+
const unlockButton = typeof options.unlockButton === "string" ? win?.document.querySelector(options.unlockButton) : options.unlockButton;
|
|
1287
|
+
const clearButton = typeof options.clearButton === "string" ? win?.document.querySelector(options.clearButton) : options.clearButton;
|
|
1288
|
+
const walletLabel = typeof options.walletLabel === "string" ? win?.document.querySelector(options.walletLabel) : options.walletLabel;
|
|
1289
|
+
const unlockedTarget = typeof options.unlockedTarget === "string" ? win?.document.querySelector(options.unlockedTarget) : options.unlockedTarget;
|
|
1290
|
+
let walletAddress = "";
|
|
1251
1291
|
let busy = false;
|
|
1252
|
-
let payment = options.payment || null;
|
|
1253
1292
|
function setStatus(message) {
|
|
1254
1293
|
if (typeof options.onStatus === "function") options.onStatus(message);
|
|
1255
1294
|
if (statusTarget) statusTarget.textContent = message || "";
|
|
1256
1295
|
}
|
|
1296
|
+
function shortAddress(address) {
|
|
1297
|
+
return address ? `${address.slice(0, 6)}...${address.slice(-4)}` : "";
|
|
1298
|
+
}
|
|
1299
|
+
function provider() {
|
|
1300
|
+
return win?.ethereum || options.provider || null;
|
|
1301
|
+
}
|
|
1257
1302
|
function setBusy(value) {
|
|
1258
|
-
busy = Boolean(value);
|
|
1259
|
-
|
|
1260
|
-
if (button && "disabled" in button)
|
|
1303
|
+
busy = Boolean(value);
|
|
1304
|
+
[connectButton, disconnectButton, unlockButton, clearButton].forEach((button) => {
|
|
1305
|
+
if (button && "disabled" in button) {
|
|
1306
|
+
button.disabled = busy || button === connectButton && !provider() || button === disconnectButton && !walletAddress;
|
|
1307
|
+
}
|
|
1261
1308
|
});
|
|
1262
1309
|
}
|
|
1263
|
-
function
|
|
1264
|
-
|
|
1265
|
-
|
|
1310
|
+
function renderWallet() {
|
|
1311
|
+
const hasProvider = Boolean(provider());
|
|
1312
|
+
if (walletLabel) walletLabel.textContent = walletAddress ? shortAddress(walletAddress) : hasProvider ? "Ready to connect" : "No wallet detected";
|
|
1313
|
+
if (connectButton) connectButton.textContent = walletAddress ? "Connected" : "Connect wallet";
|
|
1314
|
+
if (disconnectButton) disconnectButton.textContent = "Disconnect";
|
|
1315
|
+
if (connectButton && "disabled" in connectButton) connectButton.disabled = busy || !hasProvider;
|
|
1316
|
+
if (disconnectButton && "disabled" in disconnectButton) disconnectButton.disabled = busy || !walletAddress;
|
|
1266
1317
|
}
|
|
1267
|
-
function
|
|
1268
|
-
if (
|
|
1269
|
-
if (
|
|
1270
|
-
|
|
1271
|
-
|
|
1318
|
+
function setUnlocked(isUnlocked, payment = {}) {
|
|
1319
|
+
if (unlockButton) unlockButton.textContent = isUnlocked ? "Unlocked" : `Unlock for ${item.resource.price} ${item.resource.currency || "USDC"}`;
|
|
1320
|
+
if (unlockedTarget) {
|
|
1321
|
+
if ("hidden" in unlockedTarget) unlockedTarget.hidden = !isUnlocked;
|
|
1322
|
+
unlockedTarget.setAttribute("aria-hidden", isUnlocked ? "false" : "true");
|
|
1323
|
+
}
|
|
1324
|
+
if (isUnlocked) item.markUnlocked(payment);
|
|
1272
1325
|
}
|
|
1273
|
-
function
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1326
|
+
async function connect() {
|
|
1327
|
+
setBusy(true);
|
|
1328
|
+
setStatus("Opening wallet connection...");
|
|
1329
|
+
try {
|
|
1330
|
+
const evm = provider();
|
|
1331
|
+
if (!evm) throw new Error(options.noWalletMessage || "Install or open an EVM wallet to continue.");
|
|
1332
|
+
const accounts = await evm.request({ method: "eth_requestAccounts" });
|
|
1333
|
+
walletAddress = Array.isArray(accounts) ? accounts[0] || "" : "";
|
|
1334
|
+
if (!walletAddress) throw new Error("No wallet account selected.");
|
|
1335
|
+
renderWallet();
|
|
1336
|
+
setStatus("Wallet connected. You can unlock now.");
|
|
1337
|
+
return walletAddress;
|
|
1338
|
+
} finally {
|
|
1339
|
+
setBusy(false);
|
|
1340
|
+
}
|
|
1277
1341
|
}
|
|
1278
|
-
async function
|
|
1342
|
+
async function disconnect() {
|
|
1279
1343
|
setBusy(true);
|
|
1280
1344
|
try {
|
|
1281
|
-
const
|
|
1282
|
-
if (
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1345
|
+
const evm = provider();
|
|
1346
|
+
if (evm?.request && walletAddress) {
|
|
1347
|
+
try {
|
|
1348
|
+
await evm.request({ method: "wallet_revokePermissions", params: [{ eth_accounts: {} }] });
|
|
1349
|
+
} catch (_error) {
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
walletAddress = "";
|
|
1353
|
+
renderWallet();
|
|
1354
|
+
setStatus(options.disconnectMessage || "Wallet disconnected for this page.");
|
|
1355
|
+
return true;
|
|
1356
|
+
} finally {
|
|
1357
|
+
setBusy(false);
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
async function checkout(input) {
|
|
1361
|
+
const evm = provider();
|
|
1362
|
+
if (!evm) throw new Error(options.noWalletMessage || "Install or open an EVM wallet to continue.");
|
|
1363
|
+
if (!walletAddress) await connect();
|
|
1364
|
+
const gatewayWallet = await createCircleGatewayBrowserAdapter2({
|
|
1365
|
+
network,
|
|
1366
|
+
signer: {
|
|
1367
|
+
address: walletAddress,
|
|
1368
|
+
signTypedData: (typedData) => evm.request({ method: "eth_signTypedData_v4", params: [walletAddress, stringifyJson(typedData)] })
|
|
1369
|
+
},
|
|
1370
|
+
clientModule: options.circleClientModule
|
|
1371
|
+
});
|
|
1372
|
+
return gatewayWallet.pay(input);
|
|
1373
|
+
}
|
|
1374
|
+
async function unlock() {
|
|
1375
|
+
setBusy(true);
|
|
1376
|
+
try {
|
|
1377
|
+
if (!walletAddress) await connect();
|
|
1378
|
+
setBusy(true);
|
|
1379
|
+
setStatus("Requesting Gateway unlock...");
|
|
1380
|
+
const result = await checkResourceAccess(item.resource, {
|
|
1381
|
+
accessPath,
|
|
1382
|
+
source,
|
|
1383
|
+
paymentProvider: options.paymentProvider || "circle-gateway-browser",
|
|
1384
|
+
challengeMessage: options.challengeMessage || "Gateway payment required. Connect your wallet to continue...",
|
|
1385
|
+
paymentMessage: options.paymentMessage || "Approve the Gateway payment proof in your wallet...",
|
|
1386
|
+
successMessage: options.successMessage || `Unlocked ${item.resource.title || "content"}.`,
|
|
1387
|
+
method: options.method,
|
|
1388
|
+
headers: options.headers,
|
|
1389
|
+
body: options.body,
|
|
1390
|
+
checkout,
|
|
1391
|
+
onStatus: setStatus
|
|
1392
|
+
});
|
|
1393
|
+
if (result.ok) {
|
|
1394
|
+
setUnlocked(true, result.payment || {});
|
|
1395
|
+
if (typeof options.onUnlock === "function") options.onUnlock(result);
|
|
1396
|
+
}
|
|
1289
1397
|
return result;
|
|
1290
1398
|
} catch (error) {
|
|
1291
|
-
const message = error?.message ||
|
|
1399
|
+
const message = error?.message || "Unlock failed. Please try again.";
|
|
1292
1400
|
setStatus(message);
|
|
1293
|
-
|
|
1294
|
-
throw error;
|
|
1401
|
+
return { ok: false, status: 0, error: message, resource: item.resource };
|
|
1295
1402
|
} finally {
|
|
1296
1403
|
setBusy(false);
|
|
1404
|
+
renderWallet();
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
function clear() {
|
|
1408
|
+
clearPaymentProof(item.resource);
|
|
1409
|
+
setUnlocked(false);
|
|
1410
|
+
setStatus("Local payment proof cleared. The next unlock will require Gateway payment again.");
|
|
1411
|
+
}
|
|
1412
|
+
async function hydrate() {
|
|
1413
|
+
const evm = provider();
|
|
1414
|
+
try {
|
|
1415
|
+
const accounts = evm ? await evm.request({ method: "eth_accounts" }) : [];
|
|
1416
|
+
walletAddress = Array.isArray(accounts) ? accounts[0] || "" : "";
|
|
1417
|
+
} catch {
|
|
1297
1418
|
}
|
|
1419
|
+
renderWallet();
|
|
1420
|
+
setUnlocked(false);
|
|
1298
1421
|
}
|
|
1299
1422
|
function mount() {
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1423
|
+
connectButton?.addEventListener?.("click", () => connect().catch((error) => setStatus(error?.message || "Could not connect wallet.")));
|
|
1424
|
+
disconnectButton?.addEventListener?.("click", () => disconnect().catch((error) => setStatus(error?.message || "Could not disconnect wallet.")));
|
|
1425
|
+
unlockButton?.addEventListener?.("click", () => unlock());
|
|
1426
|
+
clearButton?.addEventListener?.("click", clear);
|
|
1427
|
+
hydrate();
|
|
1428
|
+
trackResourcePage(item.resource, { source });
|
|
1304
1429
|
return controller;
|
|
1305
1430
|
}
|
|
1306
|
-
const controller = { resource: item.resource,
|
|
1431
|
+
const controller = { resource: item.resource, connect, disconnect, unlock, clear, hydrate, mount, getWalletAddress: () => walletAddress };
|
|
1307
1432
|
if (options.autoMount !== false) mount();
|
|
1308
1433
|
return controller;
|
|
1309
1434
|
}
|
|
1310
|
-
function
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
if (!target) return null;
|
|
1316
|
-
const stars = [1, 2, 3, 4, 5];
|
|
1317
|
-
let selectedRating = 0;
|
|
1318
|
-
const container = win.document.createElement("div");
|
|
1319
|
-
container.className = "nibgate-rating-ui";
|
|
1320
|
-
container.style.cssText = "display:flex;align-items:center;gap:4px;padding:8px 0";
|
|
1321
|
-
const starButtons = stars.map((value) => {
|
|
1322
|
-
const btn = win.document.createElement("button");
|
|
1323
|
-
btn.type = "button";
|
|
1324
|
-
btn.dataset.nibgateRatingValue = String(value);
|
|
1325
|
-
btn.setAttribute("aria-label", `${value} star${value > 1 ? "s" : ""}`);
|
|
1326
|
-
btn.innerHTML = "\u2606";
|
|
1327
|
-
btn.style.cssText = "background:none;border:none;font-size:24px;cursor:pointer;color:#ccc;transition:color 0.15s;padding:2px;line-height:1";
|
|
1328
|
-
btn.addEventListener("mouseenter", () => {
|
|
1329
|
-
starButtons.forEach((b, i) => b.style.color = i < value ? "#f5b342" : "#ccc");
|
|
1330
|
-
});
|
|
1331
|
-
btn.addEventListener("mouseleave", () => {
|
|
1332
|
-
starButtons.forEach((b, i) => b.style.color = i < selectedRating ? "#f5b342" : "#ccc");
|
|
1333
|
-
});
|
|
1334
|
-
btn.addEventListener("click", () => {
|
|
1335
|
-
selectedRating = value;
|
|
1336
|
-
starButtons.forEach((b, i) => b.style.color = i < value ? "#f5b342" : "#ccc");
|
|
1337
|
-
rateResource(item.resource, { rating: value }).catch(() => {
|
|
1338
|
-
});
|
|
1339
|
-
});
|
|
1340
|
-
container.appendChild(btn);
|
|
1341
|
-
return btn;
|
|
1435
|
+
function createHostedUnlock(resource, options = {}) {
|
|
1436
|
+
return createEvmGatewayUnlock(resource, {
|
|
1437
|
+
...options,
|
|
1438
|
+
hosted: true,
|
|
1439
|
+
noWalletMessage: options.noWalletMessage || "Install MetaMask or another EVM wallet to unlock premium content."
|
|
1342
1440
|
});
|
|
1343
|
-
const statusEl = win.document.createElement("span");
|
|
1344
|
-
statusEl.style.cssText = "font-size:13px;color:#888;margin-left:8px";
|
|
1345
|
-
statusEl.textContent = options.label || "Rate this content";
|
|
1346
|
-
container.appendChild(statusEl);
|
|
1347
|
-
target.appendChild(container);
|
|
1348
|
-
function rate(r, input = {}) {
|
|
1349
|
-
return item.rate({ ...input, rating: r });
|
|
1350
|
-
}
|
|
1351
|
-
function setRating(value) {
|
|
1352
|
-
selectedRating = value;
|
|
1353
|
-
starButtons.forEach((b, i) => b.style.color = i < value ? "#f5b342" : "#ccc");
|
|
1354
|
-
}
|
|
1355
|
-
return { resource: item.resource, container, setRating, rate };
|
|
1356
1441
|
}
|
|
1357
1442
|
|
|
1443
|
+
// src/browser/client.js
|
|
1444
|
+
init_rating_ui();
|
|
1445
|
+
|
|
1358
1446
|
// src/browser/transfer.js
|
|
1447
|
+
init_resource();
|
|
1359
1448
|
function createTransferCheckout(resource, options = {}) {
|
|
1360
1449
|
const normalized = normalizeResource({ ...resource, paymentRail: "transfer" });
|
|
1361
1450
|
const sendTransfer = options.sendTransfer || options.transfer;
|
|
@@ -1489,7 +1578,449 @@ var Nibgate = (() => {
|
|
|
1489
1578
|
var nibgate = createNibgate();
|
|
1490
1579
|
setDefaultClient(nibgate);
|
|
1491
1580
|
|
|
1581
|
+
// src/browser/index.js
|
|
1582
|
+
init_rating_ui();
|
|
1583
|
+
init_reputation();
|
|
1584
|
+
|
|
1585
|
+
// src/browser/default-ui.js
|
|
1586
|
+
var SID = "nibgate-ui-styles";
|
|
1587
|
+
var theme = {
|
|
1588
|
+
bg: "var(--bg, #f4f4f0)",
|
|
1589
|
+
fg: "var(--fg, #0a0a0a)",
|
|
1590
|
+
muted: "var(--muted, #6b6862)",
|
|
1591
|
+
border: "var(--border, #cecdc3)",
|
|
1592
|
+
accent: "var(--accent, #7c9a6d)",
|
|
1593
|
+
accentSoft: "var(--accent-soft, #d8e8d3)",
|
|
1594
|
+
cardHover: "var(--card-hover, #e0ddd3)"
|
|
1595
|
+
};
|
|
1596
|
+
var css = (s) => Object.entries(s).map(([k, v]) => `${k.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase())}:${v}`).join(";");
|
|
1597
|
+
function h(tag, attrs, children) {
|
|
1598
|
+
const e = document.createElement(tag);
|
|
1599
|
+
for (const [k, v] of Object.entries(attrs || {})) {
|
|
1600
|
+
if (k === "dataset") Object.assign(e.dataset, v);
|
|
1601
|
+
else if (k.startsWith("on")) e.addEventListener(k.slice(2).toLowerCase(), v);
|
|
1602
|
+
else if (k === "style" && typeof v === "object") e.style.cssText = css(v);
|
|
1603
|
+
else if (k === "cls") e.className = v;
|
|
1604
|
+
else e.setAttribute(k, String(v));
|
|
1605
|
+
}
|
|
1606
|
+
if (typeof children === "string") e.innerHTML = children;
|
|
1607
|
+
else if (children) (Array.isArray(children) ? children : [children]).forEach((c) => {
|
|
1608
|
+
if (c != null) e.appendChild(typeof c === "string" ? document.createTextNode(c) : c);
|
|
1609
|
+
});
|
|
1610
|
+
return e;
|
|
1611
|
+
}
|
|
1612
|
+
function inject() {
|
|
1613
|
+
if (document.getElementById(SID)) return;
|
|
1614
|
+
const s = h("style", { id: SID }, `
|
|
1615
|
+
@keyframes nfade { from { opacity:0;transform:translateY(6px) } to { opacity:1;transform:translateY(0) } }
|
|
1616
|
+
@keyframes nscale { from { opacity:0;transform:scale(0.96) } to { opacity:1;transform:scale(1) } }
|
|
1617
|
+
@keyframes nshimmer { 0% { transform:translateX(-100%) } 100% { transform:translateX(100%) } }
|
|
1618
|
+
|
|
1619
|
+
.nui { font-family:var(--font-content,'Kumbh Sans','ABC Favorit',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif);color:${theme.fg};line-height:1.5;-webkit-font-smoothing:antialiased;font-size:19px }
|
|
1620
|
+
.nui *,.nui *::before,.nui *::after { box-sizing:border-box }
|
|
1621
|
+
.nui-btn { display:flex;align-items:center;justify-content:center;gap:8px;font-size:18px;font-weight:600;padding:14px 28px;border-radius:12px;cursor:pointer;transition:all.12s;font-family:inherit;line-height:1;border:none }
|
|
1622
|
+
.nui-btn:disabled { opacity:0.35;cursor:default;pointer-events:none }
|
|
1623
|
+
.nui-btn:focus-visible { outline:2px solid ${theme.accent};outline-offset:2px }
|
|
1624
|
+
.nui-btn-primary { background:${theme.accent};color:${theme.bg} }
|
|
1625
|
+
.nui-btn-primary:hover:not(:disabled) { opacity:0.9 }
|
|
1626
|
+
.nui-btn-primary:active:not(:disabled) { transform:scale(.98) }
|
|
1627
|
+
.nui-btn-outline { background:transparent;border:1px solid ${theme.border};color:${theme.fg} }
|
|
1628
|
+
.nui-btn-outline:hover:not(:disabled) { border-color:${theme.accent};color:${theme.accent} }
|
|
1629
|
+
.nui-input { padding:14px 16px;font-size:18px;border-radius:12px;border:1px solid ${theme.border};background:transparent;color:${theme.fg};width:100%;font-family:inherit;outline:none;transition:border-color.12s }
|
|
1630
|
+
.nui-input:focus { border-color:${theme.accent} }
|
|
1631
|
+
.nui-input::placeholder { color:${theme.muted} }
|
|
1632
|
+
.nui-label { font-size:17px;font-weight:600;color:${theme.muted};margin-bottom:8px;display:block }
|
|
1633
|
+
.nui-mono { font-family:ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,monospace }
|
|
1634
|
+
.nui-stat { font-size:18px;color:${theme.muted};line-height:1.4;min-height:28px }
|
|
1635
|
+
.nui-stat-err { color:#dc2626 }
|
|
1636
|
+
.nui-stat-ok { color:#16a34a }
|
|
1637
|
+
`);
|
|
1638
|
+
document.head.appendChild(s);
|
|
1639
|
+
}
|
|
1640
|
+
function el(tag, a, c) {
|
|
1641
|
+
return h(tag, a, c);
|
|
1642
|
+
}
|
|
1643
|
+
function status(el2, msg) {
|
|
1644
|
+
if (!el2) return;
|
|
1645
|
+
el2.textContent = msg || "";
|
|
1646
|
+
}
|
|
1647
|
+
function esc(s) {
|
|
1648
|
+
const d = document.createElement("div");
|
|
1649
|
+
d.textContent = String(s ?? "");
|
|
1650
|
+
return d.innerHTML;
|
|
1651
|
+
}
|
|
1652
|
+
function renderDefaultUnlockUI(container, resource, options = {}) {
|
|
1653
|
+
inject();
|
|
1654
|
+
const card = el("div", { cls: "nui", style: { animation: "nfade .2s ease-out" } });
|
|
1655
|
+
const lockSVG = '<svg width="24" height="24" viewBox="0 0 32 32" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:middle"><path d="M6 16v8a3 3 0 0 0 3 3h14a3 3 0 0 0 3-3v-8"/><path d="M10 16v-5a6 6 0 0 1 12 0v5"/><circle cx="16" cy="21" r="2"/></svg>';
|
|
1656
|
+
card.innerHTML = `
|
|
1657
|
+
<div style="display:flex;flex-direction:column;align-items:center;text-align:center;max-width:580px;margin:0 auto;padding:40px 52px">
|
|
1658
|
+
<div id="nibgate-lottie" style="width:165px;height:168px;margin-bottom:24px"></div>
|
|
1659
|
+
<div style="font-size:50px;font-weight:700;letter-spacing:-.03em;color:${theme.fg};margin-bottom:12px">${esc(resource.price)} USDC</div>
|
|
1660
|
+
<div style="font-size:21px;color:${theme.muted};margin-bottom:48px">Pay to unlock this content</div>
|
|
1661
|
+
<div data-nibgate-wallet-label class="nui-mono" style="font-size:18px;color:${theme.muted};margin-bottom:40px;min-height:28px">Connect wallet</div>
|
|
1662
|
+
<div data-nibgate-unlock-wrap style="width:100%;position:relative;border-radius:12px;overflow:hidden;cursor:pointer">
|
|
1663
|
+
<div data-nibgate-unlock-progress style="position:absolute;inset:0;width:0%;background:linear-gradient(90deg,rgba(255,255,255,0.1),rgba(255,255,255,0.45));border-radius:12px;transition:width .05s linear;z-index:2"></div>
|
|
1664
|
+
<div data-nibgate-shimmer style="position:absolute;inset:0;width:100%;height:100%;background:linear-gradient(90deg,transparent,rgba(255,255,255,0.12),transparent);border-radius:12px;transform:translateX(-100%);z-index:3;pointer-events:none"></div>
|
|
1665
|
+
<button type="button" data-nibgate-unlock disabled class="nui-btn nui-btn-primary" style="width:100%;padding:24px 32px;font-size:24px;position:relative;z-index:4;background:${theme.accent};transition:transform .1s,opacity .15s;display:flex">${lockSVG}Hold to pay ${esc(resource.price)} USDC</button></div>
|
|
1666
|
+
<div class="nui-stat" style="text-align:center;margin-top:16px" data-nibgate-status></div>
|
|
1667
|
+
</div>
|
|
1668
|
+
<div data-nibgate-premium hidden style="margin-top:32px;border-top:1px solid ${theme.border};padding-top:32px"></div>
|
|
1669
|
+
`;
|
|
1670
|
+
(typeof container === "string" ? document.querySelector(container) : container)?.appendChild(card);
|
|
1671
|
+
(function loadLottie() {
|
|
1672
|
+
if (!document.getElementById("nibgate-lottie")) return;
|
|
1673
|
+
function startAnim(data) {
|
|
1674
|
+
var d = document.getElementById("nibgate-lottie");
|
|
1675
|
+
if (d && window.lottie) {
|
|
1676
|
+
window.lottie.loadAnimation({ container: d, animationData: data, loop: true, autoplay: true });
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
if (window.lottie) {
|
|
1680
|
+
if (window._lottieData) {
|
|
1681
|
+
startAnim(window._lottieData);
|
|
1682
|
+
} else {
|
|
1683
|
+
fetch("/nibgate-unlock-key.json?t=1").then(function(r) {
|
|
1684
|
+
if (!r.ok) throw new Error();
|
|
1685
|
+
return r.json();
|
|
1686
|
+
}).then(function(d) {
|
|
1687
|
+
window._lottieData = d;
|
|
1688
|
+
startAnim(d);
|
|
1689
|
+
}).catch(function() {
|
|
1690
|
+
});
|
|
1691
|
+
}
|
|
1692
|
+
return;
|
|
1693
|
+
}
|
|
1694
|
+
if (window._lottieLoading) return;
|
|
1695
|
+
window._lottieLoading = true;
|
|
1696
|
+
var s = document.createElement("script");
|
|
1697
|
+
s.src = "https://cdnjs.cloudflare.com/ajax/libs/lottie-web/5.12.2/lottie.min.js";
|
|
1698
|
+
s.onload = function() {
|
|
1699
|
+
fetch("/nibgate-unlock-key.json?t=1").then(function(r) {
|
|
1700
|
+
if (!r.ok) throw new Error();
|
|
1701
|
+
return r.json();
|
|
1702
|
+
}).then(function(d) {
|
|
1703
|
+
window._lottieData = d;
|
|
1704
|
+
startAnim(d);
|
|
1705
|
+
}).catch(function() {
|
|
1706
|
+
});
|
|
1707
|
+
};
|
|
1708
|
+
document.head.appendChild(s);
|
|
1709
|
+
})();
|
|
1710
|
+
const st = card.querySelector("[data-nibgate-status]");
|
|
1711
|
+
const label = card.querySelector("[data-nibgate-wallet-label]");
|
|
1712
|
+
const wrap = card.querySelector("[data-nibgate-unlock-wrap]");
|
|
1713
|
+
const prog = card.querySelector("[data-nibgate-unlock-progress]");
|
|
1714
|
+
const shimmer = card.querySelector("[data-nibgate-shimmer]");
|
|
1715
|
+
const btn = card.querySelector("[data-nibgate-unlock]");
|
|
1716
|
+
const HOLD_MS = 1500;
|
|
1717
|
+
let holdTimer = null, holdActive = false, holdComplete = false;
|
|
1718
|
+
const ctrl = createEvmGatewayUnlock(resource, {
|
|
1719
|
+
...options,
|
|
1720
|
+
connectButton: null,
|
|
1721
|
+
unlockButton: null,
|
|
1722
|
+
walletLabel: null,
|
|
1723
|
+
status: "[data-nibgate-status]",
|
|
1724
|
+
unlockedTarget: "[data-nibgate-premium]",
|
|
1725
|
+
onStatus: (msg) => status(st, msg)
|
|
1726
|
+
});
|
|
1727
|
+
function shortAddress(a) {
|
|
1728
|
+
return a ? a.slice(0, 6) + "..." + a.slice(-4) : "";
|
|
1729
|
+
}
|
|
1730
|
+
function updateLabel() {
|
|
1731
|
+
const addr = ctrl.getWalletAddress();
|
|
1732
|
+
if (addr) {
|
|
1733
|
+
label.innerHTML = shortAddress(addr) + ' <span data-nibgate-disconnect style="cursor:pointer">\xB7 Disconnect</span>';
|
|
1734
|
+
btn.disabled = false;
|
|
1735
|
+
btn.style.cursor = "pointer";
|
|
1736
|
+
} else {
|
|
1737
|
+
label.textContent = "Connect wallet";
|
|
1738
|
+
btn.disabled = true;
|
|
1739
|
+
btn.style.cursor = "default";
|
|
1740
|
+
btn.innerHTML = lockSVG + "Hold to pay " + esc(resource.price) + " USDC";
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
function setBtnText(t) {
|
|
1744
|
+
btn.innerHTML = lockSVG + t;
|
|
1745
|
+
}
|
|
1746
|
+
function resetHold() {
|
|
1747
|
+
holdActive = false;
|
|
1748
|
+
holdComplete = false;
|
|
1749
|
+
holdTimer = null;
|
|
1750
|
+
btn.style.transform = "scale(1)";
|
|
1751
|
+
prog.style.width = "0%";
|
|
1752
|
+
shimmer.style.animation = "none";
|
|
1753
|
+
shimmer.style.transform = "translateX(-100%)";
|
|
1754
|
+
if (!btn.disabled) setBtnText("Hold to pay " + esc(resource.price) + " USDC");
|
|
1755
|
+
}
|
|
1756
|
+
function startHold(e) {
|
|
1757
|
+
if (btn.disabled || holdActive) return;
|
|
1758
|
+
e.preventDefault();
|
|
1759
|
+
holdActive = true;
|
|
1760
|
+
holdComplete = false;
|
|
1761
|
+
btn.style.transform = "scale(.97)";
|
|
1762
|
+
prog.style.transition = "none";
|
|
1763
|
+
prog.style.width = "0%";
|
|
1764
|
+
shimmer.style.animation = "nshimmer 1s ease-in-out infinite";
|
|
1765
|
+
shimmer.style.transform = "none";
|
|
1766
|
+
setBtnText("Hold\u2026");
|
|
1767
|
+
requestAnimationFrame(() => {
|
|
1768
|
+
prog.style.transition = "width " + HOLD_MS + "ms linear";
|
|
1769
|
+
prog.style.width = "100%";
|
|
1770
|
+
});
|
|
1771
|
+
const t1 = setTimeout(() => {
|
|
1772
|
+
if (holdActive && !holdComplete) setBtnText("Keep holding\u2026");
|
|
1773
|
+
}, HOLD_MS * 0.4);
|
|
1774
|
+
const t2 = setTimeout(() => {
|
|
1775
|
+
if (holdActive && !holdComplete) setBtnText("Almost there\u2026");
|
|
1776
|
+
}, HOLD_MS * 0.75);
|
|
1777
|
+
holdTimer = setTimeout(() => {
|
|
1778
|
+
holdComplete = true;
|
|
1779
|
+
holdActive = false;
|
|
1780
|
+
clearTimeout(t1);
|
|
1781
|
+
clearTimeout(t2);
|
|
1782
|
+
btn.style.transform = "scale(1)";
|
|
1783
|
+
shimmer.style.animation = "none";
|
|
1784
|
+
prog.style.transition = "width .05s linear";
|
|
1785
|
+
setBtnText("Processing\u2026");
|
|
1786
|
+
btn.disabled = true;
|
|
1787
|
+
setTimeout(() => ctrl.unlock().then(updateLabel).catch(updateLabel), 300);
|
|
1788
|
+
}, HOLD_MS);
|
|
1789
|
+
}
|
|
1790
|
+
function cancelHold() {
|
|
1791
|
+
if (!holdActive || holdComplete) return;
|
|
1792
|
+
clearTimeout(holdTimer);
|
|
1793
|
+
resetHold();
|
|
1794
|
+
}
|
|
1795
|
+
label.addEventListener("click", (e) => {
|
|
1796
|
+
if (e.target.dataset.nibgateDisconnect !== void 0) {
|
|
1797
|
+
ctrl.disconnect().then(updateLabel);
|
|
1798
|
+
} else if (!ctrl.getWalletAddress()) {
|
|
1799
|
+
ctrl.connect().then(updateLabel).catch(() => updateLabel());
|
|
1800
|
+
}
|
|
1801
|
+
});
|
|
1802
|
+
wrap.addEventListener("mousedown", startHold);
|
|
1803
|
+
wrap.addEventListener("touchstart", startHold, { passive: false });
|
|
1804
|
+
document.addEventListener("mouseup", cancelHold);
|
|
1805
|
+
document.addEventListener("touchend", cancelHold);
|
|
1806
|
+
wrap.addEventListener("mouseleave", cancelHold);
|
|
1807
|
+
setTimeout(updateLabel, 200);
|
|
1808
|
+
const cleanup = () => {
|
|
1809
|
+
document.removeEventListener("mouseup", cancelHold);
|
|
1810
|
+
document.removeEventListener("touchend", cancelHold);
|
|
1811
|
+
};
|
|
1812
|
+
card.addEventListener("remove", cleanup);
|
|
1813
|
+
return { ...ctrl, element: card, destroy: () => {
|
|
1814
|
+
cleanup();
|
|
1815
|
+
card.remove();
|
|
1816
|
+
} };
|
|
1817
|
+
}
|
|
1818
|
+
function renderDefaultRatingUI(container, resource, options = {}) {
|
|
1819
|
+
inject();
|
|
1820
|
+
let sel = 0, busy = false, ctrl = null, statusTimer = null;
|
|
1821
|
+
const wrap = el("div", { cls: "nui", style: { animation: "nfade .2s ease-out", textAlign: "center", padding: "28px 0" } });
|
|
1822
|
+
const starRow = el("div", { style: { display: "flex", justifyContent: "center", alignItems: "center", gap: "4px" } });
|
|
1823
|
+
const stars = [];
|
|
1824
|
+
for (let i = 1; i <= 5; i++) {
|
|
1825
|
+
const b = el("button", {
|
|
1826
|
+
type: "button",
|
|
1827
|
+
"aria-label": `${i} star${i > 1 ? "s" : ""}`,
|
|
1828
|
+
"data-star": i,
|
|
1829
|
+
style: {
|
|
1830
|
+
background: "none",
|
|
1831
|
+
border: "none",
|
|
1832
|
+
cursor: "pointer",
|
|
1833
|
+
padding: "4px",
|
|
1834
|
+
fontSize: "31px",
|
|
1835
|
+
lineHeight: "1",
|
|
1836
|
+
color: theme.border,
|
|
1837
|
+
transition: "color .12s, transform .12s",
|
|
1838
|
+
borderRadius: "4px",
|
|
1839
|
+
fontFamily: "inherit"
|
|
1840
|
+
}
|
|
1841
|
+
}, "\u2606");
|
|
1842
|
+
stars.push(b);
|
|
1843
|
+
starRow.appendChild(b);
|
|
1844
|
+
}
|
|
1845
|
+
const avgEl = el("span", { style: { fontSize: "17px", color: theme.muted, marginLeft: "12px" } });
|
|
1846
|
+
avgEl.textContent = "No ratings yet";
|
|
1847
|
+
starRow.appendChild(avgEl);
|
|
1848
|
+
const st = el("div", { style: { fontSize: "17px", color: theme.muted, marginTop: "8px", minHeight: "1.4em" } });
|
|
1849
|
+
st.textContent = "Tap stars to rate";
|
|
1850
|
+
wrap.append(starRow, st);
|
|
1851
|
+
(typeof container === "string" ? document.querySelector(container) : container)?.appendChild(wrap);
|
|
1852
|
+
function clearStatus() {
|
|
1853
|
+
if (statusTimer) {
|
|
1854
|
+
clearTimeout(statusTimer);
|
|
1855
|
+
statusTimer = null;
|
|
1856
|
+
}
|
|
1857
|
+
st.textContent = sel > 0 ? "" : "Tap stars to rate";
|
|
1858
|
+
st.style.color = theme.muted;
|
|
1859
|
+
}
|
|
1860
|
+
function setStatus(msg, color, autoClear) {
|
|
1861
|
+
st.textContent = msg || "";
|
|
1862
|
+
st.style.color = color || "";
|
|
1863
|
+
if (autoClear) statusTimer = setTimeout(clearStatus, autoClear);
|
|
1864
|
+
}
|
|
1865
|
+
function fill(h2) {
|
|
1866
|
+
stars.forEach((b, i) => {
|
|
1867
|
+
const active = i < h2;
|
|
1868
|
+
b.style.color = active ? theme.accent : theme.border;
|
|
1869
|
+
b.textContent = active ? "\u2605" : "\u2606";
|
|
1870
|
+
b.style.transform = active ? "scale(1.08)" : "scale(1)";
|
|
1871
|
+
});
|
|
1872
|
+
}
|
|
1873
|
+
function setPrompt(v) {
|
|
1874
|
+
const labels = ["", "Poor", "Below average", "Average", "Good", "Excellent"];
|
|
1875
|
+
setStatus(labels[v]);
|
|
1876
|
+
}
|
|
1877
|
+
async function rate(v) {
|
|
1878
|
+
if (busy || !ctrl) return;
|
|
1879
|
+
busy = true;
|
|
1880
|
+
stars.forEach((b) => {
|
|
1881
|
+
b.disabled = true;
|
|
1882
|
+
b.style.cursor = "default";
|
|
1883
|
+
});
|
|
1884
|
+
sel = v;
|
|
1885
|
+
fill(v);
|
|
1886
|
+
setStatus("Submitting\u2026");
|
|
1887
|
+
try {
|
|
1888
|
+
await ctrl.rate({ rating: v });
|
|
1889
|
+
fill(v);
|
|
1890
|
+
setStatus("You rated " + v + " \u2605".repeat(v), theme.accent, 3e3);
|
|
1891
|
+
refresh();
|
|
1892
|
+
} catch (e) {
|
|
1893
|
+
setStatus(e?.message || "Could not save rating. Try again.", "#dc2626");
|
|
1894
|
+
fill(sel);
|
|
1895
|
+
} finally {
|
|
1896
|
+
busy = false;
|
|
1897
|
+
stars.forEach((b) => {
|
|
1898
|
+
b.disabled = false;
|
|
1899
|
+
b.style.cursor = "pointer";
|
|
1900
|
+
});
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
function refresh() {
|
|
1904
|
+
const u = options.statsUrl || (resource.id ? `${options.apiBase || "/api"}/rating/${resource.id}` : null);
|
|
1905
|
+
if (!u) return;
|
|
1906
|
+
fetch(u).then((r) => r.json()).then((d) => {
|
|
1907
|
+
if (d.count > 0) {
|
|
1908
|
+
avgEl.textContent = `${d.average.toFixed(1)} \u2014 ${d.count} rating${d.count !== 1 ? "s" : ""}`;
|
|
1909
|
+
} else {
|
|
1910
|
+
avgEl.textContent = "No ratings yet";
|
|
1911
|
+
}
|
|
1912
|
+
}).catch(() => {
|
|
1913
|
+
});
|
|
1914
|
+
}
|
|
1915
|
+
stars.forEach((b) => {
|
|
1916
|
+
const v = parseInt(b.dataset.star);
|
|
1917
|
+
b.addEventListener("mouseenter", () => {
|
|
1918
|
+
if (!busy) {
|
|
1919
|
+
fill(v);
|
|
1920
|
+
setPrompt(v);
|
|
1921
|
+
}
|
|
1922
|
+
});
|
|
1923
|
+
b.addEventListener("mouseleave", () => {
|
|
1924
|
+
if (!busy) {
|
|
1925
|
+
fill(sel);
|
|
1926
|
+
clearStatus();
|
|
1927
|
+
}
|
|
1928
|
+
});
|
|
1929
|
+
b.addEventListener("click", () => rate(v));
|
|
1930
|
+
b.addEventListener("keydown", (e) => {
|
|
1931
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
1932
|
+
e.preventDefault();
|
|
1933
|
+
rate(v);
|
|
1934
|
+
}
|
|
1935
|
+
});
|
|
1936
|
+
});
|
|
1937
|
+
Promise.resolve().then(() => (init_rating_ui(), rating_ui_exports)).then((m) => {
|
|
1938
|
+
ctrl = m.createOnchainRating(resource, {
|
|
1939
|
+
autoMount: false,
|
|
1940
|
+
contentId: options.contentId || "0x" + (resource.id || "").replace(/-/g, ""),
|
|
1941
|
+
onRated: (r) => {
|
|
1942
|
+
setStatus("Rating saved", theme.accent, 3e3);
|
|
1943
|
+
refresh();
|
|
1944
|
+
if (typeof options.onRated === "function") options.onRated(r);
|
|
1945
|
+
},
|
|
1946
|
+
onError: (e) => {
|
|
1947
|
+
setStatus(e?.message || "Could not save rating. Try again.", "#dc2626");
|
|
1948
|
+
if (typeof options.onError === "function") options.onError(e);
|
|
1949
|
+
}
|
|
1950
|
+
});
|
|
1951
|
+
refresh();
|
|
1952
|
+
}).catch(() => {
|
|
1953
|
+
setStatus("Could not load rating", "#dc2626");
|
|
1954
|
+
});
|
|
1955
|
+
return { element: wrap, destroy: () => wrap.remove(), refresh, setRating: (v) => {
|
|
1956
|
+
sel = v;
|
|
1957
|
+
fill(v);
|
|
1958
|
+
} };
|
|
1959
|
+
}
|
|
1960
|
+
function renderDefaultGatewayWalletUI(container, options = {}) {
|
|
1961
|
+
inject();
|
|
1962
|
+
let tab = "deposit";
|
|
1963
|
+
const wrap = el("div", { cls: "nui", style: { animation: "nfade .2s ease-out", border: "1px solid " + theme.border, borderRadius: "16px", padding: "28px" } });
|
|
1964
|
+
wrap.innerHTML = `
|
|
1965
|
+
<div style="display:flex;gap:12px;margin-bottom:20px">
|
|
1966
|
+
<div data-gw-wallet-card style="flex:1;background:${theme.bg};border:1px solid ${theme.border};border-radius:12px;padding:16px">
|
|
1967
|
+
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:4px">
|
|
1968
|
+
<div style="font-size:12px;font-weight:600;color:${theme.muted};letter-spacing:.02em">Wallet</div>
|
|
1969
|
+
<button data-gw-connect style="font-size:12px;font-weight:600;cursor:pointer;border:1px solid ${theme.accent};background:transparent;color:${theme.accent};border-radius:8px;padding:4px 12px;font-family:inherit">Connect</button>
|
|
1970
|
+
</div>
|
|
1971
|
+
<div data-gw-wallet-balance class="nui-mono" style="font-size:24px;font-weight:700;color:${theme.fg}">\u2014</div>
|
|
1972
|
+
</div>
|
|
1973
|
+
<div style="flex:1;background:${theme.bg};border:1px solid ${theme.border};border-radius:12px;padding:16px">
|
|
1974
|
+
<div style="font-size:12px;font-weight:600;color:${theme.muted};margin-bottom:4px;letter-spacing:.02em">Gateway</div>
|
|
1975
|
+
<div data-gw-balance class="nui-mono" style="font-size:24px;font-weight:700;color:${theme.fg}">\u2014</div>
|
|
1976
|
+
</div>
|
|
1977
|
+
</div>
|
|
1978
|
+
<div style="display:flex;gap:0;margin-bottom:20px;border-bottom:1px solid ${theme.border}">
|
|
1979
|
+
<button data-tab="deposit" style="flex:1;padding:10px 0;font-size:15px;font-weight:600;cursor:pointer;border:none;background:transparent;color:${theme.fg};border-bottom:2px solid ${theme.accent};font-family:inherit">Deposit</button>
|
|
1980
|
+
<button data-tab="withdraw" style="flex:1;padding:10px 0;font-size:15px;font-weight:600;cursor:pointer;border:none;background:transparent;color:${theme.muted};border-bottom:2px solid transparent;font-family:inherit">Withdraw</button>
|
|
1981
|
+
</div>
|
|
1982
|
+
<div data-gw-form></div>
|
|
1983
|
+
`;
|
|
1984
|
+
(typeof container === "string" ? document.querySelector(container) : container)?.appendChild(wrap);
|
|
1985
|
+
const formEl = wrap.querySelector("[data-gw-form]");
|
|
1986
|
+
const tabs = wrap.querySelectorAll("[data-tab]");
|
|
1987
|
+
function select(btns, t) {
|
|
1988
|
+
btns.forEach((b) => {
|
|
1989
|
+
const on = b.dataset.tab === t;
|
|
1990
|
+
b.style.color = on ? theme.fg : theme.muted;
|
|
1991
|
+
b.style.borderBottomColor = on ? theme.accent : "transparent";
|
|
1992
|
+
});
|
|
1993
|
+
}
|
|
1994
|
+
function render(t) {
|
|
1995
|
+
tab = t;
|
|
1996
|
+
select(tabs, t);
|
|
1997
|
+
if (t === "deposit") {
|
|
1998
|
+
formEl.innerHTML = `
|
|
1999
|
+
<label class="nui-label">Amount (USDC)</label>
|
|
2000
|
+
<input type="number" step="0.01" min="0" placeholder="0.00" data-gw-deposit-amount class="nui-input" style="margin-bottom:16px">
|
|
2001
|
+
<button type="button" data-gw-deposit class="nui-btn nui-btn-primary" style="width:100%;padding:16px 28px;font-size:20px">Deposit</button>
|
|
2002
|
+
<div data-gw-tx class="nui-mono" style="font-size:12px;color:${theme.muted};word-break:break-all;margin-top:12px;display:none"></div>
|
|
2003
|
+
`;
|
|
2004
|
+
} else {
|
|
2005
|
+
formEl.innerHTML = `
|
|
2006
|
+
<label class="nui-label">Amount (USDC)</label>
|
|
2007
|
+
<input type="number" step="0.01" min="0" placeholder="0.00" data-gw-withdraw-amount class="nui-input" style="margin-bottom:16px">
|
|
2008
|
+
<button type="button" data-gw-withdraw class="nui-btn nui-btn-primary" style="width:100%;padding:16px 28px;font-size:20px">Withdraw to your wallet</button>
|
|
2009
|
+
<div data-gw-tx class="nui-mono" style="font-size:12px;color:${theme.muted};word-break:break-all;margin-top:12px;display:none"></div>
|
|
2010
|
+
`;
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
render("deposit");
|
|
2014
|
+
tabs.forEach((b) => b.addEventListener("click", () => render(b.dataset.tab)));
|
|
2015
|
+
return { element: wrap, destroy: () => wrap.remove(), switchTab: render };
|
|
2016
|
+
}
|
|
2017
|
+
|
|
2018
|
+
// src/browser/index.js
|
|
2019
|
+
init_resource();
|
|
2020
|
+
|
|
1492
2021
|
// src/core/settings.js
|
|
2022
|
+
init_payment();
|
|
2023
|
+
init_resource();
|
|
1493
2024
|
var NIBGATE_CONTENT_SETTING_FIELDS = [
|
|
1494
2025
|
{ name: "publishToNibgate", label: "Publish to Nibgate discovery", type: "boolean", defaultValue: true },
|
|
1495
2026
|
{ name: "type", label: "Content type", type: "select", options: CONTENT_TYPES, defaultValue: "article" },
|
|
@@ -1532,6 +2063,9 @@ var Nibgate = (() => {
|
|
|
1532
2063
|
function settingsToUnlockPolicy(settings = {}) {
|
|
1533
2064
|
return normalizeUnlockPolicy(settings.unlockMode || settings.unlock || "one_time");
|
|
1534
2065
|
}
|
|
2066
|
+
|
|
2067
|
+
// src/browser/index.js
|
|
2068
|
+
init_payment();
|
|
1535
2069
|
return __toCommonJS(index_exports);
|
|
1536
2070
|
})();
|
|
1537
2071
|
//# sourceMappingURL=nibgate.js.map
|