javascript-solid-server 0.0.129 → 0.0.131

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.
@@ -342,7 +342,8 @@
342
342
  "Bash(sed -i 's/\"currency\": \"sats\"/\"currency\": \"tbtc4\"/g' /home/melvin/articles/solid-402.html /home/melvin/articles/solid-pay.html /home/melvin/articles/solid-balance.html)",
343
343
  "Bash(sed -i 's/\"currency\":\"sats\"/\"currency\":\"tbtc4\"/g' /home/melvin/articles/solid-402.html /home/melvin/articles/solid-pay.html /home/melvin/articles/solid-balance.html)",
344
344
  "WebFetch(domain:lists.w3.org)",
345
- "Bash(git:*)"
345
+ "Bash(git:*)",
346
+ "Bash(npm publish:*)"
346
347
  ]
347
348
  }
348
349
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "javascript-solid-server",
3
- "version": "0.0.129",
3
+ "version": "0.0.131",
4
4
  "description": "A minimal, fast Solid server",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -104,7 +104,7 @@ export async function authorize(request, reply, options = {}) {
104
104
  }
105
105
 
106
106
  // Check WAC permissions
107
- const { allowed, wacAllow, paymentRequired } = await checkAccess({
107
+ const { allowed, wacAllow, paymentRequired, paid, balance, currency } = await checkAccess({
108
108
  resourceUrl: checkUrl,
109
109
  resourcePath: checkPath,
110
110
  isContainer: checkIsContainer,
@@ -112,7 +112,7 @@ export async function authorize(request, reply, options = {}) {
112
112
  requiredMode
113
113
  });
114
114
 
115
- return { authorized: allowed, webId, wacAllow, authError, paymentRequired };
115
+ return { authorized: allowed, webId, wacAllow, authError, paymentRequired, paid, balance, currency };
116
116
  }
117
117
 
118
118
  /**
@@ -96,7 +96,7 @@ export function getCorsHeaders(origin) {
96
96
  'Access-Control-Allow-Origin': origin || '*',
97
97
  'Access-Control-Allow-Methods': 'GET, HEAD, POST, PUT, DELETE, PATCH, OPTIONS',
98
98
  'Access-Control-Allow-Headers': 'Accept, Authorization, Content-Type, DPoP, If-Match, If-None-Match, Link, Range, Slug, Origin',
99
- 'Access-Control-Expose-Headers': 'Accept-Patch, Accept-Post, Accept-Ranges, Allow, Content-Length, Content-Range, Content-Type, ETag, Link, Location, Updates-Via, WAC-Allow',
99
+ 'Access-Control-Expose-Headers': 'Accept-Patch, Accept-Post, Accept-Ranges, Allow, Content-Length, Content-Range, Content-Type, ETag, Link, Location, Updates-Via, WAC-Allow, X-Cost, X-Balance, X-Pay-Currency',
100
100
  'Access-Control-Allow-Credentials': 'true',
101
101
  'Access-Control-Max-Age': '86400'
102
102
  };
package/src/server.js CHANGED
@@ -448,7 +448,7 @@ export function createServer(options = {}) {
448
448
  return;
449
449
  }
450
450
 
451
- const { authorized, webId, wacAllow, authError, paymentRequired } = await authorize(request, reply);
451
+ const { authorized, webId, wacAllow, authError, paymentRequired, paid, balance, currency } = await authorize(request, reply);
452
452
 
453
453
  // Store webId and wacAllow on request for handlers to use
454
454
  request.webId = webId;
@@ -457,6 +457,13 @@ export function createServer(options = {}) {
457
457
  // Set WAC-Allow header for all responses (handlers may override)
458
458
  reply.header('WAC-Allow', wacAllow);
459
459
 
460
+ // Set payment headers for paid access
461
+ if (paid !== undefined) {
462
+ reply.header('X-Cost', String(paid));
463
+ reply.header('X-Balance', String(balance));
464
+ if (currency) reply.header('X-Pay-Currency', currency);
465
+ }
466
+
460
467
  // Handle payment-gated resources
461
468
  if (paymentRequired) {
462
469
  return reply.code(402).send({
package/src/token.js CHANGED
@@ -187,33 +187,34 @@ async function broadcastTx(rawTxHex, mempoolUrl) {
187
187
  }
188
188
 
189
189
  // --- Trail persistence ---
190
- function trailDir() {
191
- return path.join(process.env.DATA_ROOT || './data', '.well-known', 'token');
190
+ function trailDir(root) {
191
+ return path.join(root || process.env.DATA_ROOT || './data', '.well-known', 'token');
192
192
  }
193
193
 
194
- function trailPath(ticker) {
195
- return path.join(trailDir(), `${ticker.toLowerCase()}.json`);
194
+ function trailPath(ticker, root) {
195
+ return path.join(trailDir(root), `${ticker.toLowerCase()}.json`);
196
196
  }
197
197
 
198
- export async function loadTrail(ticker) {
198
+ export async function loadTrail(ticker, root) {
199
199
  try {
200
- const data = await fs.readFile(trailPath(ticker), 'utf8');
200
+ const data = await fs.readFile(trailPath(ticker, root), 'utf8');
201
201
  return JSON.parse(data);
202
202
  } catch { return null; }
203
203
  }
204
204
 
205
- async function saveTrail(trail) {
206
- await fs.ensureDir(trailDir());
207
- await fs.writeFile(trailPath(trail.ticker), JSON.stringify(trail, null, 2));
205
+ async function saveTrail(trail, root) {
206
+ await fs.ensureDir(trailDir(root));
207
+ await fs.writeFile(trailPath(trail.ticker, root), JSON.stringify(trail, null, 2));
208
208
  }
209
209
 
210
- export async function listTrails() {
210
+ export async function listTrails(root) {
211
211
  try {
212
- const files = await fs.readdir(trailDir());
212
+ const dir = trailDir(root);
213
+ const files = await fs.readdir(dir);
213
214
  const trails = [];
214
215
  for (const f of files) {
215
216
  if (f.endsWith('.json')) {
216
- const data = await fs.readFile(path.join(trailDir(), f), 'utf8');
217
+ const data = await fs.readFile(path.join(dir, f), 'utf8');
217
218
  trails.push(JSON.parse(data));
218
219
  }
219
220
  }
@@ -235,14 +236,14 @@ export function parseTxoUri(uri) {
235
236
  }
236
237
 
237
238
  // --- Mint: create genesis MRC20 token ---
238
- export async function mintToken({ ticker, name, supply, voucher, mempoolUrl = 'https://mempool.space/testnet4', network = 'testnet4' }) {
239
+ export async function mintToken({ ticker, name, supply, voucher, mempoolUrl = 'https://mempool.space/testnet4', network = 'testnet4', root }) {
239
240
  const txo = parseTxoUri(voucher);
240
241
  const privkeyBytes = hexToU8(txo.privkey);
241
242
  const pubkeyBase = new Uint8Array(secp256k1.getPublicKey(privkeyBytes, true));
242
243
  const pubkeyBaseHex = bytesToHex(pubkeyBase);
243
244
 
244
245
  // Check if token already exists
245
- const existing = await loadTrail(ticker);
246
+ const existing = await loadTrail(ticker, root);
246
247
  if (existing) throw new Error(`Token ${ticker} already exists`);
247
248
 
248
249
  // Create genesis MRC20 state
@@ -300,14 +301,14 @@ export async function mintToken({ ticker, name, supply, voucher, mempoolUrl = 'h
300
301
  network,
301
302
  dateCreated: new Date().toISOString()
302
303
  };
303
- await saveTrail(trail);
304
+ await saveTrail(trail, root);
304
305
 
305
306
  return { trail, txid: newTxid, address: genesisAddr };
306
307
  }
307
308
 
308
309
  // --- Transfer: send tokens to an address ---
309
- export async function transferToken({ ticker, from, to, amount, mempoolUrl = 'https://mempool.space/testnet4' }) {
310
- const trail = await loadTrail(ticker);
310
+ export async function transferToken({ ticker, from, to, amount, mempoolUrl = 'https://mempool.space/testnet4', root }) {
311
+ const trail = await loadTrail(ticker, root);
311
312
  if (!trail) throw new Error(`Token ${ticker} not found`);
312
313
 
313
314
  const privkeyBytes = hexToU8(trail.privkey);
@@ -382,14 +383,14 @@ export async function transferToken({ ticker, from, to, amount, mempoolUrl = 'ht
382
383
  trail.currentTxid = newTxid;
383
384
  trail.currentVout = 0;
384
385
  trail.currentAmount = outputAmount;
385
- await saveTrail(trail);
386
+ await saveTrail(trail, root);
386
387
 
387
388
  return { trail, txid: newTxid, address: newAddr, state: newState, prevState: currentState };
388
389
  }
389
390
 
390
391
  // --- Info: show token state ---
391
- export async function tokenInfo(ticker) {
392
- const trail = await loadTrail(ticker);
392
+ export async function tokenInfo(ticker, { root } = {}) {
393
+ const trail = await loadTrail(ticker, root);
393
394
  if (!trail) throw new Error(`Token ${ticker} not found`);
394
395
 
395
396
  const currentState = trail.states[trail.states.length - 1];
@@ -49,7 +49,7 @@ export async function checkAccess({
49
49
  // Calculate WAC-Allow header
50
50
  const wacAllow = calculateWacAllow(authorizations, resourceUrl, agentWebId, isDefault);
51
51
 
52
- return { allowed: result.allowed, wacAllow, paymentRequired: result.paymentRequired || null };
52
+ return { allowed: result.allowed, wacAllow, paymentRequired: result.paymentRequired || null, paid: result.paid, balance: result.balance, currency: result.currency };
53
53
  }
54
54
 
55
55
  /**
@@ -183,10 +183,10 @@ async function checkAuthorizations(authorizations, targetUrl, agentWebId, requir
183
183
  // Paid access: check balance and deduct
184
184
  const balance = getBalance(ledger, agentWebId, currency);
185
185
  if (cost > 0 && balance >= cost) {
186
- debit(ledger, agentWebId, cost, currency);
186
+ const result = debit(ledger, agentWebId, cost, currency);
187
187
  const { writeLedger } = await import('../webledger.js');
188
188
  await writeLedger(ledger);
189
- return { allowed: true, paid: cost };
189
+ return { allowed: true, paid: cost, balance: result.balance, currency };
190
190
  }
191
191
  } catch (e) {
192
192
  // Ledger read failed — fall through to payment required