@nibgate/sdk 0.2.1 → 0.2.2
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/SKILL.md +136 -10
- package/dist/nibgate.js +352 -362
- package/dist/nibgate.js.map +4 -4
- package/dist/nibgate.min.js +2 -2
- package/dist/nibgate.min.js.map +4 -4
- package/package.json +6 -3
- package/src/browser/access.js +168 -0
- package/src/browser/checkout.js +53 -0
- package/src/browser/client.js +97 -0
- package/src/browser/evm-gateway.js +180 -0
- package/src/browser/gate.js +63 -0
- package/src/browser/index.js +9 -770
- package/src/browser/rating-ui.js +161 -0
- package/src/browser/track.js +45 -0
- package/src/server/access.js +14 -29
- package/src/server/admin-page.html +168 -0
- package/src/server/admin.js +18 -180
- package/src/server/gateway.js +8 -3
- package/src/server/webhooks.js +2 -2
- package/src/server/runtime.js +0 -39
package/src/browser/index.js
CHANGED
|
@@ -1,773 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import { createTransferCheckout, payWithTransfer } from './transfer.js';
|
|
9
|
-
|
|
10
|
-
export async function createCircleGatewayBrowserAdapter(options = {}) {
|
|
11
|
-
const gateway = await import('./gateway.js');
|
|
12
|
-
return gateway.createCircleGatewayBrowserAdapter(options);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function createGate(resource, options = {}) {
|
|
16
|
-
const normalized = normalizeResource(resource);
|
|
17
|
-
const client = options.client || nibgate;
|
|
18
|
-
|
|
19
|
-
return {
|
|
20
|
-
resource: normalized,
|
|
21
|
-
content(extra = {}) {
|
|
22
|
-
return client.content(normalized, extra);
|
|
23
|
-
},
|
|
24
|
-
view(extra = {}) {
|
|
25
|
-
return client.view(normalized, extra);
|
|
26
|
-
},
|
|
27
|
-
track(eventName, payload = {}) {
|
|
28
|
-
return client.track(eventName, payloadWithResource(normalized, payload));
|
|
29
|
-
},
|
|
30
|
-
unlockStarted(extra = {}) {
|
|
31
|
-
return client.unlockStarted(normalized, extra);
|
|
32
|
-
},
|
|
33
|
-
unlockCompleted(payment = {}) {
|
|
34
|
-
markUnlocked(normalized, payment);
|
|
35
|
-
return client.unlockCompleted(normalized, payment);
|
|
36
|
-
},
|
|
37
|
-
paymentCompleted(payment = {}) {
|
|
38
|
-
return client.paymentCompleted(normalized, payment);
|
|
39
|
-
},
|
|
40
|
-
isUnlocked() {
|
|
41
|
-
return hasUnlock(normalized);
|
|
42
|
-
},
|
|
43
|
-
markUnlocked(payment = {}) {
|
|
44
|
-
markUnlocked(normalized, payment);
|
|
45
|
-
client.unlockCompleted(normalized, payment);
|
|
46
|
-
client.paymentCompleted(normalized, payment);
|
|
47
|
-
return true;
|
|
48
|
-
},
|
|
49
|
-
async unlock(handlerOrPayment = {}) {
|
|
50
|
-
client.unlockStarted(normalized);
|
|
51
|
-
const payment = typeof handlerOrPayment === 'function'
|
|
52
|
-
? await handlerOrPayment(normalized)
|
|
53
|
-
: handlerOrPayment;
|
|
54
|
-
markUnlocked(normalized, payment || {});
|
|
55
|
-
client.unlockCompleted(normalized, payment || {});
|
|
56
|
-
client.paymentCompleted(normalized, payment || {});
|
|
57
|
-
return { unlocked: true, resource: normalized, payment: payment || {} };
|
|
58
|
-
},
|
|
59
|
-
rate(rating = {}, extra = {}) {
|
|
60
|
-
return client.rateResource(normalized, rating, extra);
|
|
61
|
-
}
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export function rateResource(resource, rating = {}, extra = {}) {
|
|
66
|
-
const normalized = normalizeResource(resource);
|
|
67
|
-
const normalizedRating = normalizeRating(rating);
|
|
68
|
-
const payload = {
|
|
69
|
-
...extra,
|
|
70
|
-
...normalizedRating,
|
|
71
|
-
ratingMessage: extra.ratingMessage || rating.message || rating.ratingMessage || ratingMessage(normalized, normalizedRating, extra),
|
|
72
|
-
ratingSignature: extra.ratingSignature || rating.signature || rating.ratingSignature || undefined,
|
|
73
|
-
resource: normalized
|
|
74
|
-
};
|
|
75
|
-
return emit('content_rating', payload);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export { contentRatingHash, NIBGATE_CONTENT_HASH_NAMESPACE, NIBGATE_REPUTATION_ABI, NIBGATE_REPUTATION_CHAIN_ID, NIBGATE_REPUTATION_CHAIN_NAME, NIBGATE_REPUTATION_CONTRACT, NIBGATE_REPUTATION_RPC_URL, rateContentOnchain, reviewTextHash } from './reputation.js';
|
|
79
|
-
|
|
80
|
-
export function trackResourcePage(resource, options = {}) {
|
|
81
|
-
const item = createGate(resource, options.gateOptions || {});
|
|
82
|
-
const validation = validateResourceMetadata(item.resource, options.validation || {});
|
|
83
|
-
if ((validation.warnings.length || validation.errors.length) && options.warn !== false && browserWindow()?.console?.warn) {
|
|
84
|
-
browserWindow().console.warn('Nibgate content metadata needs attention', validation);
|
|
85
|
-
}
|
|
86
|
-
item.content({ source: options.source, metadataQuality: { score: validation.score, warnings: validation.warnings, errors: validation.errors }, ...(options.content || {}) });
|
|
87
|
-
item.view({
|
|
88
|
-
source: options.source,
|
|
89
|
-
path: options.path || browserWindow()?.location?.pathname || item.resource.path,
|
|
90
|
-
referrer: options.referrer ?? browserWindow()?.document?.referrer ?? '',
|
|
91
|
-
...(options.view || {})
|
|
92
|
-
});
|
|
93
|
-
return item;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export async function checkResourceAccess(resource, options = {}) {
|
|
97
|
-
const item = createGate(resource, options.gateOptions || {});
|
|
98
|
-
const accessPath = options.accessPath || item.resource.accessPath || '/api/nibgate/access';
|
|
99
|
-
const status = typeof options.onStatus === 'function' ? options.onStatus : () => {};
|
|
100
|
-
|
|
101
|
-
status(options.checkingMessage || 'Checking access route...');
|
|
102
|
-
item.unlockStarted({ source: options.source, paymentProvider: options.paymentProvider || 'nibgate-access-route' });
|
|
103
|
-
|
|
104
|
-
const response = await fetch(accessPath, {
|
|
105
|
-
method: options.method || 'GET',
|
|
106
|
-
headers: {
|
|
107
|
-
accept: 'application/json',
|
|
108
|
-
...(getPaymentProof(item.resource) ? { 'x-nibgate-payment-proof': getPaymentProof(item.resource) } : {}),
|
|
109
|
-
...(options.headers || {})
|
|
110
|
-
},
|
|
111
|
-
body: options.body
|
|
112
|
-
});
|
|
113
|
-
const payload = await response.json().catch(() => ({}));
|
|
114
|
-
|
|
115
|
-
if (response.status === 402) {
|
|
116
|
-
item.track('payment_challenge_returned', { source: options.source, challenge: payload, resource: item.resource });
|
|
117
|
-
status(options.challengeMessage || 'Payment challenge returned. Continue with checkout.');
|
|
118
|
-
if (typeof options.createPaymentSignature === 'function' || typeof options.checkout === 'function') {
|
|
119
|
-
const paymentResult = await payWithPaymentSignature(resource, {
|
|
120
|
-
...options,
|
|
121
|
-
challenge: payload,
|
|
122
|
-
paymentRequiredHeader: response.headers.get('PAYMENT-REQUIRED') || response.headers.get('payment-required') || ''
|
|
123
|
-
});
|
|
124
|
-
return paymentResult;
|
|
125
|
-
}
|
|
126
|
-
if (options.autoPay && options.payPath) {
|
|
127
|
-
const paymentResult = await payAndUnlockResource(resource, options);
|
|
128
|
-
if (paymentResult.ok && options.retryAfterPay !== false) {
|
|
129
|
-
return checkResourceAccess(resource, { ...options, autoPay: false });
|
|
130
|
-
}
|
|
131
|
-
return paymentResult;
|
|
132
|
-
}
|
|
133
|
-
return { ok: false, status: response.status, challenge: payload, resource: item.resource, response };
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
if (!response.ok) {
|
|
137
|
-
status(payload.error || options.errorMessage || 'Access check failed');
|
|
138
|
-
return { ok: false, status: response.status, error: payload.error || 'Access check failed', payload, resource: item.resource, response };
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const payment = options.payment || payload.payment || null;
|
|
142
|
-
if (payment) {
|
|
143
|
-
item.unlockCompleted(payment);
|
|
144
|
-
item.paymentCompleted(payment);
|
|
145
|
-
}
|
|
146
|
-
status(options.successMessage || 'Access allowed and Nibgate events emitted.');
|
|
147
|
-
return { ok: true, status: response.status, payload, payment, resource: item.resource, response };
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
export async function payWithPaymentSignature(resource, options = {}) {
|
|
151
|
-
const item = createGate(resource, options.gateOptions || {});
|
|
152
|
-
const accessPath = options.accessPath || item.resource.accessPath || '/api/nibgate/access';
|
|
153
|
-
const status = typeof options.onStatus === 'function' ? options.onStatus : () => {};
|
|
154
|
-
|
|
155
|
-
status(options.paymentMessage || 'Waiting for wallet payment approval...');
|
|
156
|
-
item.unlockStarted({ source: options.source, paymentProvider: options.paymentProvider || 'wallet-gateway' });
|
|
157
|
-
|
|
158
|
-
let paymentSignature = options.paymentSignature || '';
|
|
159
|
-
let paymentMemo = options.memo || '';
|
|
160
|
-
let paymentMetadata = options.payment || {};
|
|
161
|
-
|
|
162
|
-
if (!paymentSignature) {
|
|
163
|
-
const paymentRequiredHeader = options.paymentRequiredHeader || '';
|
|
164
|
-
const challenge = options.challenge || null;
|
|
165
|
-
const checkout = options.createPaymentSignature || options.checkout;
|
|
166
|
-
const result = await checkout({
|
|
167
|
-
resource: item.resource,
|
|
168
|
-
challenge,
|
|
169
|
-
paymentRequiredHeader,
|
|
170
|
-
accessPath
|
|
171
|
-
});
|
|
172
|
-
paymentSignature = result?.paymentSignature || result?.signature || result?.payment || '';
|
|
173
|
-
paymentMemo = result?.memo || result?.paymentMemo || '';
|
|
174
|
-
paymentMetadata = result?.metadata || result?.paymentMetadata || result || {};
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
if (!paymentSignature) {
|
|
178
|
-
const error = 'Wallet did not return a payment signature.';
|
|
179
|
-
item.track('payment_failed', { source: options.source, error });
|
|
180
|
-
status(error);
|
|
181
|
-
return { ok: false, status: 400, error, resource: item.resource };
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
const response = await fetch(accessPath, {
|
|
185
|
-
method: options.method || 'GET',
|
|
186
|
-
headers: {
|
|
187
|
-
accept: 'application/json',
|
|
188
|
-
'payment-signature': paymentSignature,
|
|
189
|
-
...(paymentMemo ? { 'payment-memo': paymentMemo } : {}),
|
|
190
|
-
...(options.headers || {})
|
|
191
|
-
}
|
|
192
|
-
});
|
|
193
|
-
const responseText = await response.text();
|
|
194
|
-
let payload = {};
|
|
195
|
-
try {
|
|
196
|
-
payload = responseText ? JSON.parse(responseText) : {};
|
|
197
|
-
} catch (_error) {
|
|
198
|
-
payload = { error: responseText || 'Payment verification failed' };
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
if (!response.ok) {
|
|
202
|
-
const detail = payload.detail || payload.reason || payload.invalidReason || payload.error || responseText || 'Payment verification failed';
|
|
203
|
-
const error = typeof detail === 'string' ? detail : stringifyJson(detail);
|
|
204
|
-
item.track('payment_failed', { source: options.source, status: response.status, error, ...paymentMetadata });
|
|
205
|
-
status(options.paymentErrorMessage || error);
|
|
206
|
-
return { ok: false, status: response.status, error, payload, resource: item.resource, response };
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
const payment = payload.payment || {
|
|
210
|
-
paymentProvider: options.paymentProvider || 'wallet-gateway',
|
|
211
|
-
paymentId: paymentSignature,
|
|
212
|
-
memo: paymentMemo,
|
|
213
|
-
amount: Number(item.resource.price || 0),
|
|
214
|
-
revenue: Number(item.resource.price || 0),
|
|
215
|
-
currency: item.resource.currency || 'USDC',
|
|
216
|
-
...paymentMetadata
|
|
217
|
-
};
|
|
218
|
-
storePaymentProof(item.resource, payload.unlockProof);
|
|
219
|
-
item.markUnlocked(payment);
|
|
220
|
-
status(options.paymentSuccessMessage || 'Payment verified. Content unlocked.');
|
|
221
|
-
return { ok: true, status: response.status, payload, payment, resource: item.resource, response };
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
function setElementText(target, message) {
|
|
225
|
-
const win = browserWindow();
|
|
226
|
-
if (!target || !win) return;
|
|
227
|
-
const element = typeof target === 'string' ? win.document.querySelector(target) : target;
|
|
228
|
-
if (element) element.textContent = message || '';
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
function setElementDisabled(target, disabled) {
|
|
232
|
-
const win = browserWindow();
|
|
233
|
-
if (!target || !win) return;
|
|
234
|
-
const element = typeof target === 'string' ? win.document.querySelector(target) : target;
|
|
235
|
-
if (element && 'disabled' in element) element.disabled = Boolean(disabled);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
export function createWalletCheckout(resource, options = {}) {
|
|
239
|
-
const normalized = normalizeResource(resource);
|
|
240
|
-
const accessPath = options.accessPath || normalized.accessPath || '/api/nibgate/access';
|
|
241
|
-
const button = options.button || null;
|
|
242
|
-
const statusTarget = options.status || null;
|
|
243
|
-
const status = typeof options.onStatus === 'function'
|
|
244
|
-
? options.onStatus
|
|
245
|
-
: (message) => setElementText(statusTarget, message);
|
|
246
|
-
const checkout = options.checkout || options.createPaymentSignature || options.pay;
|
|
247
|
-
|
|
248
|
-
if (typeof checkout !== 'function') {
|
|
249
|
-
throw new Error('createWalletCheckout requires checkout/createPaymentSignature/pay callback for the active wallet or Gateway adapter.');
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
async function unlock(extra = {}) {
|
|
253
|
-
setElementDisabled(button, true);
|
|
254
|
-
try {
|
|
255
|
-
return await checkResourceAccess(normalized, {
|
|
256
|
-
...options,
|
|
257
|
-
...extra,
|
|
258
|
-
accessPath,
|
|
259
|
-
createPaymentSignature: checkout,
|
|
260
|
-
onStatus: status
|
|
261
|
-
});
|
|
262
|
-
} finally {
|
|
263
|
-
setElementDisabled(button, false);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
function mount() {
|
|
268
|
-
const win = browserWindow();
|
|
269
|
-
if (!win || !button) return { unlock };
|
|
270
|
-
const element = typeof button === 'string' ? win.document.querySelector(button) : button;
|
|
271
|
-
if (element) element.addEventListener('click', () => unlock().catch((error) => status(error.message || 'Checkout failed.')));
|
|
272
|
-
return { unlock };
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
return {
|
|
276
|
-
resource: normalized,
|
|
277
|
-
unlock,
|
|
278
|
-
mount
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
export function createEvmGatewayUnlock(resource, options = {}) {
|
|
283
|
-
const item = createGate(resource, options.gateOptions || {});
|
|
284
|
-
const win = browserWindow();
|
|
285
|
-
const accessPath = options.accessPath || item.resource.accessPath || '/api/nibgate/access';
|
|
286
|
-
const source = options.source || 'nibgate-evm-gateway';
|
|
287
|
-
const network = options.network || 'eip155:5042002';
|
|
288
|
-
const statusTarget = typeof options.status === 'string' ? win?.document.querySelector(options.status) : options.status;
|
|
289
|
-
const connectButton = typeof options.connectButton === 'string' ? win?.document.querySelector(options.connectButton) : options.connectButton;
|
|
290
|
-
const disconnectButton = typeof options.disconnectButton === 'string' ? win?.document.querySelector(options.disconnectButton) : options.disconnectButton;
|
|
291
|
-
const unlockButton = typeof options.unlockButton === 'string' ? win?.document.querySelector(options.unlockButton) : options.unlockButton;
|
|
292
|
-
const clearButton = typeof options.clearButton === 'string' ? win?.document.querySelector(options.clearButton) : options.clearButton;
|
|
293
|
-
const walletLabel = typeof options.walletLabel === 'string' ? win?.document.querySelector(options.walletLabel) : options.walletLabel;
|
|
294
|
-
const unlockedTarget = typeof options.unlockedTarget === 'string' ? win?.document.querySelector(options.unlockedTarget) : options.unlockedTarget;
|
|
295
|
-
|
|
296
|
-
let walletAddress = '';
|
|
297
|
-
let busy = false;
|
|
298
|
-
|
|
299
|
-
function setStatus(message) {
|
|
300
|
-
if (typeof options.onStatus === 'function') options.onStatus(message);
|
|
301
|
-
if (statusTarget) statusTarget.textContent = message || '';
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
function shortAddress(address) {
|
|
305
|
-
return address ? `${address.slice(0, 6)}...${address.slice(-4)}` : '';
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
function provider() {
|
|
309
|
-
return win?.ethereum || options.provider || null;
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
function setBusy(value) {
|
|
313
|
-
busy = Boolean(value);
|
|
314
|
-
[connectButton, disconnectButton, unlockButton, clearButton].forEach((button) => {
|
|
315
|
-
if (button && 'disabled' in button) {
|
|
316
|
-
button.disabled = busy || (button === connectButton && !provider()) || (button === disconnectButton && !walletAddress);
|
|
317
|
-
}
|
|
318
|
-
});
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
function renderWallet() {
|
|
322
|
-
const hasProvider = Boolean(provider());
|
|
323
|
-
if (walletLabel) walletLabel.textContent = walletAddress ? shortAddress(walletAddress) : hasProvider ? 'Ready to connect' : 'No wallet detected';
|
|
324
|
-
if (connectButton) connectButton.textContent = walletAddress ? 'Connected' : 'Connect wallet';
|
|
325
|
-
if (disconnectButton) disconnectButton.textContent = 'Disconnect';
|
|
326
|
-
if (connectButton && 'disabled' in connectButton) connectButton.disabled = busy || !hasProvider;
|
|
327
|
-
if (disconnectButton && 'disabled' in disconnectButton) disconnectButton.disabled = busy || !walletAddress;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
function setUnlocked(isUnlocked, payment = {}) {
|
|
331
|
-
if (unlockButton) unlockButton.textContent = isUnlocked ? 'Unlocked' : `Unlock for ${item.resource.price} ${item.resource.currency || 'USDC'}`;
|
|
332
|
-
if (unlockedTarget) {
|
|
333
|
-
if ('hidden' in unlockedTarget) unlockedTarget.hidden = !isUnlocked;
|
|
334
|
-
unlockedTarget.setAttribute('aria-hidden', isUnlocked ? 'false' : 'true');
|
|
335
|
-
}
|
|
336
|
-
if (isUnlocked) item.markUnlocked(payment);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
async function connect() {
|
|
340
|
-
setBusy(true);
|
|
341
|
-
setStatus('Opening wallet connection...');
|
|
342
|
-
try {
|
|
343
|
-
const evm = provider();
|
|
344
|
-
if (!evm) throw new Error(options.noWalletMessage || 'Install or open an EVM wallet to continue.');
|
|
345
|
-
const accounts = await evm.request({ method: 'eth_requestAccounts' });
|
|
346
|
-
walletAddress = Array.isArray(accounts) ? accounts[0] || '' : '';
|
|
347
|
-
if (!walletAddress) throw new Error('No wallet account selected.');
|
|
348
|
-
renderWallet();
|
|
349
|
-
setStatus('Wallet connected. You can unlock now.');
|
|
350
|
-
return walletAddress;
|
|
351
|
-
} finally {
|
|
352
|
-
setBusy(false);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
async function disconnect() {
|
|
357
|
-
setBusy(true);
|
|
358
|
-
try {
|
|
359
|
-
const evm = provider();
|
|
360
|
-
if (evm?.request && walletAddress) {
|
|
361
|
-
try {
|
|
362
|
-
await evm.request({
|
|
363
|
-
method: 'wallet_revokePermissions',
|
|
364
|
-
params: [{ eth_accounts: {} }]
|
|
365
|
-
});
|
|
366
|
-
} catch (_error) {
|
|
367
|
-
// Not every injected wallet supports permission revocation. Clearing local
|
|
368
|
-
// controller state still lets the visitor connect a different account in-wallet.
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
walletAddress = '';
|
|
372
|
-
renderWallet();
|
|
373
|
-
setStatus(options.disconnectMessage || 'Wallet disconnected for this page.');
|
|
374
|
-
return true;
|
|
375
|
-
} finally {
|
|
376
|
-
setBusy(false);
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
async function checkout(input) {
|
|
381
|
-
const evm = provider();
|
|
382
|
-
if (!evm) throw new Error(options.noWalletMessage || 'Install or open an EVM wallet to continue.');
|
|
383
|
-
if (!walletAddress) await connect();
|
|
384
|
-
const gatewayWallet = await createCircleGatewayBrowserAdapter({
|
|
385
|
-
network,
|
|
386
|
-
signer: {
|
|
387
|
-
address: walletAddress,
|
|
388
|
-
signTypedData: (typedData) => evm.request({
|
|
389
|
-
method: 'eth_signTypedData_v4',
|
|
390
|
-
params: [walletAddress, stringifyJson(typedData)]
|
|
391
|
-
})
|
|
392
|
-
},
|
|
393
|
-
clientModule: options.circleClientModule,
|
|
394
|
-
clientModuleUrl: options.circleClientModuleUrl
|
|
395
|
-
});
|
|
396
|
-
return gatewayWallet.pay(input);
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
async function unlock() {
|
|
400
|
-
setBusy(true);
|
|
401
|
-
try {
|
|
402
|
-
if (!walletAddress) await connect();
|
|
403
|
-
setBusy(true);
|
|
404
|
-
setStatus('Requesting Gateway unlock...');
|
|
405
|
-
const result = await checkResourceAccess(item.resource, {
|
|
406
|
-
accessPath,
|
|
407
|
-
source,
|
|
408
|
-
paymentProvider: options.paymentProvider || 'circle-gateway-browser',
|
|
409
|
-
challengeMessage: options.challengeMessage || 'Gateway payment required. Connect your wallet to continue...',
|
|
410
|
-
paymentMessage: options.paymentMessage || 'Approve the Gateway payment proof in your wallet...',
|
|
411
|
-
successMessage: options.successMessage || `Unlocked ${item.resource.title || 'content'}.`,
|
|
412
|
-
checkout,
|
|
413
|
-
onStatus: setStatus
|
|
414
|
-
});
|
|
415
|
-
if (result.ok) {
|
|
416
|
-
setUnlocked(true, result.payment || {});
|
|
417
|
-
if (typeof options.onUnlock === 'function') options.onUnlock(result);
|
|
418
|
-
}
|
|
419
|
-
return result;
|
|
420
|
-
} catch (error) {
|
|
421
|
-
const message = error?.message || 'Unlock failed. Please try again.';
|
|
422
|
-
setStatus(message);
|
|
423
|
-
return { ok: false, status: 0, error: message, resource: item.resource };
|
|
424
|
-
} finally {
|
|
425
|
-
setBusy(false);
|
|
426
|
-
renderWallet();
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
function clear() {
|
|
431
|
-
clearPaymentProof(item.resource);
|
|
432
|
-
setUnlocked(false);
|
|
433
|
-
setStatus('Local payment proof cleared. The next unlock will require Gateway payment again.');
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
async function hydrate() {
|
|
437
|
-
const evm = provider();
|
|
438
|
-
try {
|
|
439
|
-
const accounts = evm ? await evm.request({ method: 'eth_accounts' }) : [];
|
|
440
|
-
walletAddress = Array.isArray(accounts) ? accounts[0] || '' : '';
|
|
441
|
-
} catch {}
|
|
442
|
-
renderWallet();
|
|
443
|
-
setUnlocked(false);
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
function mount() {
|
|
447
|
-
connectButton?.addEventListener?.('click', () => connect().catch((error) => setStatus(error?.message || 'Could not connect wallet.')));
|
|
448
|
-
disconnectButton?.addEventListener?.('click', () => disconnect().catch((error) => setStatus(error?.message || 'Could not disconnect wallet.')));
|
|
449
|
-
unlockButton?.addEventListener?.('click', () => unlock());
|
|
450
|
-
clearButton?.addEventListener?.('click', clear);
|
|
451
|
-
hydrate();
|
|
452
|
-
trackResourcePage(item.resource, { source });
|
|
453
|
-
return controller;
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
const controller = { resource: item.resource, connect, disconnect, unlock, clear, hydrate, mount, getWalletAddress: () => walletAddress };
|
|
457
|
-
if (options.autoMount !== false) mount();
|
|
458
|
-
return controller;
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
export function createOnchainRating(resource, options = {}) {
|
|
462
|
-
const item = createGate(resource, options.gateOptions || {});
|
|
463
|
-
const win = browserWindow();
|
|
464
|
-
const statusTarget = typeof options.status === 'string' ? win?.document.querySelector(options.status) : options.status;
|
|
465
|
-
const ratingTarget = typeof options.ratingTarget === 'string' ? win?.document.querySelector(options.ratingTarget) : options.ratingTarget;
|
|
466
|
-
const buttonSelector = options.ratingButtons || options.buttons || '[data-nibgate-rating-value], [data-rating]';
|
|
467
|
-
const explicitButtons = Array.isArray(options.buttons)
|
|
468
|
-
? options.buttons
|
|
469
|
-
: typeof options.buttons === 'string'
|
|
470
|
-
? Array.from(win?.document.querySelectorAll(options.buttons) || [])
|
|
471
|
-
: options.buttons
|
|
472
|
-
? [options.buttons]
|
|
473
|
-
: null;
|
|
474
|
-
const buttons = explicitButtons || Array.from(win?.document.querySelectorAll(buttonSelector) || []);
|
|
475
|
-
const source = options.source || 'nibgate-onchain-rating';
|
|
476
|
-
|
|
477
|
-
let busy = false;
|
|
478
|
-
let payment = options.payment || null;
|
|
479
|
-
|
|
480
|
-
function setStatus(message) {
|
|
481
|
-
if (typeof options.onStatus === 'function') options.onStatus(message);
|
|
482
|
-
if (statusTarget) statusTarget.textContent = message || '';
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
function setBusy(value) {
|
|
486
|
-
busy = Boolean(value);
|
|
487
|
-
buttons.forEach((button) => {
|
|
488
|
-
if (button && 'disabled' in button) button.disabled = busy;
|
|
489
|
-
});
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
function setPayment(nextPayment = null) {
|
|
493
|
-
payment = nextPayment || null;
|
|
494
|
-
return payment;
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
function setVisible(isVisible) {
|
|
498
|
-
if (!ratingTarget) return Boolean(isVisible);
|
|
499
|
-
if ('hidden' in ratingTarget) ratingTarget.hidden = !isVisible;
|
|
500
|
-
ratingTarget.setAttribute('aria-hidden', isVisible ? 'false' : 'true');
|
|
501
|
-
return Boolean(isVisible);
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
function valueFromButton(button) {
|
|
505
|
-
const raw = button?.dataset?.nibgateRatingValue || button?.dataset?.rating || button?.value || button?.textContent;
|
|
506
|
-
const numeric = Number.parseFloat(String(raw || '').replace(/[^\d.]/g, ''));
|
|
507
|
-
return Number.isFinite(numeric) ? numeric : Number(options.rating || options.stars || 0);
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
async function rate(input = {}) {
|
|
511
|
-
setBusy(true);
|
|
512
|
-
try {
|
|
513
|
-
const rating = Number.parseFloat(input.rating ?? input.stars ?? input.value ?? options.rating ?? options.stars);
|
|
514
|
-
if (!Number.isFinite(rating)) throw new Error('Choose a rating before sending.');
|
|
515
|
-
const paymentId = input.paymentId || options.paymentId || (typeof options.getPaymentId === 'function' ? options.getPaymentId() : payment?.paymentId);
|
|
516
|
-
const unlockRef = input.unlockRef
|
|
517
|
-
|| options.unlockRef
|
|
518
|
-
|| (typeof options.getUnlockRef === 'function' ? options.getUnlockRef() : null)
|
|
519
|
-
|| paymentId
|
|
520
|
-
|| payment?.txHash
|
|
521
|
-
|| payment?.transactionHash
|
|
522
|
-
|| '';
|
|
523
|
-
|
|
524
|
-
setStatus(options.pendingMessage || 'Send the onchain rating transaction...');
|
|
525
|
-
const result = await rateContentOnchain(item.resource, {
|
|
526
|
-
...options,
|
|
527
|
-
...input,
|
|
528
|
-
rating,
|
|
529
|
-
paymentId,
|
|
530
|
-
unlockRef,
|
|
531
|
-
source
|
|
532
|
-
});
|
|
533
|
-
setStatus(options.successMessage || 'Rating sent to Nibgate reputation.');
|
|
534
|
-
if (typeof options.onRated === 'function') options.onRated(result);
|
|
535
|
-
return result;
|
|
536
|
-
} catch (error) {
|
|
537
|
-
const message = error?.message || options.errorMessage || 'Rating failed.';
|
|
538
|
-
setStatus(message);
|
|
539
|
-
if (typeof options.onError === 'function') options.onError(error);
|
|
540
|
-
throw error;
|
|
541
|
-
} finally {
|
|
542
|
-
setBusy(false);
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
function mount() {
|
|
547
|
-
buttons.forEach((button) => {
|
|
548
|
-
button?.addEventListener?.('click', () => rate({ rating: valueFromButton(button) }).catch(() => null));
|
|
549
|
-
});
|
|
550
|
-
if (options.visible !== undefined) setVisible(Boolean(options.visible));
|
|
551
|
-
return controller;
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
const controller = { resource: item.resource, rate, mount, setPayment, setVisible };
|
|
555
|
-
if (options.autoMount !== false) mount();
|
|
556
|
-
return controller;
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
export function mountRatingUI(resource, options = {}) {
|
|
560
|
-
const item = createGate(resource, options.gateOptions || {});
|
|
561
|
-
const win = browserWindow();
|
|
562
|
-
if (!win) return null;
|
|
563
|
-
const target = typeof options.target === 'string' ? win.document.querySelector(options.target) : options.target;
|
|
564
|
-
if (!target) return null;
|
|
565
|
-
|
|
566
|
-
const stars = [1, 2, 3, 4, 5];
|
|
567
|
-
let selectedRating = 0;
|
|
568
|
-
|
|
569
|
-
const container = win.document.createElement('div');
|
|
570
|
-
container.className = 'nibgate-rating-ui';
|
|
571
|
-
container.style.cssText = 'display:flex;align-items:center;gap:4px;padding:8px 0';
|
|
572
|
-
|
|
573
|
-
const starButtons = stars.map((value) => {
|
|
574
|
-
const btn = win.document.createElement('button');
|
|
575
|
-
btn.type = 'button';
|
|
576
|
-
btn.dataset.nibgateRatingValue = String(value);
|
|
577
|
-
btn.setAttribute('aria-label', `${value} star${value > 1 ? 's' : ''}`);
|
|
578
|
-
btn.innerHTML = '☆';
|
|
579
|
-
btn.style.cssText = 'background:none;border:none;font-size:24px;cursor:pointer;color:#ccc;transition:color 0.15s;padding:2px;line-height:1';
|
|
580
|
-
btn.addEventListener('mouseenter', () => {
|
|
581
|
-
starButtons.forEach((b, i) => b.style.color = i < value ? '#f5b342' : '#ccc');
|
|
582
|
-
});
|
|
583
|
-
btn.addEventListener('mouseleave', () => {
|
|
584
|
-
starButtons.forEach((b, i) => b.style.color = i < selectedRating ? '#f5b342' : '#ccc');
|
|
585
|
-
});
|
|
586
|
-
btn.addEventListener('click', () => {
|
|
587
|
-
selectedRating = value;
|
|
588
|
-
starButtons.forEach((b, i) => b.style.color = i < value ? '#f5b342' : '#ccc');
|
|
589
|
-
rate(item.resource, { rating: value }).catch(() => {});
|
|
590
|
-
});
|
|
591
|
-
container.appendChild(btn);
|
|
592
|
-
return btn;
|
|
593
|
-
});
|
|
594
|
-
|
|
595
|
-
const statusEl = win.document.createElement('span');
|
|
596
|
-
statusEl.style.cssText = 'font-size:13px;color:#888;margin-left:8px';
|
|
597
|
-
statusEl.textContent = options.label || 'Rate this content';
|
|
598
|
-
container.appendChild(statusEl);
|
|
599
|
-
|
|
600
|
-
target.appendChild(container);
|
|
601
|
-
|
|
602
|
-
function rate(r, input = {}) {
|
|
603
|
-
return item.rate({ ...input, rating: r });
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
function setRating(value) {
|
|
607
|
-
selectedRating = value;
|
|
608
|
-
starButtons.forEach((b, i) => b.style.color = i < value ? '#f5b342' : '#ccc');
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
return {
|
|
612
|
-
resource: item.resource,
|
|
613
|
-
container,
|
|
614
|
-
setRating,
|
|
615
|
-
rate
|
|
616
|
-
};
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
export async function payAndUnlockResource(resource, options = {}) {
|
|
620
|
-
const item = createGate(resource, options.gateOptions || {});
|
|
621
|
-
const payPath = options.payPath || item.resource.payPath || '/api/nibgate/pay';
|
|
622
|
-
const status = typeof options.onStatus === 'function' ? options.onStatus : () => {};
|
|
623
|
-
|
|
624
|
-
status(options.paymentMessage || 'Starting payment...');
|
|
625
|
-
item.unlockStarted({ source: options.source, paymentProvider: options.paymentProvider || 'circle-gateway' });
|
|
626
|
-
|
|
627
|
-
const response = await fetch(payPath, {
|
|
628
|
-
method: options.payMethod || 'POST',
|
|
629
|
-
headers: {
|
|
630
|
-
accept: 'application/json',
|
|
631
|
-
'content-type': 'application/json',
|
|
632
|
-
...(options.payHeaders || {})
|
|
633
|
-
},
|
|
634
|
-
body: JSON.stringify({
|
|
635
|
-
resource: item.resource,
|
|
636
|
-
...(options.payPayload || {})
|
|
637
|
-
})
|
|
638
|
-
});
|
|
639
|
-
const payload = await response.json().catch(() => ({}));
|
|
640
|
-
|
|
641
|
-
if (!response.ok || !payload.success) {
|
|
642
|
-
item.track('payment_failed', { source: options.source, status: response.status, error: payload.error || 'Payment failed', detail: payload.detail || '' });
|
|
643
|
-
status(payload.detail || payload.error || options.paymentErrorMessage || 'Payment failed.');
|
|
644
|
-
return { ok: false, status: response.status, payload, resource: item.resource, response };
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
const payment = payload.payment || {
|
|
648
|
-
paymentProvider: options.paymentProvider || 'circle-gateway',
|
|
649
|
-
paymentId: payload.paymentId || `nibgate_payment_${Date.now()}`,
|
|
650
|
-
amount: Number(item.resource.price || 0),
|
|
651
|
-
revenue: Number(item.resource.price || 0),
|
|
652
|
-
currency: item.resource.currency || 'USDC'
|
|
653
|
-
};
|
|
654
|
-
storePaymentProof(item.resource, payload.unlockProof);
|
|
655
|
-
item.markUnlocked(payment);
|
|
656
|
-
status(options.paymentSuccessMessage || 'Payment verified. Content unlocked.');
|
|
657
|
-
return { ok: true, status: response.status, payload, payment, resource: item.resource, response };
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
export function setupResourcePage(resource, options = {}) {
|
|
661
|
-
const item = trackResourcePage(resource, options);
|
|
662
|
-
const win = browserWindow();
|
|
663
|
-
if (!win) return item;
|
|
664
|
-
|
|
665
|
-
const button = typeof options.button === 'string' ? win.document.querySelector(options.button) : options.button;
|
|
666
|
-
const statusElement = typeof options.status === 'string' ? win.document.querySelector(options.status) : options.status;
|
|
667
|
-
const setStatus = options.onStatus || ((message) => {
|
|
668
|
-
if (statusElement) statusElement.textContent = message || '';
|
|
669
|
-
});
|
|
670
|
-
|
|
671
|
-
if (button) {
|
|
672
|
-
button.addEventListener('click', async () => {
|
|
673
|
-
button.disabled = true;
|
|
674
|
-
try {
|
|
675
|
-
await checkResourceAccess(resource, { ...options, onStatus: setStatus });
|
|
676
|
-
} finally {
|
|
677
|
-
button.disabled = false;
|
|
678
|
-
}
|
|
679
|
-
});
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
return item;
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
export function createNibgate(defaults = {}) {
|
|
686
|
-
const defaultResource = defaults.resource ? normalizeResource(defaults.resource) : null;
|
|
687
|
-
|
|
688
|
-
function resourceWithDefaults(resource = {}) {
|
|
689
|
-
return normalizeResource({
|
|
690
|
-
...(defaultResource || {}),
|
|
691
|
-
...(typeof resource === 'string' ? { id: resource } : resource)
|
|
692
|
-
});
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
return {
|
|
696
|
-
content(resource, extra = {}) {
|
|
697
|
-
return emit('content_registered', payloadWithResource(resourceWithDefaults(resource), extra));
|
|
698
|
-
},
|
|
699
|
-
registerContent(resource, extra = {}) {
|
|
700
|
-
return emit('content_registered', payloadWithResource(resourceWithDefaults(resource), extra));
|
|
701
|
-
},
|
|
702
|
-
view(resource, extra = {}) {
|
|
703
|
-
return emit('resource_view', payloadWithResource(resourceWithDefaults(resource), extra));
|
|
704
|
-
},
|
|
705
|
-
track(eventName, payload = {}) {
|
|
706
|
-
return emit(eventName || 'custom', payload);
|
|
707
|
-
},
|
|
708
|
-
unlockStarted(resource, extra = {}) {
|
|
709
|
-
return emit('unlock_started', payloadWithResource(resourceWithDefaults(resource), extra));
|
|
710
|
-
},
|
|
711
|
-
unlockCompleted(resource, payment = {}) {
|
|
712
|
-
return emit('unlock_completed', payloadWithResource(resourceWithDefaults(resource), payment));
|
|
713
|
-
},
|
|
714
|
-
paymentCompleted(resource, payment = {}) {
|
|
715
|
-
return emit('payment_completed', payloadWithResource(resourceWithDefaults(resource), payment));
|
|
716
|
-
},
|
|
717
|
-
rateResource(resource, rating = {}, extra = {}) {
|
|
718
|
-
return rateResource(resourceWithDefaults(resource), rating, extra);
|
|
719
|
-
},
|
|
720
|
-
ratingMessage(resource, rating = {}, messageOptions = {}) {
|
|
721
|
-
return ratingMessage(resourceWithDefaults(resource), rating, messageOptions);
|
|
722
|
-
},
|
|
723
|
-
gate(resource, options = {}) {
|
|
724
|
-
return createGate(resourceWithDefaults(resource), { ...options, client: this });
|
|
725
|
-
},
|
|
726
|
-
trackResourcePage(resource, options = {}) {
|
|
727
|
-
return trackResourcePage(resourceWithDefaults(resource), options);
|
|
728
|
-
},
|
|
729
|
-
checkResourceAccess(resource, options = {}) {
|
|
730
|
-
return checkResourceAccess(resourceWithDefaults(resource), options);
|
|
731
|
-
},
|
|
732
|
-
payWithPaymentSignature(resource, options = {}) {
|
|
733
|
-
return payWithPaymentSignature(resourceWithDefaults(resource), options);
|
|
734
|
-
},
|
|
735
|
-
createWalletCheckout(resource, options = {}) {
|
|
736
|
-
return createWalletCheckout(resourceWithDefaults(resource), options);
|
|
737
|
-
},
|
|
738
|
-
createCircleGatewayBrowserAdapter(options = {}) {
|
|
739
|
-
return createCircleGatewayBrowserAdapter(options);
|
|
740
|
-
},
|
|
741
|
-
createTransferCheckout(resource, options = {}) {
|
|
742
|
-
return createTransferCheckout(resourceWithDefaults(resource), options);
|
|
743
|
-
},
|
|
744
|
-
payWithTransfer(resource, options = {}) {
|
|
745
|
-
return payWithTransfer(resourceWithDefaults(resource), options);
|
|
746
|
-
},
|
|
747
|
-
createEvmGatewayUnlock(resource, options = {}) {
|
|
748
|
-
return createEvmGatewayUnlock(resourceWithDefaults(resource), options);
|
|
749
|
-
},
|
|
750
|
-
createOnchainRating(resource, options = {}) {
|
|
751
|
-
return createOnchainRating(resourceWithDefaults(resource), options);
|
|
752
|
-
},
|
|
753
|
-
mountRatingUI(resource, options = {}) {
|
|
754
|
-
return mountRatingUI(resourceWithDefaults(resource), options);
|
|
755
|
-
},
|
|
756
|
-
payAndUnlockResource(resource, options = {}) {
|
|
757
|
-
return payAndUnlockResource(resourceWithDefaults(resource), options);
|
|
758
|
-
},
|
|
759
|
-
setupResourcePage(resource, options = {}) {
|
|
760
|
-
return setupResourcePage(resourceWithDefaults(resource), options);
|
|
761
|
-
},
|
|
762
|
-
normalizeResource: resourceWithDefaults,
|
|
763
|
-
normalizeContentType,
|
|
764
|
-
flush: flushQueue
|
|
765
|
-
};
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
export const nibgate = createNibgate();
|
|
769
|
-
export const gate = createGate;
|
|
1
|
+
export { createGate } from './gate.js';
|
|
2
|
+
export { checkResourceAccess, payWithPaymentSignature, payAndUnlockResource } from './access.js';
|
|
3
|
+
export { createWalletCheckout } from './checkout.js';
|
|
4
|
+
export { createNibgate, nibgate } from './client.js';
|
|
5
|
+
export { createEvmGatewayUnlock, createCircleGatewayBrowserAdapter } from './evm-gateway.js';
|
|
6
|
+
export { rateResource, createOnchainRating, mountRatingUI } from './rating-ui.js';
|
|
7
|
+
export { trackResourcePage, setupResourcePage } from './track.js';
|
|
770
8
|
export { createTransferCheckout, payWithTransfer } from './transfer.js';
|
|
771
|
-
export {
|
|
9
|
+
export { contentRatingHash, NIBGATE_CONTENT_HASH_NAMESPACE, NIBGATE_REPUTATION_ABI, NIBGATE_REPUTATION_CHAIN_ID, NIBGATE_REPUTATION_CHAIN_NAME, NIBGATE_REPUTATION_CONTRACT, NIBGATE_REPUTATION_RPC_URL, rateContentOnchain, reviewTextHash } from './reputation.js';
|
|
10
|
+
export { CONTENT_TYPES, ACCESS_MODES, UNLOCK_MODES, normalizeContentType, normalizeResource, normalizeAccessPolicy, normalizeUnlockPolicy, validateResourceMetadata } from '../core/resource.js';
|
|
772
11
|
export { NIBGATE_CONTENT_SETTING_FIELDS, createNibgateContentSettings, settingsToAccessPolicy, settingsToUnlockPolicy } from '../core/settings.js';
|
|
773
12
|
export { PAYMENT_RAILS, normalizePaymentRail } from '../core/payment.js';
|