@402flow/sdk 0.1.0-alpha.23 → 0.1.0-alpha.24
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/README.md +130 -3
- package/dist/contracts.d.ts +1167 -130
- package/dist/contracts.js +67 -0
- package/dist/contracts.js.map +1 -1
- package/dist/executors.d.ts +27 -0
- package/dist/executors.js +2 -0
- package/dist/executors.js.map +1 -0
- package/dist/harness-instructions.d.ts +8 -0
- package/dist/harness-instructions.js.map +1 -1
- package/dist/http-utils.d.ts +1 -0
- package/dist/http-utils.js +12 -0
- package/dist/http-utils.js.map +1 -0
- package/dist/index.d.ts +14 -3
- package/dist/index.js +119 -28
- package/dist/index.js.map +1 -1
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -324,6 +324,131 @@ if (prepared.kind === 'ready') {
|
|
|
324
324
|
|
|
325
325
|
If preparation does not return `kind === 'ready'`, that is not necessarily an error. It means this exact request did not currently resolve to a payable executable path. The caller can accept that result, run a normal non-paid path, or revise and prepare again.
|
|
326
326
|
|
|
327
|
+
### Delegated Execution With Custom Executors
|
|
328
|
+
|
|
329
|
+
`executePreparedRequest()` now supports governed delegated execution through a caller-supplied executor interface.
|
|
330
|
+
|
|
331
|
+
This lets the SDK keep authorization, policy, receipts, and final outcome normalization in the 402flow control plane while handing the final paid merchant call to a provider-specific executor owned by the host app or a separate integration package.
|
|
332
|
+
|
|
333
|
+
The core SDK stays provider-neutral. That means third-party payment clients such as Dexter or pay.sh should live in the host app's own dependency graph, not inside `@402flow/sdk` itself.
|
|
334
|
+
|
|
335
|
+
The flow is:
|
|
336
|
+
|
|
337
|
+
1. prepare the exact request locally with `preparePaidRequest()`
|
|
338
|
+
2. ask the control plane to authorize delegated execution
|
|
339
|
+
3. if authorized, let the supplied executor perform the actual paid merchant request
|
|
340
|
+
4. finalize the normalized executor result back through the control plane
|
|
341
|
+
5. return the same stable SDK result or `FetchPaidError` contract the caller already uses elsewhere
|
|
342
|
+
|
|
343
|
+
If the control plane denies authorization, the delegated executor is never invoked.
|
|
344
|
+
|
|
345
|
+
```ts
|
|
346
|
+
import {
|
|
347
|
+
createJsonRequestBody,
|
|
348
|
+
type PreparedRequestExecutorInput,
|
|
349
|
+
type PreparedRequestExecutor,
|
|
350
|
+
} from '@402flow/sdk';
|
|
351
|
+
|
|
352
|
+
async function callDexter(
|
|
353
|
+
prepared: PreparedRequestExecutorInput['prepared'],
|
|
354
|
+
) {
|
|
355
|
+
// Replace this with your real Dexter SDK call.
|
|
356
|
+
return {
|
|
357
|
+
status: 200,
|
|
358
|
+
body: { ok: true },
|
|
359
|
+
settlementReference: 'settlement-ref-1',
|
|
360
|
+
paymentReference: 'payment-ref-1',
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function mapDexterResult(
|
|
365
|
+
prepared: PreparedRequestExecutorInput['prepared'],
|
|
366
|
+
result: Awaited<ReturnType<typeof callDexter>>,
|
|
367
|
+
) {
|
|
368
|
+
return {
|
|
369
|
+
protocol: prepared.protocol,
|
|
370
|
+
executionStatus: 'succeeded' as const,
|
|
371
|
+
settlementEvidenceClass: 'settled' as const,
|
|
372
|
+
merchantOutcome: 'success_response' as const,
|
|
373
|
+
merchantResponse: {
|
|
374
|
+
status: result.status,
|
|
375
|
+
headers: {
|
|
376
|
+
'content-type': 'application/json',
|
|
377
|
+
},
|
|
378
|
+
body: JSON.stringify(result.body),
|
|
379
|
+
},
|
|
380
|
+
settlementReference: result.settlementReference,
|
|
381
|
+
paymentReference: result.paymentReference,
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const dexterExecutor: PreparedRequestExecutor = {
|
|
386
|
+
provider: 'dexter',
|
|
387
|
+
async execute({ prepared }) {
|
|
388
|
+
const dexterResult = await callDexter(prepared);
|
|
389
|
+
return mapDexterResult(prepared, dexterResult);
|
|
390
|
+
},
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
const prepared = await client.preparePaidRequest(
|
|
394
|
+
'https://merchant.example.com/images/generate',
|
|
395
|
+
{
|
|
396
|
+
method: 'POST',
|
|
397
|
+
headers: {
|
|
398
|
+
'content-type': 'application/json',
|
|
399
|
+
},
|
|
400
|
+
body: createJsonRequestBody({
|
|
401
|
+
prompt: 'foggy coastline',
|
|
402
|
+
}),
|
|
403
|
+
},
|
|
404
|
+
);
|
|
405
|
+
|
|
406
|
+
if (prepared.kind === 'ready') {
|
|
407
|
+
const result = await client.executePreparedRequest(prepared, {
|
|
408
|
+
description: 'generate image through delegated execution',
|
|
409
|
+
executionProvider: 'dexter',
|
|
410
|
+
executor: dexterExecutor,
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
console.log('paid response status:', result.response.status);
|
|
414
|
+
}
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
This snippet shows the intended split directly: your host-owned code does the provider call and maps it into the delegated execution contract, while the SDK still owns authorize, finalize, and the outward result shape. For a real host-owned Dexter integration that performs the paid call, see `third-party-executors/examples/dexter-delegated-executor.mjs`.
|
|
418
|
+
|
|
419
|
+
Responsibility split:
|
|
420
|
+
|
|
421
|
+
1. the SDK asks the control plane for delegated authorization
|
|
422
|
+
2. if authorized, the SDK invokes your executor
|
|
423
|
+
3. your executor performs the provider-specific paid request and returns `SdkDelegatedExecutionResult`
|
|
424
|
+
4. the SDK finalizes that result with the control plane
|
|
425
|
+
5. the SDK returns the same outward `PaidResponse` or `FetchPaidError` contract as the direct path
|
|
426
|
+
|
|
427
|
+
If your host app wants to execute through Dexter, pay.sh, or another provider, that integration should install and own the third-party SDK directly. `@402flow/sdk` only owns the executor contract and the governed authorize/finalize flow.
|
|
428
|
+
|
|
429
|
+
The repo keeps third-party executor proofs in the separate `third-party-executors/` package so the main `@402flow/sdk` install path stays provider-neutral.
|
|
430
|
+
|
|
431
|
+
For a repo-wide verification pass from the SDK root, run:
|
|
432
|
+
|
|
433
|
+
```bash
|
|
434
|
+
npm run install:all
|
|
435
|
+
npm run check:all
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
`npm run install:all` installs both the main SDK package and the separate `third-party-executors` package from the SDK root. `npm run check` still validates only the main SDK package. `npm run check:all` runs the main SDK checks first, then runs the separate `third-party-executors` package checks from the top level.
|
|
439
|
+
|
|
440
|
+
For a repo-local host-owned Dexter example, run:
|
|
441
|
+
|
|
442
|
+
```bash
|
|
443
|
+
cd third-party-executors
|
|
444
|
+
npm install
|
|
445
|
+
export DEXTER_EVM_PRIVATE_KEY="..."
|
|
446
|
+
|
|
447
|
+
npm run example:dexter-delegated-executor -- \
|
|
448
|
+
"https://merchant.example.com/paid-endpoint" \
|
|
449
|
+
'{"topic":"sdk integration rollout","audience":"platform engineers","format":"bullets"}'
|
|
450
|
+
```
|
|
451
|
+
|
|
327
452
|
## Prepared Result Semantics
|
|
328
453
|
|
|
329
454
|
`preparePaidRequest()` separates request checking from paid execution.
|
|
@@ -477,8 +602,10 @@ When unset, first-party fixtures default to the self-hosted demo merchant at `ht
|
|
|
477
602
|
## Publish
|
|
478
603
|
|
|
479
604
|
```bash
|
|
480
|
-
npm install
|
|
481
|
-
npm run check
|
|
605
|
+
npm run install:all
|
|
606
|
+
npm run check:all
|
|
482
607
|
npm run pack:check
|
|
483
608
|
npm publish --access public
|
|
484
|
-
```
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
`npm publish` now also runs `npm run check:all` through the root `prepublishOnly` hook, so the repo-wide test pass is enforced before publish even if you skip that step manually.
|