payment-kit 1.29.2 → 1.29.4
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/api/src/bootstrap.ts +11 -0
- package/api/src/crons/index.ts +14 -13
- package/api/src/crons/tenant-fanout.ts +82 -0
- package/api/src/host-node/did-connect-runtime-node.ts +33 -0
- package/api/src/host-node/serve-static-arc.ts +68 -0
- package/api/src/host-node/serve-static.ts +41 -0
- package/api/src/libs/auth.ts +166 -27
- package/api/src/libs/context.ts +11 -0
- package/api/src/libs/did-connect/runtime-did-connect-js.ts +88 -0
- package/api/src/libs/did-connect/tenant-identity.ts +221 -0
- package/api/src/libs/drivers/identity.ts +61 -0
- package/api/src/libs/drivers/index.ts +1 -1
- package/api/src/libs/http-fetch-adapter.ts +11 -1
- package/api/src/libs/queue/index.ts +14 -2
- package/api/src/middlewares/hono/context.ts +7 -0
- package/api/src/middlewares/hono/csrf.ts +13 -2
- package/api/src/middlewares/hono/security.ts +6 -11
- package/api/src/queues/checkout-session.ts +21 -9
- package/api/src/queues/event.ts +29 -7
- package/api/src/queues/payment.ts +23 -9
- package/api/src/queues/payout.ts +28 -16
- package/api/src/queues/refund.ts +18 -6
- package/api/src/routes/hono/customers.ts +6 -1
- package/api/src/routes/hono/refunds.ts +2 -3
- package/api/src/service.ts +178 -31
- package/api/src/store/sequelize.ts +16 -1
- package/api/tests/bootstrap/bootstrap.spec.ts +162 -0
- package/api/tests/crons/tenant-fanout.spec.ts +158 -0
- package/api/tests/libs/did-connect-runtime-js.spec.ts +98 -0
- package/api/tests/libs/did-connect-tenant-identity.spec.ts +159 -0
- package/api/tests/libs/service-host.spec.ts +37 -0
- package/api/tests/queues/event-tenant.spec.ts +60 -4
- package/api/tests/service/didconnect-storage-slot.spec.ts +60 -0
- package/api/tests/service/fail-closed-http.spec.ts +79 -0
- package/api/tests/service/static-arc-handler.spec.ts +101 -0
- package/api/tests/service/static-externalized.spec.ts +48 -0
- package/blocklet.yml +1 -1
- package/cloudflare/MIGRATION-RUNBOOK.md +3 -8
- package/cloudflare/README.md +8 -21
- package/cloudflare/STAGING-MIGRATION-GUIDE.md +3 -15
- package/cloudflare/build.ts +10 -5
- package/cloudflare/cf-adapter.ts +419 -0
- package/cloudflare/did-connect-runtime.ts +96 -0
- package/cloudflare/did-connect-token-storage.ts +151 -0
- package/cloudflare/esbuild-cf-config.cjs +407 -0
- package/cloudflare/run-build.js +33 -357
- package/cloudflare/scripts/cf-package-import-probe.mjs +90 -0
- package/cloudflare/scripts/didconnect-mock-smoke.mjs +140 -0
- package/cloudflare/shims/blocklet-sdk/wallet-authenticator.ts +16 -1
- package/cloudflare/shims/blocklet-sdk/wallet-handler.ts +18 -3
- package/cloudflare/tests/cf-adapter.spec.ts +244 -0
- package/cloudflare/tests/did-connect-token-storage.spec.ts +105 -0
- package/cloudflare/tests/worker-handler-gate.spec.ts +35 -10
- package/cloudflare/vite.config.ts +53 -45
- package/cloudflare/worker.ts +98 -56
- package/cloudflare/wrangler.json +0 -6
- package/cloudflare/wrangler.jsonc +0 -6
- package/cloudflare/wrangler.local-e2e.jsonc +0 -1
- package/cloudflare/wrangler.staging.json +0 -6
- package/package.json +7 -7
- package/scripts/bootstrap-inject.ts +166 -0
- package/src/app.tsx +2 -1
- package/src/libs/service-host.ts +13 -0
- package/vite.arc.config.ts +159 -0
- package/cloudflare/did-connect-auth.ts +0 -310
- package/cloudflare/shims/blocklet-sdk/util-csrf.ts +0 -13
- package/cloudflare/shims/blocklet-sdk/util-wallet.ts +0 -8
package/cloudflare/run-build.js
CHANGED
|
@@ -1,366 +1,42 @@
|
|
|
1
1
|
const { build } = require("esbuild");
|
|
2
2
|
const path = require("path");
|
|
3
|
+
const { buildCfEsbuildOptions } = require("./esbuild-cf-config.cjs");
|
|
4
|
+
|
|
3
5
|
const cfDir = __dirname;
|
|
4
6
|
const s = (f) => path.resolve(cfDir, f);
|
|
5
7
|
|
|
6
|
-
// Phase
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
// Replace every `_virtual/rolldown_runtime.mjs` with a stub whose `__require`
|
|
27
|
-
// returns a Proxy that swallows further property accesses + function calls,
|
|
28
|
-
// and whose `__commonJSMin` returns an empty-module factory. This lets the
|
|
29
|
-
// importing modules finish loading without crashing the worker at startup.
|
|
30
|
-
const rolldownRuntimeStub = `
|
|
31
|
-
// After the Phase 2 CBOR switch (payment-method.ts + did-connect-auth.ts
|
|
32
|
-
// moved off @ocap/client/legacy), no codepath in the worker calls
|
|
33
|
-
// __require('google-protobuf') at runtime. The CBOR-only @ocap/client
|
|
34
|
-
// default entry + @ocap/client/encode use plain ESM imports for their
|
|
35
|
-
// deps. Any remaining __require calls in Rolldown-compiled packages
|
|
36
|
-
// (debug, @arcblock/event-hub, etc.) are handled by the __noop Proxy
|
|
37
|
-
// below — they don't need real modules for payment-kit's flows.
|
|
38
|
-
const __requireMap = {};
|
|
39
|
-
const __noopTarget = function(){};
|
|
40
|
-
const __noopHandler = {
|
|
41
|
-
get(t, p) {
|
|
42
|
-
if (p === Symbol.toPrimitive || p === 'toString') return () => '';
|
|
43
|
-
if (p === 'default') return new Proxy(__noopTarget, __noopHandler);
|
|
44
|
-
return new Proxy(__noopTarget, __noopHandler);
|
|
45
|
-
},
|
|
46
|
-
apply() { return new Proxy(__noopTarget, __noopHandler); },
|
|
47
|
-
construct() { return new Proxy(__noopTarget, __noopHandler); },
|
|
48
|
-
};
|
|
49
|
-
const __noop = new Proxy(__noopTarget, __noopHandler);
|
|
50
|
-
export const __commonJSMin = function(cb){
|
|
51
|
-
return function(){
|
|
52
|
-
var m = { exports: {} };
|
|
53
|
-
try { cb(m.exports, m); } catch (_) {}
|
|
54
|
-
return m.exports;
|
|
55
|
-
};
|
|
56
|
-
};
|
|
57
|
-
export const __require = function(id){
|
|
58
|
-
if (__requireMap[id]) return __requireMap[id];
|
|
59
|
-
return __noop;
|
|
60
|
-
};
|
|
61
|
-
export const __exportAll = function(target, source){
|
|
62
|
-
if (source) {
|
|
63
|
-
try {
|
|
64
|
-
for (var key in source) {
|
|
65
|
-
if (key !== 'default' && !Object.prototype.hasOwnProperty.call(target, key)) {
|
|
66
|
-
Object.defineProperty(target, key, {
|
|
67
|
-
get: function() { return source[key]; },
|
|
68
|
-
enumerable: true,
|
|
69
|
-
configurable: true,
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
} catch (_) {}
|
|
74
|
-
}
|
|
75
|
-
return target;
|
|
76
|
-
};
|
|
77
|
-
export const __toESM = function(mod){ return mod && mod.__esModule ? mod : { default: mod }; };
|
|
78
|
-
export const __toCommonJS = function(mod){ return mod; };
|
|
79
|
-
export const __copyProps = function(target){ return target; };
|
|
80
|
-
export default { __commonJSMin, __require, __exportAll, __toESM, __toCommonJS, __copyProps };
|
|
81
|
-
`;
|
|
82
|
-
const rolldownRuntimeNoopPlugin = {
|
|
83
|
-
name: 'rolldown-runtime-noop',
|
|
84
|
-
setup(build) {
|
|
85
|
-
// Catch any import that resolves to a `_virtual/rolldown_runtime.mjs`
|
|
86
|
-
// file, regardless of which package it comes from.
|
|
87
|
-
build.onResolve({ filter: /_virtual\/rolldown_runtime\.mjs$/ }, (args) => {
|
|
88
|
-
return { path: args.path, namespace: 'rolldown-runtime-noop' };
|
|
89
|
-
});
|
|
90
|
-
build.onLoad({ filter: /.*/, namespace: 'rolldown-runtime-noop' }, () => ({
|
|
91
|
-
contents: rolldownRuntimeStub,
|
|
92
|
-
loader: 'js',
|
|
93
|
-
// Need a resolveDir so the virtual stub's `import "google-protobuf"` resolves
|
|
94
|
-
// against the workspace's node_modules. Pointing at cfDir is sufficient since
|
|
95
|
-
// pnpm hoists google-protobuf up to the repo root node_modules.
|
|
96
|
-
resolveDir: cfDir,
|
|
97
|
-
}));
|
|
98
|
-
},
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
// Plugin: fix lodash sub-path CJS/ESM interop
|
|
102
|
-
// The "lodash" → "lodash-es" alias also redirects require('lodash/camelCase')
|
|
103
|
-
// to lodash-es/camelCase (ESM). CJS consumers get { __esModule, default: fn }
|
|
104
|
-
// instead of the function itself, causing "_m is not a function" at runtime.
|
|
105
|
-
// Fix: intercept lodash-es sub-path imports from CJS contexts and generate a
|
|
106
|
-
// virtual wrapper that re-exports the default as module.exports.
|
|
107
|
-
const lodashSubpathPlugin = {
|
|
108
|
-
name: 'lodash-subpath-interop',
|
|
109
|
-
setup(build) {
|
|
110
|
-
// Intercept resolved lodash-es sub-path imports that come from require() calls
|
|
111
|
-
// Intercept CJS require('lodash/xxx') — the alias hasn't applied yet at this stage
|
|
112
|
-
build.onResolve({ filter: /^lodash\/.+/ }, (args) => {
|
|
113
|
-
if (args.kind === 'require-call') {
|
|
114
|
-
// Convert lodash/xxx → lodash-es/xxx and wrap for CJS compat
|
|
115
|
-
const esPath = args.path.replace(/^lodash\//, 'lodash-es/');
|
|
116
|
-
return {
|
|
117
|
-
path: esPath,
|
|
118
|
-
namespace: 'lodash-cjs-compat',
|
|
119
|
-
};
|
|
8
|
+
// S3-CF Phase 2: the worker-safe alias / shim / plugin / banner table now lives in
|
|
9
|
+
// esbuild-cf-config.cjs, shared with packages/payment-core/build-cf.mjs (the
|
|
10
|
+
// re-bundleable @arcblock/payment-service/cf library) so the standalone Worker and
|
|
11
|
+
// the embedded ./cf artifact can never drift. This script only adds the bits unique
|
|
12
|
+
// to the standalone Worker bundle: the worker.ts entry point (with a default
|
|
13
|
+
// export) and the dist/meta.json + size report.
|
|
14
|
+
|
|
15
|
+
build(
|
|
16
|
+
buildCfEsbuildOptions({
|
|
17
|
+
entryPoints: [s("worker.ts")],
|
|
18
|
+
outdir: s("dist"),
|
|
19
|
+
})
|
|
20
|
+
)
|
|
21
|
+
.then((result) => {
|
|
22
|
+
require("fs").writeFileSync(s("dist/meta.json"), JSON.stringify(result.metafile));
|
|
23
|
+
Object.entries(result.metafile.outputs).forEach(function (entry) {
|
|
24
|
+
var k = entry[0],
|
|
25
|
+
v = entry[1];
|
|
26
|
+
if (k.indexOf(".map") === -1) {
|
|
27
|
+
console.log(k + ": " + (v.bytes / 1024).toFixed(1) + "KB");
|
|
120
28
|
}
|
|
121
29
|
});
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
.replace(/^export default /m, 'module.exports = ')
|
|
133
|
-
.replace(/^export\s*\{[^}]*\}\s*;?\s*$/m, '');
|
|
134
|
-
return {
|
|
135
|
-
contents: transformed,
|
|
136
|
-
loader: 'js',
|
|
137
|
-
resolveDir: require('path').dirname(filePath),
|
|
138
|
-
};
|
|
139
|
-
});
|
|
140
|
-
},
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
// Plugin: redirect bare `@arcblock/did-util` and `@ocap/message` imports to their
|
|
144
|
-
// CBOR-only subpath entries, and noop `@arcblock/did-util/protobuf`. This strips
|
|
145
|
-
// the protobuf runtime (`@ocap/proto/runtime`, `google-protobuf`, `*_pb.js`) out
|
|
146
|
-
// of the CF Workers bundle. Only matches EXACT bare specifiers — subpath imports
|
|
147
|
-
// (e.g. `@arcblock/did-util/cbor`) pass through untouched.
|
|
148
|
-
const cborOnlyPlugin = {
|
|
149
|
-
name: 'cbor-only-redirect',
|
|
150
|
-
setup(build) {
|
|
151
|
-
build.onResolve({ filter: /^@arcblock\/did-util$/ }, () => ({
|
|
152
|
-
path: path.resolve(__dirname, '../../..', 'node_modules/@arcblock/did-util/esm/cbor.mjs'),
|
|
153
|
-
}));
|
|
154
|
-
build.onResolve({ filter: /^@ocap\/message$/ }, () => ({
|
|
155
|
-
path: path.resolve(__dirname, '../../..', 'node_modules/@ocap/message/esm/cbor.mjs'),
|
|
156
|
-
}));
|
|
157
|
-
build.onResolve({ filter: /^@arcblock\/did-util\/protobuf$/ }, () => ({
|
|
158
|
-
path: s('shims/noop.ts'),
|
|
159
|
-
}));
|
|
160
|
-
},
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
// Plugin: noop entire package trees that have sub-path imports (alias alone
|
|
164
|
-
// doesn't work because esbuild treats "axon" alias as prefix, breaking
|
|
165
|
-
// "axon/lib/plugins/queue"). This plugin intercepts bare + sub-path imports.
|
|
166
|
-
const noopPackagesPlugin = {
|
|
167
|
-
name: 'noop-packages',
|
|
168
|
-
setup(build) {
|
|
169
|
-
const packages = ['axon', '@arcblock/ws', '@arcblock/event-hub', 'graphql', 'follow-redirects', 'proxy-from-env'];
|
|
170
|
-
const filter = new RegExp('^(' + packages.join('|') + ')(\\/|$)');
|
|
171
|
-
build.onResolve({ filter }, () => ({ path: s('shims/noop.ts') }));
|
|
172
|
-
},
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
// Plugin: drop ethers' non-English BIP39 wordlists (~70K dead weight). The payment
|
|
176
|
-
// worker never uses Mnemonic/HD wallets (0 source refs to Mnemonic/HDNode/wordlist),
|
|
177
|
-
// so the 8 non-English word tables never execute. ethers' own /dist build strips
|
|
178
|
-
// these too (~80kb). Keep LangEn (Mnemonic default). Stub the rest with a static
|
|
179
|
-
// wordlist() returning null so wordlists.js's top-level LangXx.wordlist() calls
|
|
180
|
-
// (run at module init) don't crash.
|
|
181
|
-
const dropEthersWordlistsPlugin = {
|
|
182
|
-
name: 'drop-ethers-wordlists',
|
|
183
|
-
setup(build) {
|
|
184
|
-
build.onLoad({ filter: /ethers\/lib\.esm\/wordlists\/lang-(cz|es|fr|ja|ko|it|pt|zh)\.js$/ }, (args) => {
|
|
185
|
-
const lang = /lang-(\w+)\.js$/.exec(args.path)[1];
|
|
186
|
-
const cls = 'Lang' + lang.charAt(0).toUpperCase() + lang.slice(1);
|
|
187
|
-
return { contents: `export class ${cls} { static wordlist() { return null; } }`, loader: 'js' };
|
|
188
|
-
});
|
|
189
|
-
},
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
build({
|
|
193
|
-
entryPoints: [s("worker.ts")],
|
|
194
|
-
bundle: true, format: "esm", platform: "node", target: "esnext",
|
|
195
|
-
outdir: s("dist"), minify: true, sourcemap: true, metafile: true,
|
|
196
|
-
mainFields: ["module", "main"],
|
|
197
|
-
plugins: [
|
|
198
|
-
noopPackagesPlugin, rolldownRuntimeNoopPlugin, lodashSubpathPlugin, dropEthersWordlistsPlugin,
|
|
199
|
-
],
|
|
200
|
-
external: ["cloudflare:*", "__STATIC_CONTENT_MANIFEST"],
|
|
201
|
-
// Give import.meta.url a stable fallback so bundled deps that call
|
|
202
|
-
// createRequire(import.meta.url) at module init don't throw at worker
|
|
203
|
-
// startup. The polyfill in banner handles any legitimate require() lookups.
|
|
204
|
-
define: {
|
|
205
|
-
"import.meta.url": '"file:///worker.js"',
|
|
206
|
-
},
|
|
207
|
-
banner: {
|
|
208
|
-
js: [
|
|
209
|
-
'// Polyfill require() for CJS modules that dynamically require Node builtins',
|
|
210
|
-
'import __nodeCrypto from "node:crypto";',
|
|
211
|
-
'import __nodeBuffer from "node:buffer";',
|
|
212
|
-
'import __nodeEvents from "node:events";',
|
|
213
|
-
'import __nodeStream from "node:stream";',
|
|
214
|
-
'import __nodeUtil from "node:util";',
|
|
215
|
-
'import __nodeAssert from "node:assert";',
|
|
216
|
-
'import __nodePath from "node:path";',
|
|
217
|
-
'import __nodeUrl from "node:url";',
|
|
218
|
-
'import __nodeProcess from "node:process";',
|
|
219
|
-
'const __qsShim = { stringify: function(o) { return new URLSearchParams(o).toString(); }, parse: function(s) { return Object.fromEntries(new URLSearchParams(s).entries()); } };',
|
|
220
|
-
'const __nodeModules = { crypto: __nodeCrypto, buffer: __nodeBuffer, events: __nodeEvents, stream: __nodeStream, util: __nodeUtil, assert: __nodeAssert, path: __nodePath, url: __nodeUrl, process: __nodeProcess, querystring: __qsShim };',
|
|
221
|
-
'// Accept both "xxx" and "node:xxx" specifier forms; return empty stub for unknown modules',
|
|
222
|
-
'// (some deps tree-shake to nothing but still call require() at init — a hard throw crashes the worker).',
|
|
223
|
-
'globalThis.require = globalThis.require || function(m) { var key = typeof m === "string" && m.indexOf("node:") === 0 ? m.slice(5) : m; if (__nodeModules[key]) return __nodeModules[key]; console.warn("[require shim] unknown module: " + m); return {}; };',
|
|
224
|
-
'// Ensure process.stderr.fd exists for debug/supports-color',
|
|
225
|
-
'if (typeof process !== "undefined") { if (!process.stderr) process.stderr = { fd: 2, write: function(){} }; else if (process.stderr.fd === undefined) process.stderr.fd = 2; if (!process.stdout) process.stdout = { fd: 1, write: function(){} }; else if (process.stdout.fd === undefined) process.stdout.fd = 1; }',
|
|
226
|
-
'// Polyfill process.on/process.exit for modules that use them at init',
|
|
227
|
-
'if (typeof process !== "undefined" && !process.on) { process.on = function(){}; process.once = function(){}; process.off = function(){}; process.removeListener = function(){}; process.emit = function(){}; process.exit = function(){}; }',
|
|
228
|
-
'// CF Workers: defer timers called during global scope init until first request',
|
|
229
|
-
'var __deferredTimers = []; var __timersReady = false;',
|
|
230
|
-
'globalThis.__flushDeferredTimers = function() { if (__timersReady) return; __timersReady = true; var q = __deferredTimers; __deferredTimers = []; q.forEach(function(entry) { try { entry.fn.apply(null, entry.args); } catch(e) { console.error("deferred timer error:", e); } }); };',
|
|
231
|
-
'var _origSetTimeout = globalThis.setTimeout; globalThis.setTimeout = function() { if (!__timersReady) { var args = arguments; var fn = args[0]; __deferredTimers.push({fn: function(){ _origSetTimeout.apply(globalThis, args); }, args: []}); return { unref: function(){}, ref: function(){}, [Symbol.toPrimitive]: function(){ return 0; } }; } var id = _origSetTimeout.apply(this, arguments); if (typeof id === "number") { return { unref: function(){}, ref: function(){}, [Symbol.toPrimitive]: function(){ return id; } }; } if (id && !id.unref) id.unref = function(){}; return id; };',
|
|
232
|
-
'var _origSetInterval = globalThis.setInterval; globalThis.setInterval = function() { if (!__timersReady) { var args = arguments; __deferredTimers.push({fn: function(){ _origSetInterval.apply(globalThis, args); }, args: []}); return { unref: function(){}, ref: function(){}, [Symbol.toPrimitive]: function(){ return 0; } }; } var id = _origSetInterval.apply(this, arguments); if (typeof id === "number") { return { unref: function(){}, ref: function(){}, [Symbol.toPrimitive]: function(){ return id; } }; } if (id && !id.unref) id.unref = function(){}; return id; };',
|
|
233
|
-
'var _origSetImmediate = globalThis.setImmediate; if (_origSetImmediate) { globalThis.setImmediate = function() { if (!__timersReady) { var args = arguments; __deferredTimers.push({fn: function(){ _origSetImmediate.apply(globalThis, args); }, args: []}); return { unref: function(){}, ref: function(){} }; } return _origSetImmediate.apply(this, arguments); }; } else { globalThis.setImmediate = function(fn) { if (!__timersReady) { __deferredTimers.push({fn: function(){ _origSetTimeout(fn, 0); }, args: []}); return { unref: function(){}, ref: function(){} }; } return _origSetTimeout(fn, 0); }; }',
|
|
234
|
-
'if (typeof process !== "undefined") { var _origNextTick = process.nextTick; if (_origNextTick) { process.nextTick = function() { if (!__timersReady) { var args = Array.prototype.slice.call(arguments); var fn = args.shift(); __deferredTimers.push({fn: function(){ _origNextTick.apply(process, [fn].concat(args)); }, args: []}); return; } return _origNextTick.apply(process, arguments); }; } else { process.nextTick = function(fn) { if (!__timersReady) { __deferredTimers.push({fn: function(){ _origSetTimeout(fn, 0); }, args: []}); return; } _origSetTimeout(fn, 0); }; } }',
|
|
235
|
-
].join('\n'),
|
|
236
|
-
},
|
|
237
|
-
alias: {
|
|
238
|
-
// axios → lightweight fetch-based shim (115KB → ~2KB)
|
|
239
|
-
"axios": s("shims/axios-lite.ts"),
|
|
240
|
-
|
|
241
|
-
// node-fetch → native fetch (drops encoding/tr46/whatwg-url polyfill ~754KB
|
|
242
|
-
// pulled in by @apple/app-store-server-library; dead weight on CF Workers)
|
|
243
|
-
"node-fetch": s("shims/node-fetch.ts"),
|
|
244
|
-
|
|
245
|
-
// Stripe — wrap constructor to use fetch HTTP client in CF Workers
|
|
246
|
-
"stripe": s("shims/stripe-cf.ts"),
|
|
247
|
-
"__real_stripe__": require.resolve("stripe"),
|
|
248
|
-
|
|
249
|
-
// @ocap/message ships a patch.mjs that monkey-patches google-protobuf at
|
|
250
|
-
// module init. google-protobuf isn't available in CF Workers (no filesystem
|
|
251
|
-
// for createRequire to resolve), and Payment Kit doesn't use the features
|
|
252
|
-
// that patch touches. Replace with a noop so the import chain stays alive
|
|
253
|
-
// but the patch is skipped. Same for the rolldown_runtime helper that
|
|
254
|
-
// relies on createRequire(import.meta.url).
|
|
255
|
-
"@ocap/message/esm/patch.mjs": s("shims/noop.ts"),
|
|
256
|
-
"@ocap/message/esm/_virtual/rolldown_runtime.mjs": s("shims/noop.ts"),
|
|
257
|
-
|
|
258
|
-
"sequelize": s("shims/sequelize-d1/index.ts"),
|
|
259
|
-
"sqlite3": s("shims/noop.ts"),
|
|
260
|
-
"cls-hooked": s("shims/noop.ts"),
|
|
261
|
-
"express-async-errors": s("shims/noop.ts"),
|
|
262
|
-
"cors": s("shims/cors.ts"),
|
|
263
|
-
"cookie-parser": s("shims/cookie-parser.ts"),
|
|
264
|
-
"@blocklet/sdk/lib/middlewares/fallback": s("shims/blocklet-sdk/fallback.ts"),
|
|
265
|
-
"@blocklet/sdk/lib/middlewares/cdn": s("shims/blocklet-sdk/cdn.ts"),
|
|
266
|
-
"@blocklet/sdk/lib/middlewares/session": s("shims/blocklet-sdk/session.ts"),
|
|
267
|
-
"@blocklet/sdk/lib/middlewares": s("shims/blocklet-sdk/middlewares.ts"),
|
|
268
|
-
"@blocklet/sdk/lib/env": s("shims/blocklet-sdk/env.ts"),
|
|
269
|
-
"@blocklet/sdk/lib/config": s("shims/blocklet-sdk/config.ts"),
|
|
270
|
-
"@blocklet/sdk/lib/component": s("shims/blocklet-sdk/component.ts"),
|
|
271
|
-
"@blocklet/sdk/lib/wallet": s("shims/blocklet-sdk/wallet.ts"),
|
|
272
|
-
"@blocklet/sdk/lib/wallet-authenticator": s("shims/blocklet-sdk/wallet-authenticator.ts"),
|
|
273
|
-
"@blocklet/sdk/lib/wallet-handler": s("shims/blocklet-sdk/wallet-handler.ts"),
|
|
274
|
-
"@blocklet/sdk/lib/security": s("shims/blocklet-sdk/security.ts"),
|
|
275
|
-
"@blocklet/sdk/lib/util/verify-sign": s("shims/blocklet-sdk/verify-sign.ts"),
|
|
276
|
-
"@blocklet/sdk/lib/util/verify-session": s("shims/blocklet-sdk/verify-session.ts"),
|
|
277
|
-
"@blocklet/sdk/lib/util/component-api": s("shims/blocklet-sdk/component-api.ts"),
|
|
278
|
-
"@blocklet/sdk/lib/error-handler": s("shims/noop.ts"),
|
|
279
|
-
"@blocklet/sdk/lib/did": s("shims/blocklet-sdk/did.ts"),
|
|
280
|
-
"@blocklet/sdk/lib/types/notification": s("shims/noop.ts"),
|
|
281
|
-
"@blocklet/sdk/service/notification": s("shims/blocklet-sdk/notification.ts"),
|
|
282
|
-
"@blocklet/sdk/service/eventbus": s("shims/blocklet-sdk/eventbus.ts"),
|
|
283
|
-
"@blocklet/sdk/service/auth": s("shims/blocklet-sdk/auth-service.ts"),
|
|
284
|
-
"@blocklet/sdk/service/blocklet": s("shims/blocklet-sdk/auth-service.ts"),
|
|
285
|
-
"@blocklet/sdk": s("shims/blocklet-sdk/index.ts"),
|
|
286
|
-
"@blocklet/xss": s("shims/xss.ts"),
|
|
287
|
-
"@blocklet/error": s("shims/error.ts"),
|
|
288
|
-
"@blocklet/logger": s("shims/blocklet-sdk/logger.ts"),
|
|
289
|
-
"@blocklet/payment-vendor": s("shims/payment-vendor.ts"),
|
|
290
|
-
"@arcblock/did-connect-storage-nedb": s("shims/nedb-storage.ts"),
|
|
291
|
-
"@abtnode/cron": s("shims/cron.ts"),
|
|
292
|
-
"dotenv-flow": s("shims/noop.ts"),
|
|
293
|
-
"fastq": s("shims/fastq.ts"),
|
|
294
|
-
|
|
295
|
-
// Bundle size optimizations — lodash base import aliased to lodash-es for tree-shaking.
|
|
296
|
-
// Sub-path imports (lodash/camelCase etc.) handled by lodashSubpathPlugin below.
|
|
297
|
-
"lodash": "lodash-es", // 357KB CJS → tree-shakeable ESM
|
|
298
|
-
"esprima": s("shims/noop.ts"), // 215KB — only used by @ocap/contract, not called in payment-kit
|
|
299
|
-
"mustache": s("shims/noop.ts"), // 16KB — template engine, not used in payment-kit
|
|
300
|
-
"mime-types": s("shims/mime-types.ts"), // 150KB — lightweight shim with common MIME types
|
|
301
|
-
"mime-db": s("shims/noop.ts"),
|
|
302
|
-
"ws": s("shims/ws-lite.ts"), // 66KB — native WebSocket wrapper for CF Workers
|
|
303
|
-
"crypto-js": s("shims/crypto-js-warn.ts"), // 115KB — AES legacy not used, warns if called
|
|
304
|
-
"crypto-js/aes": s("shims/crypto-js-warn.ts"),
|
|
305
|
-
"crypto-js/enc-base64": s("shims/noop.ts"),
|
|
306
|
-
"crypto-js/enc-hex": s("shims/noop.ts"),
|
|
307
|
-
"crypto-js/enc-latin1": s("shims/noop.ts"),
|
|
308
|
-
"crypto-js/enc-utf8": s("shims/noop.ts"),
|
|
309
|
-
"crypto-js/enc-utf16": s("shims/noop.ts"),
|
|
310
|
-
|
|
311
|
-
// Force ESM-only to avoid CJS+ESM double-bundling
|
|
312
|
-
"valibot": path.resolve(__dirname, "../../..", "node_modules/valibot/dist/index.mjs"), // 396KB → ~200KB
|
|
313
|
-
// CF Workers: noop modules not useful in Workers environment
|
|
314
|
-
"phoenix": s("shims/noop.ts"), // 36KB — Phoenix channels, only used by @arcblock/ws
|
|
315
|
-
"numbro": s("shims/noop.ts"), // 49KB — number formatting, replaced inline in util.ts
|
|
316
|
-
|
|
317
|
-
// Node built-in shims (not fully supported in CF Workers nodejs_compat)
|
|
318
|
-
"os": s("shims/node-os.ts"),
|
|
319
|
-
"node:os": s("shims/node-os.ts"),
|
|
320
|
-
"tty": s("shims/node-tty.ts"),
|
|
321
|
-
"node:tty": s("shims/node-tty.ts"),
|
|
322
|
-
"http": s("shims/node-http.ts"),
|
|
323
|
-
"node:http": s("shims/node-http.ts"),
|
|
324
|
-
"https": s("shims/node-https.ts"),
|
|
325
|
-
"node:https": s("shims/node-https.ts"),
|
|
326
|
-
"http2": s("shims/node-http.ts"),
|
|
327
|
-
"node:http2": s("shims/node-http.ts"),
|
|
328
|
-
"net": s("shims/node-net.ts"),
|
|
329
|
-
"node:net": s("shims/node-net.ts"),
|
|
330
|
-
"tls": s("shims/node-misc.ts"),
|
|
331
|
-
"node:tls": s("shims/node-misc.ts"),
|
|
332
|
-
"fs": s("shims/node-fs.ts"),
|
|
333
|
-
"node:fs": s("shims/node-fs.ts"),
|
|
334
|
-
"cluster": s("shims/node-misc.ts"),
|
|
335
|
-
"node:cluster": s("shims/node-misc.ts"),
|
|
336
|
-
"zlib": s("shims/node-zlib.ts"),
|
|
337
|
-
"node:zlib": s("shims/node-zlib.ts"),
|
|
338
|
-
"child_process": s("shims/node-child-process.ts"),
|
|
339
|
-
"node:child_process": s("shims/node-child-process.ts"),
|
|
340
|
-
"bufferutil": s("shims/noop.ts"),
|
|
341
|
-
"utf-8-validate": s("shims/noop.ts"),
|
|
342
|
-
"dns": s("shims/noop.ts"),
|
|
343
|
-
"node:dns": s("shims/noop.ts"),
|
|
344
|
-
"pg": s("shims/noop.ts"),
|
|
345
|
-
"postgres": s("shims/noop.ts"),
|
|
346
|
-
},
|
|
347
|
-
logLevel: "warning",
|
|
348
|
-
}).then(result => {
|
|
349
|
-
require('fs').writeFileSync(s('dist/meta.json'), JSON.stringify(result.metafile));
|
|
350
|
-
Object.entries(result.metafile.outputs).forEach(function(entry) {
|
|
351
|
-
var k = entry[0], v = entry[1];
|
|
352
|
-
if (k.indexOf(".map") === -1) {
|
|
353
|
-
console.log(k + ": " + (v.bytes / 1024).toFixed(1) + "KB");
|
|
30
|
+
console.log("BUILD SUCCESS");
|
|
31
|
+
})
|
|
32
|
+
.catch((err) => {
|
|
33
|
+
console.error("BUILD FAILED: " + (err.errors ? err.errors.length + " errors" : err.message));
|
|
34
|
+
if (err.errors) {
|
|
35
|
+
err.errors.slice(0, 20).forEach(function (e) {
|
|
36
|
+
var loc = e.location ? " at " + e.location.file + ":" + e.location.line : "";
|
|
37
|
+
console.error(" " + e.text + loc);
|
|
38
|
+
});
|
|
39
|
+
if (err.errors.length > 20) console.error(" ... and " + (err.errors.length - 20) + " more");
|
|
354
40
|
}
|
|
41
|
+
process.exitCode = 1;
|
|
355
42
|
});
|
|
356
|
-
console.log("BUILD SUCCESS");
|
|
357
|
-
}).catch(err => {
|
|
358
|
-
console.error("BUILD FAILED: " + (err.errors ? err.errors.length + " errors" : err.message));
|
|
359
|
-
if (err.errors) {
|
|
360
|
-
err.errors.slice(0, 20).forEach(function(e) {
|
|
361
|
-
var loc = e.location ? " at " + e.location.file + ":" + e.location.line : "";
|
|
362
|
-
console.error(" " + e.text + loc);
|
|
363
|
-
});
|
|
364
|
-
if (err.errors.length > 20) console.error(" ... and " + (err.errors.length - 20) + " more");
|
|
365
|
-
}
|
|
366
|
-
});
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// S3-CF Phase 2 命门 acceptance gate — the tiny-worker dynamic import probe.
|
|
2
|
+
//
|
|
3
|
+
// Builds a throwaway Worker whose ONLY payment touchpoint is
|
|
4
|
+
// import { createCloudflarePaymentAdapter } from "@arcblock/payment-service/cf";
|
|
5
|
+
// and runs `wrangler deploy --dry-run` with a CLEAN wrangler config: NO alias, NO
|
|
6
|
+
// define, NO external rules of its own — only `nodejs_compat`. If the published
|
|
7
|
+
// `./cf` artifact is genuinely self-contained + re-bundleable, this succeeds. If
|
|
8
|
+
// the consumer would need ANY alias/define/external, wrangler reports an unresolved
|
|
9
|
+
// import and the probe fails (命门 RED → architecture §3 service-binding fallback).
|
|
10
|
+
//
|
|
11
|
+
// node blocklets/core/cloudflare/scripts/cf-package-import-probe.mjs
|
|
12
|
+
//
|
|
13
|
+
// Reproducible: a fixed temp dir, the local payment-core symlinked as
|
|
14
|
+
// @arcblock/payment-service so the package `exports` map ("./cf" → dist/cf.js) is
|
|
15
|
+
// what resolves — exactly what arc sees from the published tarball.
|
|
16
|
+
|
|
17
|
+
import { mkdirSync, writeFileSync, rmSync, symlinkSync, existsSync } from 'fs';
|
|
18
|
+
import { dirname, join, resolve } from 'path';
|
|
19
|
+
import { fileURLToPath } from 'url';
|
|
20
|
+
import { execFileSync } from 'child_process';
|
|
21
|
+
import { tmpdir } from 'os';
|
|
22
|
+
|
|
23
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
24
|
+
const paymentCoreDir = resolve(here, '../../../../packages/payment-core');
|
|
25
|
+
const cfDist = join(paymentCoreDir, 'dist', 'cf.js');
|
|
26
|
+
|
|
27
|
+
if (!existsSync(cfDist)) {
|
|
28
|
+
console.error(`probe RED — ${cfDist} missing. Run \`node packages/payment-core/build-cf.mjs\` first.`);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const probeDir = join(tmpdir(), 'cf-import-probe');
|
|
33
|
+
rmSync(probeDir, { recursive: true, force: true });
|
|
34
|
+
mkdirSync(join(probeDir, 'src'), { recursive: true });
|
|
35
|
+
mkdirSync(join(probeDir, 'node_modules', '@arcblock'), { recursive: true });
|
|
36
|
+
|
|
37
|
+
// Symlink the local payment-core as @arcblock/payment-service so the import
|
|
38
|
+
// resolves through the real package `exports` map — NOT a bypass path.
|
|
39
|
+
symlinkSync(paymentCoreDir, join(probeDir, 'node_modules', '@arcblock', 'payment-service'), 'dir');
|
|
40
|
+
|
|
41
|
+
// The tiny worker. Reference the imported symbol so esbuild can't tree-shake the
|
|
42
|
+
// import away (the whole point is to force the ./cf graph into the bundle).
|
|
43
|
+
writeFileSync(
|
|
44
|
+
join(probeDir, 'src', 'index.ts'),
|
|
45
|
+
`import { createCloudflarePaymentAdapter, PAYMENT_PREFIX } from "@arcblock/payment-service/cf";
|
|
46
|
+
export default {
|
|
47
|
+
async fetch() {
|
|
48
|
+
// touch the import so it is retained; never actually called in --dry-run
|
|
49
|
+
if (typeof createCloudflarePaymentAdapter !== "function") throw new Error("not a function");
|
|
50
|
+
return new Response("ok:" + PAYMENT_PREFIX);
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
`,
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
// CLEAN wrangler config — the load-bearing part of the gate: zero alias / zero
|
|
57
|
+
// define / zero external rules. Only nodejs_compat (a platform flag every Workers
|
|
58
|
+
// host sets), the same the standalone worker uses.
|
|
59
|
+
writeFileSync(
|
|
60
|
+
join(probeDir, 'wrangler.jsonc'),
|
|
61
|
+
JSON.stringify(
|
|
62
|
+
{
|
|
63
|
+
name: 'cf-import-probe',
|
|
64
|
+
main: 'src/index.ts',
|
|
65
|
+
compatibility_date: '2026-01-01',
|
|
66
|
+
compatibility_flags: ['nodejs_compat'],
|
|
67
|
+
},
|
|
68
|
+
null,
|
|
69
|
+
2,
|
|
70
|
+
),
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
writeFileSync(join(probeDir, 'package.json'), JSON.stringify({ name: 'cf-import-probe', private: true }, null, 2));
|
|
74
|
+
|
|
75
|
+
const outDir = join(probeDir, 'out');
|
|
76
|
+
console.log(`[probe] wrangler deploy --dry-run in ${probeDir} (clean config: nodejs_compat only)`);
|
|
77
|
+
try {
|
|
78
|
+
const stdout = execFileSync(
|
|
79
|
+
'npx',
|
|
80
|
+
['wrangler', 'deploy', '--dry-run', '--outdir', outDir],
|
|
81
|
+
{ cwd: probeDir, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] },
|
|
82
|
+
);
|
|
83
|
+
console.log(stdout);
|
|
84
|
+
console.log('\n命门 GREEN — tiny worker built + dry-run deployed with ZERO consumer-side alias/define/external ✓');
|
|
85
|
+
} catch (err) {
|
|
86
|
+
console.error('\n命门 RED — tiny worker dry-run FAILED. Consumer would need alias/define/external:');
|
|
87
|
+
console.error(err.stdout || '');
|
|
88
|
+
console.error(err.stderr || err.message);
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* S3-CF (DID convergence) — local mock-AUTH_SERVICE smoke (TEST HARNESS ONLY).
|
|
4
|
+
*
|
|
5
|
+
* Runs the REAL built worker (dist/worker.js) in Miniflare with a MOCK AUTH_SERVICE
|
|
6
|
+
* that implements the production RPC contract (getInstanceAppIdentity) returning a
|
|
7
|
+
* deterministic TEST-ONLY appSk — no real secret, never in prod/wrangler config.
|
|
8
|
+
*
|
|
9
|
+
* The smoke goes through the REAL path (no handler/authenticator bypass):
|
|
10
|
+
* Host → tenant context → CF IdentityDriver → AUTH_SERVICE.getInstanceAppIdentity
|
|
11
|
+
* → resolveTenantIdentity → real @arcblock/did-connect-js authenticator
|
|
12
|
+
* → core buildConnectRoutesHono → tokenStorage.create (CF D1).
|
|
13
|
+
*
|
|
14
|
+
* Asserts the locally-verifiable integration facts:
|
|
15
|
+
* - /api/did/<action>/token is registered (not 404) and, WITH the mock, no longer
|
|
16
|
+
* fail-closes on getInstanceAppIdentity (the mock RPC is reached + appSk used);
|
|
17
|
+
* - a token row lands in the CF D1 _did_connect_tokens table, stamped with the
|
|
18
|
+
* tenant instanceDid.
|
|
19
|
+
* (The full wallet scan/auth handshake against a REAL AUTH_SERVICE stays a
|
|
20
|
+
* deploy/staging gate — see cloudflare/README.md.)
|
|
21
|
+
*
|
|
22
|
+
* node scripts/didconnect-mock-smoke.mjs
|
|
23
|
+
*/
|
|
24
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
25
|
+
import { dirname, join } from "node:path";
|
|
26
|
+
import { fileURLToPath } from "node:url";
|
|
27
|
+
|
|
28
|
+
import { Miniflare } from "miniflare";
|
|
29
|
+
|
|
30
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
31
|
+
const cfDir = join(__dirname, "..");
|
|
32
|
+
const workerPath = join(cfDir, "dist", "worker.js");
|
|
33
|
+
|
|
34
|
+
if (!existsSync(workerPath)) {
|
|
35
|
+
console.error(`mock-smoke: ${workerPath} not found — run \`node run-build.js\` first.`);
|
|
36
|
+
process.exit(2);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Deterministic TEST-ONLY app signing key (ROLE_APPLICATION/ED25519/SHA3). NOT a
|
|
40
|
+
// real secret — generated once for the harness; isolation uses a second key.
|
|
41
|
+
const TEST_APP_SK =
|
|
42
|
+
"0xe86ad1043f2de80374ea9eb2ca5a0cdf0111126804cfb128e9e658cc44ae47694497dddaacf925929a19e8bc84a5059569983f307a4c67a694cca79c2001e634";
|
|
43
|
+
const INSTANCE_DID = "zMOCK_APP_INSTANCE";
|
|
44
|
+
|
|
45
|
+
const mockAuthScript = `
|
|
46
|
+
import { WorkerEntrypoint } from 'cloudflare:workers';
|
|
47
|
+
// Mock of the did-connect-service@4.0.3 AUTH_SERVICE RPC surface used on the DID path.
|
|
48
|
+
export class MockAuth extends WorkerEntrypoint {
|
|
49
|
+
async getInstanceAppIdentity(instanceDid) {
|
|
50
|
+
return { appSk: ${JSON.stringify(TEST_APP_SK)}, appInfo: { name: 'Mock Tenant', description: 'mock', icon: 'https://x/i.png' } };
|
|
51
|
+
}
|
|
52
|
+
async resolveInstanceDidForHost(host) { return null; }
|
|
53
|
+
async getAppEk(instanceDid) { return null; }
|
|
54
|
+
async resolveIdentity() { return null; }
|
|
55
|
+
}
|
|
56
|
+
export default { fetch() { return new Response('mock-auth'); } };
|
|
57
|
+
`;
|
|
58
|
+
|
|
59
|
+
const mf = new Miniflare({
|
|
60
|
+
workers: [
|
|
61
|
+
{
|
|
62
|
+
name: "payment",
|
|
63
|
+
modules: true,
|
|
64
|
+
modulesRoot: join(cfDir, "dist"),
|
|
65
|
+
scriptPath: workerPath,
|
|
66
|
+
compatibilityDate: "2024-12-01",
|
|
67
|
+
compatibilityFlags: ["nodejs_compat"],
|
|
68
|
+
d1Databases: { DB: "payment-mock-smoke" },
|
|
69
|
+
serviceBindings: { AUTH_SERVICE: { name: "mock-auth", entrypoint: "MockAuth" } },
|
|
70
|
+
bindings: {
|
|
71
|
+
APP_NAME: "Payment Kit",
|
|
72
|
+
APP_PID: INSTANCE_DID,
|
|
73
|
+
APP_URL: "http://localhost",
|
|
74
|
+
PAYMENT_LIVEMODE: "false",
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: "mock-auth",
|
|
79
|
+
modules: true,
|
|
80
|
+
script: mockAuthScript,
|
|
81
|
+
compatibilityDate: "2024-12-01",
|
|
82
|
+
compatibilityFlags: ["nodejs_compat"],
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
let failed = false;
|
|
88
|
+
const log = (...a) => console.log(...a);
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
const db = await mf.getD1Database("DB", "payment");
|
|
92
|
+
await db.exec(
|
|
93
|
+
"CREATE TABLE IF NOT EXISTS _did_connect_tokens (token TEXT PRIMARY KEY, data TEXT NOT NULL, expires_at INTEGER NOT NULL)",
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
// 1) healthz
|
|
97
|
+
const health = await mf.dispatchFetch("http://localhost/api/healthz");
|
|
98
|
+
log("=== healthz ===", health.status, await health.text());
|
|
99
|
+
|
|
100
|
+
// 2) /api/did/payment/token through the mock AUTH_SERVICE
|
|
101
|
+
const res = await mf.dispatchFetch("http://localhost/api/did/payment/token", {
|
|
102
|
+
method: "POST",
|
|
103
|
+
headers: { "content-type": "application/json" },
|
|
104
|
+
body: "{}",
|
|
105
|
+
});
|
|
106
|
+
const body = await res.text();
|
|
107
|
+
log("=== POST /api/did/payment/token ===", res.status);
|
|
108
|
+
log(body.slice(0, 800));
|
|
109
|
+
|
|
110
|
+
if (body.includes("getInstanceAppIdentity unavailable")) {
|
|
111
|
+
console.error("FAIL: still fail-closed — the mock AUTH_SERVICE RPC was not reached");
|
|
112
|
+
failed = true;
|
|
113
|
+
} else {
|
|
114
|
+
log("PASS: past the fail-closed gate — mock getInstanceAppIdentity reached + appSk resolved");
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 3) a token row landed in D1, stamped with the tenant instanceDid
|
|
118
|
+
const { results } = await db.prepare("SELECT token, data FROM _did_connect_tokens").all();
|
|
119
|
+
log("=== _did_connect_tokens rows ===", JSON.stringify(results, null, 2));
|
|
120
|
+
const tagged = (results || []).some((r) => {
|
|
121
|
+
try {
|
|
122
|
+
return JSON.parse(r.data).instanceDid === INSTANCE_DID;
|
|
123
|
+
} catch {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
if (tagged) {
|
|
128
|
+
log(`PASS: a token row is stamped with instanceDid=${INSTANCE_DID}`);
|
|
129
|
+
} else {
|
|
130
|
+
console.error("FAIL: no token row stamped with the tenant instanceDid");
|
|
131
|
+
failed = true;
|
|
132
|
+
}
|
|
133
|
+
} catch (err) {
|
|
134
|
+
console.error("mock-smoke error:", err?.stack || err?.message || err);
|
|
135
|
+
failed = true;
|
|
136
|
+
} finally {
|
|
137
|
+
await mf.dispose();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
process.exit(failed ? 1 : 0);
|
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
// CF FAIL-FAST shim for @blocklet/sdk/lib/wallet-authenticator.
|
|
2
|
+
//
|
|
3
|
+
// S3-CF (DID convergence): see wallet-handler.ts. On workerd the @blocklet/sdk
|
|
4
|
+
// WalletAuthenticator is a no-op — it cannot sign DID-Connect sessions/certs. CF
|
|
5
|
+
// (and arc-node embedded) MUST inject the real @arcblock/did-connect-js runtime.
|
|
6
|
+
// Throw on construct so a host that forgot to inject fails loudly instead of
|
|
7
|
+
// producing an authenticator that silently signs nothing.
|
|
8
|
+
const FORBIDDEN =
|
|
9
|
+
'CF must inject a DID-Connect runtime via setDidConnectRuntime(createCloudflareDidConnectRuntime); ' +
|
|
10
|
+
'the @blocklet/sdk wallet-authenticator shim is forbidden on workerd';
|
|
11
|
+
|
|
1
12
|
export class WalletAuthenticator {
|
|
2
|
-
constructor(_opts?: any) {
|
|
13
|
+
constructor(_opts?: any) {
|
|
14
|
+
throw new Error(`[did-connect] WalletAuthenticator: ${FORBIDDEN}`);
|
|
15
|
+
}
|
|
3
16
|
}
|
|
17
|
+
|
|
18
|
+
export default { WalletAuthenticator };
|