aboutsummaryrefslogtreecommitdiffstats
path: root/index.js
diff options
context:
space:
mode:
Diffstat (limited to 'index.js')
-rw-r--r--index.js9
1 files changed, 3 insertions, 6 deletions
diff --git a/index.js b/index.js
index 6a34f543..7f97a20d 100644
--- a/index.js
+++ b/index.js
@@ -44,17 +44,14 @@ app.get("/locations", locations.list);
app.get("/invoice", invoices.get);
app.post("/invoice", optional, invoices.create);
-app.post("/send", auth, payments.send);
+app.post("/payments", auth, payments.create);
app.get("/payments", auth, payments.list);
app.get("/payments/:hash", auth, payments.get);
-app.post("/lightning/send", auth, payments.sendLightning);
+app.post("/query", auth, payments.query);
app.get("/pot/:name", payments.pot);
-app.post("/withdraw", auth, payments.withdraw);
+app.post("/take", auth, payments.take);
app.post("/bitcoin", payments.bitcoin);
-app.get("/voucher/:id", payments.voucher);
-app.post("/redeem", optional, payments.redeem);
-
app.get("/me", auth, users.me);
app.get("/users/:key", users.get);
app.post("/register", users.create);
nt-style: italic } /* Name.Label */ .highlight .nn { color: #B06; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #369; font-weight: bold } /* Name.Property */ .highlight .nt { color: #B06; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #369 } /* Name.Variable */ .highlight .ow { color: #080 } /* Operator.Word */ .highlight .w { color: #BBB } /* Text.Whitespace */ .highlight .mb { color: #00D; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #00D; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #00D; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #00D; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #00D; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #D20; background-color: #FFF0F0 } /* Literal.String.Affix */ .highlight .sb { color: #D20; background-color: #FFF0F0 } /* Literal.String.Backtick */ .highlight .sc { color: #D20; background-color: #FFF0F0 } /* Literal.String.Char */ .highlight .dl { color: #D20; background-color: #FFF0F0 } /* Literal.String.Delimiter */ .highlight .sd { color: #D20; background-color: #FFF0F0 } /* Literal.String.Doc */ .highlight .s2 { color: #D20; background-color: #FFF0F0 } /* Literal.String.Double */ .highlight .se { color: #04D; background-color: #FFF0F0 } /* Literal.String.Escape */ .highlight .sh { color: #D20; background-color: #FFF0F0 } /* Literal.String.Heredoc */ .highlight .si { color: #33B; background-color: #FFF0F0 } /* Literal.String.Interpol */ .highlight .sx { color: #2B2; background-color: #F0FFF0 } /* Literal.String.Other */ .highlight .sr { color: #080; background-color: #FFF0FF } /* Literal.String.Regex */ .highlight .s1 { color: #D20; background-color: #FFF0F0 } /* Literal.String.Single */ .highlight .ss { color: #A60; background-color: #FFF0F0 } /* Literal.String.Symbol */ .highlight .bp { color: #038 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #06B; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #369 } /* Name.Variable.Class */ .highlight .vg { color: #D70 } /* Name.Variable.Global */ .highlight .vi { color: #33B } /* Name.Variable.Instance */ .highlight .vm { color: #369 } /* Name.Variable.Magic */ .highlight .il { color: #00D; font-weight: bold } /* Literal.Number.Integer.Long */
// Every feature must survive being built, started, rendered and stopped
// against a stubbed wallet — the cheap check that catches typos and
// undefined helpers that only bite at runtime in the browser.
// Run: bun tools/features-smoke-test.js
import { buildFeatures } from '../src/features/index.js';

let fails = 0;
const check = (name, ok, detail = '') => {
  console.log(` ${ok ? '✓' : '✗'} ${name}${detail ? ' — ' + String(detail).slice(0, 160) : ''}`);
  if (!ok) fails++;
};

// Nothing may reach the network in a smoke test; failing fast also exercises
// each feature's error path, which is where the runtime typos hide.
const realFetch = globalThis.fetch;
globalThis.fetch = async () => { throw new Error('offline (smoke test)'); };
globalThis.window = undefined;
globalThis.localStorage = {
  _d: {}, getItem(k) { return this._d[k] ?? null; }, setItem(k, v) { this._d[k] = String(v); },
  removeItem(k) { delete this._d[k]; }, key(i) { return Object.keys(this._d)[i] ?? null; },
  get length() { return Object.keys(this._d).length; },
};

const errors = [];
const store = {};
const wallet = {
  watchOnly: false, offline: false, mnemonic: 'x '.repeat(11) + 'y',
  utxos: [], txs: [], receive: [], change: [],
  account: () => null,
  loadFeatureState: (n, fb) => JSON.parse(store[n] || 'null') || fb,
  saveFeatureState: (n, st) => { store[n] = JSON.stringify(st); },
  _cacheKey: () => 'smoke', saveCache: () => {},
  nostrPubkey: () => 'ab'.repeat(32),
  nostrNpub: () => 'npub1smoketestsmoketestsmoketestsmoketestsmoketestsmoke',
  nostrSign: (e) => ({ ...e, id: 'ab'.repeat(32), pubkey: 'ab'.repeat(32), sig: 'cd'.repeat(64) }),
  registerCacheExtension: () => {}, registerRealtimeHook: () => {}, registerLoadHook: () => {},
  registerScanHook: () => {}, registerCacheSavedHook: () => {}, registerCoinLock: () => {},
  loadArkState: () => null, saveArkState: () => {}, _arkNs: () => 'ns', _arkKey: () => 'k',
  _arkHoldKey: () => 'h', _arkVtxoOwner: () => null, adoptArkState: () => null,
  freshReceive: () => ({ address: 'bc1qsmoke' }), freshChange: () => ({ address: 'bc1qchange' }),
  balance: () => ({ spendableSat: 0 }), emit: () => {}, on: () => {},
};
const ctx = {
  h: (tag, attrs, ...kids) => ({ tag, attrs, kids }),
  ui: {}, render: () => {}, wallet,
  hook: () => null, getAccount: () => 'savings', openMnemonic: async () => {},
  fmtAmount: String, unitLabel: () => 'sats', unitTag: () => 'sats', getUnit: () => 'sats',
  parseAmount: (v) => parseInt(v, 10) || 0,
  copyBtn: () => null, pasteBtn: () => null, toast: () => {}, blankSend: () => ({}),
  openExternal: () => {},
};

let features = [];
try {
  features = buildFeatures(ctx);
  check('all features construct', features.length > 0, `${features.length} features`);
} catch (e) { check('all features construct', false, e.message); }

// Surface async failures (an undefined helper inside an async init shows up
// as an unhandled rejection, not a throw).
process.on('unhandledRejection', (e) => errors.push(String(e && e.message || e)));

for (const f of features) {
  for (const m of ['init', 'settingsCards', 'receiveModes', 'historyEntries', 'stop']) {
    if (typeof f[m] !== 'function') continue;
    try { f[m](); } catch (e) { errors.push(`${f.id}.${m}: ${e.message}`); }
  }
}
await new Promise((r) => setTimeout(r, 300)); // let async inits settle

const runtime = errors.filter((e) => /is not defined|is not a function|Cannot read/.test(e));
check('no undefined identifiers at runtime', runtime.length === 0, runtime.join(' | '));
if (errors.length && !runtime.length) console.log(`   (${errors.length} benign offline error(s) ignored)`);

globalThis.fetch = realFetch;
console.log(fails ? `\n❌ ${fails} failure(s)` : '\n✅ features survive a cold start');
process.exit(fails ? 1 : 0);