swiftproof 0.1.0 → 0.1.1
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/package.json +27 -4
- package/src/index.js +0 -130
- package/src/index.ts +0 -471
- package/src/types.js +0 -2
- package/src/types.ts +0 -101
- package/tsconfig.json +0 -19
- package/tsup.config.js +0 -12
- package/tsup.config.ts +0 -11
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "swiftproof",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "SwiftProof chain of custody SDK — plug and play proof infrastructure",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "SwiftProof chain of custody SDK — plug and play proof infrastructure for any industry where something needs to be verified.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
6
7
|
"types": "./dist/index.d.ts",
|
|
7
8
|
"exports": {
|
|
8
9
|
".": {
|
|
@@ -11,6 +12,9 @@
|
|
|
11
12
|
"require": "./dist/index.js"
|
|
12
13
|
}
|
|
13
14
|
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
14
18
|
"scripts": {
|
|
15
19
|
"build": "tsup",
|
|
16
20
|
"dev": "tsup --watch"
|
|
@@ -20,7 +24,26 @@
|
|
|
20
24
|
"proof",
|
|
21
25
|
"logistics",
|
|
22
26
|
"delivery",
|
|
23
|
-
"audit-trail"
|
|
27
|
+
"audit-trail",
|
|
28
|
+
"swiftproof",
|
|
29
|
+
"handoff",
|
|
30
|
+
"gps",
|
|
31
|
+
"tamper-proof",
|
|
32
|
+
"verification",
|
|
33
|
+
"fintech",
|
|
34
|
+
"pharmacy",
|
|
35
|
+
"field-agent"
|
|
24
36
|
],
|
|
25
|
-
"license": "MIT"
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "https://github.com/jorbit22/swiftproof-infra"
|
|
41
|
+
},
|
|
42
|
+
"homepage": "https://tryswiftproof.com/getting-started",
|
|
43
|
+
"bugs": {
|
|
44
|
+
"email": "hello@tryswiftproof.com"
|
|
45
|
+
},
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=18.0.0"
|
|
48
|
+
}
|
|
26
49
|
}
|
package/src/index.js
DELETED
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
-
};
|
|
16
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
17
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
18
|
-
};
|
|
19
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
-
exports.SwiftProof = void 0;
|
|
21
|
-
const axios_1 = __importDefault(require("axios"));
|
|
22
|
-
__exportStar(require("./types"), exports);
|
|
23
|
-
class SwiftProof {
|
|
24
|
-
constructor(config) {
|
|
25
|
-
var _a;
|
|
26
|
-
// ── Chains ────────────────────────────────────────────────
|
|
27
|
-
this.chains = {
|
|
28
|
-
/**
|
|
29
|
-
* Create a new chain of custody
|
|
30
|
-
*
|
|
31
|
-
* @example
|
|
32
|
-
* const chain = await sp.chains.create({
|
|
33
|
-
* title: 'Order #1234',
|
|
34
|
-
* actors: [
|
|
35
|
-
* { event: 'packed', actor: 'Kitchen' },
|
|
36
|
-
* { event: 'pickup', actor: 'Rider' },
|
|
37
|
-
* { event: 'delivered', actor: 'Rider' },
|
|
38
|
-
* ]
|
|
39
|
-
* });
|
|
40
|
-
*/
|
|
41
|
-
create: async (params) => {
|
|
42
|
-
const res = await this.client.post('/v1/chains', params);
|
|
43
|
-
return res.data;
|
|
44
|
-
},
|
|
45
|
-
/**
|
|
46
|
-
* Get full audit trail for a chain
|
|
47
|
-
*
|
|
48
|
-
* @example
|
|
49
|
-
* const chain = await sp.chains.get('sp_ch_abc123');
|
|
50
|
-
*/
|
|
51
|
-
get: async (chainId) => {
|
|
52
|
-
const res = await this.client.get(`/v1/chains/${chainId}`);
|
|
53
|
-
return res.data;
|
|
54
|
-
},
|
|
55
|
-
/**
|
|
56
|
-
* Verify chain integrity — use for dispute resolution
|
|
57
|
-
*
|
|
58
|
-
* @example
|
|
59
|
-
* const result = await sp.chains.verify('sp_ch_abc123');
|
|
60
|
-
* if (result.verified) {
|
|
61
|
-
* // delivery is proven — no dispute possible
|
|
62
|
-
* }
|
|
63
|
-
*/
|
|
64
|
-
verify: async (chainId) => {
|
|
65
|
-
const res = await this.client.get(`/v1/chains/${chainId}/verify`);
|
|
66
|
-
return res.data;
|
|
67
|
-
},
|
|
68
|
-
};
|
|
69
|
-
// ── Handoffs ──────────────────────────────────────────────
|
|
70
|
-
this.handoffs = {
|
|
71
|
-
/**
|
|
72
|
-
* Confirm a handoff using an actor token
|
|
73
|
-
* This is called when kitchen, rider, or any actor taps their link
|
|
74
|
-
*
|
|
75
|
-
* @example
|
|
76
|
-
* await sp.handoffs.confirm('actor_token_here', {
|
|
77
|
-
* lat: 6.5244,
|
|
78
|
-
* lng: 3.3792,
|
|
79
|
-
* note: 'Packed and sealed'
|
|
80
|
-
* });
|
|
81
|
-
*/
|
|
82
|
-
confirm: async (actorToken, params) => {
|
|
83
|
-
const res = await this.client.post(`/confirm/${actorToken}`, params !== null && params !== void 0 ? params : {});
|
|
84
|
-
return res.data;
|
|
85
|
-
},
|
|
86
|
-
};
|
|
87
|
-
// ── Tracking ──────────────────────────────────────────────
|
|
88
|
-
this.tracking = {
|
|
89
|
-
/**
|
|
90
|
-
* Get public tracking data — no auth required
|
|
91
|
-
* Use this to show customers where their order is
|
|
92
|
-
*
|
|
93
|
-
* @example
|
|
94
|
-
* const tracking = await sp.tracking.get('tracking_token_here');
|
|
95
|
-
* console.log(tracking.current_location);
|
|
96
|
-
*/
|
|
97
|
-
get: async (trackingToken) => {
|
|
98
|
-
// Tracking is public — use plain axios not authenticated client
|
|
99
|
-
const res = await axios_1.default.get(`${this.baseUrl}/track/${trackingToken}`);
|
|
100
|
-
return res.data;
|
|
101
|
-
},
|
|
102
|
-
};
|
|
103
|
-
if (!config.apiKey)
|
|
104
|
-
throw new Error('SwiftProof: apiKey is required');
|
|
105
|
-
if (!config.apiKey.startsWith('sp_')) {
|
|
106
|
-
throw new Error('SwiftProof: invalid apiKey format. Must start with sp_');
|
|
107
|
-
}
|
|
108
|
-
this.baseUrl = (_a = config.baseUrl) !== null && _a !== void 0 ? _a : 'https://api.swiftproof.io';
|
|
109
|
-
this.client = axios_1.default.create({
|
|
110
|
-
baseURL: this.baseUrl,
|
|
111
|
-
headers: {
|
|
112
|
-
Authorization: `Bearer ${config.apiKey}`,
|
|
113
|
-
'Content-Type': 'application/json',
|
|
114
|
-
'User-Agent': 'swiftproof-js/0.1.0',
|
|
115
|
-
},
|
|
116
|
-
timeout: 30000,
|
|
117
|
-
});
|
|
118
|
-
// Response interceptor — clean error messages
|
|
119
|
-
this.client.interceptors.response.use((res) => res, (err) => {
|
|
120
|
-
var _a, _b, _c, _d, _e;
|
|
121
|
-
const message = (_d = (_c = (_b = (_a = err === null || err === void 0 ? void 0 : err.response) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.message) !== null && _c !== void 0 ? _c : err === null || err === void 0 ? void 0 : err.message) !== null && _d !== void 0 ? _d : 'SwiftProof request failed';
|
|
122
|
-
const status = (_e = err === null || err === void 0 ? void 0 : err.response) === null || _e === void 0 ? void 0 : _e.status;
|
|
123
|
-
const error = new Error(`SwiftProof [${status}]: ${message}`);
|
|
124
|
-
return Promise.reject(error);
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
exports.SwiftProof = SwiftProof;
|
|
129
|
-
// Default export
|
|
130
|
-
exports.default = SwiftProof;
|
package/src/index.ts
DELETED
|
@@ -1,471 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SwiftProof JavaScript/TypeScript SDK
|
|
3
|
-
*
|
|
4
|
-
* Proof-of-work infrastructure for any industry where
|
|
5
|
-
* something needs to be verified.
|
|
6
|
-
*
|
|
7
|
-
* npm install swiftproof
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
11
|
-
// PROOF EVENT — the core object every chain produces
|
|
12
|
-
// Every confirmation in every industry maps to this same shape
|
|
13
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* A single verified proof event — one step in a chain of custody.
|
|
17
|
-
*
|
|
18
|
-
* Every proof event answers five universal questions:
|
|
19
|
-
* → who did it = actor
|
|
20
|
-
* → what happened = event
|
|
21
|
-
* → when it happened = confirmed_at
|
|
22
|
-
* → where it happened = location + lat + lng
|
|
23
|
-
* → evidence = photo_url
|
|
24
|
-
*
|
|
25
|
-
* The hash field makes it tamper-proof:
|
|
26
|
-
* each hash includes the previous one, so altering any
|
|
27
|
-
* record breaks the entire chain and is caught by verify().
|
|
28
|
-
*/
|
|
29
|
-
export interface ProofEvent {
|
|
30
|
-
/** What happened — e.g. "delivered", "inspected", "dispensed", "signed" */
|
|
31
|
-
event: string;
|
|
32
|
-
|
|
33
|
-
/** Who did it — e.g. "Rider — Emeka", "Pharmacist", "Inspector" */
|
|
34
|
-
actor: string;
|
|
35
|
-
|
|
36
|
-
/** Whether this step has been confirmed yet */
|
|
37
|
-
confirmed: boolean;
|
|
38
|
-
|
|
39
|
-
/** Exact ISO timestamp of when the actor tapped confirm — null if not yet confirmed */
|
|
40
|
-
confirmed_at: string | null;
|
|
41
|
-
|
|
42
|
-
/** Human-readable location name (reverse geocoded) — e.g. "Allen Avenue, Ikeja" */
|
|
43
|
-
location: string | null;
|
|
44
|
-
|
|
45
|
-
/** GPS latitude at moment of confirmation */
|
|
46
|
-
lat: number | null;
|
|
47
|
-
|
|
48
|
-
/** GPS longitude at moment of confirmation */
|
|
49
|
-
lng: number | null;
|
|
50
|
-
|
|
51
|
-
/** Photo evidence URL (Cloudinary) — attached by actor at confirmation */
|
|
52
|
-
photo_url: string | null;
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* SHA-256 hash of this event chained to the previous one.
|
|
56
|
-
* If this hash doesn't match when verify() recomputes it,
|
|
57
|
-
* the chain is flagged as tampered.
|
|
58
|
-
*/
|
|
59
|
-
hash: string | null;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
63
|
-
// HANDOFF — a pending proof event (before confirmation)
|
|
64
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* A pending step in a chain — waiting for the actor to confirm.
|
|
68
|
-
* Returned when you create a chain, before any confirmation happens.
|
|
69
|
-
*/
|
|
70
|
-
export interface PendingHandoff {
|
|
71
|
-
/** Unique handoff ID */
|
|
72
|
-
id: string;
|
|
73
|
-
|
|
74
|
-
/** What this step is — e.g. "pickup", "delivered" */
|
|
75
|
-
event: string;
|
|
76
|
-
|
|
77
|
-
/** Who needs to confirm this step */
|
|
78
|
-
actor: string;
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* The URL to send to the actor.
|
|
82
|
-
* When they open this link on any phone browser and tap confirm,
|
|
83
|
-
* their GPS is captured automatically and the proof event is recorded.
|
|
84
|
-
* No app download. No account. Just tap.
|
|
85
|
-
*/
|
|
86
|
-
confirmation_url: string;
|
|
87
|
-
|
|
88
|
-
/** Raw token embedded in confirmation_url — for reference only */
|
|
89
|
-
confirmation_token: string;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
93
|
-
// CHAIN — a complete chain of custody
|
|
94
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* A chain of custody — ties multiple proof events together
|
|
98
|
-
* under one verifiable record.
|
|
99
|
-
*
|
|
100
|
-
* One chain per real-world event:
|
|
101
|
-
* - one food delivery
|
|
102
|
-
* - one pharmaceutical dispensing
|
|
103
|
-
* - one field agent visit
|
|
104
|
-
* - one court document service
|
|
105
|
-
* - one construction milestone
|
|
106
|
-
*/
|
|
107
|
-
export interface Chain {
|
|
108
|
-
/** Unique chain ID — reference this when calling getChain() or verify() */
|
|
109
|
-
chain_id: string;
|
|
110
|
-
|
|
111
|
-
/** Human-readable title — e.g. "Order #1234 — Amaka Johnson" */
|
|
112
|
-
title: string;
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Current status of the chain:
|
|
116
|
-
* - "open" → some steps still pending confirmation
|
|
117
|
-
* - "completed" → all steps confirmed, chain is sealed
|
|
118
|
-
*/
|
|
119
|
-
status: 'open' | 'completed';
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Public tracking URL — send this to whoever is waiting on the delivery.
|
|
123
|
-
* They open it in any browser. No account needed.
|
|
124
|
-
* Updates automatically every 15 seconds as steps are confirmed.
|
|
125
|
-
*/
|
|
126
|
-
tracking_url: string;
|
|
127
|
-
|
|
128
|
-
/** Raw tracking token embedded in tracking_url */
|
|
129
|
-
tracking_token: string;
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* One per actor you defined when creating the chain.
|
|
133
|
-
* Send each confirmation_url to the relevant party:
|
|
134
|
-
* chain.handoffs[0].confirmation_url → Kitchen
|
|
135
|
-
* chain.handoffs[1].confirmation_url → Rider (pickup)
|
|
136
|
-
* chain.handoffs[2].confirmation_url → Rider (delivery)
|
|
137
|
-
*/
|
|
138
|
-
handoffs: PendingHandoff[];
|
|
139
|
-
|
|
140
|
-
/** ISO timestamp of when this chain was created */
|
|
141
|
-
created_at: string;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
145
|
-
// CHAIN DETAIL — full audit trail (from getChain())
|
|
146
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Full chain detail including the complete audit trail.
|
|
150
|
-
* Returned by getChain() — use this to see the current state
|
|
151
|
-
* of every step and all captured evidence.
|
|
152
|
-
*/
|
|
153
|
-
export interface ChainDetail {
|
|
154
|
-
chain_id: string;
|
|
155
|
-
title: string;
|
|
156
|
-
status: 'open' | 'completed';
|
|
157
|
-
metadata: Record<string, any>;
|
|
158
|
-
tracking_url: string;
|
|
159
|
-
|
|
160
|
-
/** SHA-256 hash of the entire completed chain — null until all steps confirmed */
|
|
161
|
-
integrity_hash: string | null;
|
|
162
|
-
|
|
163
|
-
/** Whether the chain has been tampered with */
|
|
164
|
-
tampered: boolean;
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* True only when:
|
|
168
|
-
* - status === "completed" (all steps confirmed)
|
|
169
|
-
* - tampered === false (no records altered)
|
|
170
|
-
*/
|
|
171
|
-
verified: boolean;
|
|
172
|
-
|
|
173
|
-
created_at: string;
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* The complete audit trail — one ProofEvent per actor.
|
|
177
|
-
* Each event answers: who, what, when, where, evidence.
|
|
178
|
-
*/
|
|
179
|
-
trail: (ProofEvent & {
|
|
180
|
-
id: string;
|
|
181
|
-
note: string | null;
|
|
182
|
-
/** confirmation_url is null once the step has been confirmed (link is spent) */
|
|
183
|
-
confirmation_url: string | null;
|
|
184
|
-
})[];
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
188
|
-
// VERIFICATION RESULT — from verify()
|
|
189
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* Result of calling verify() on a completed chain.
|
|
193
|
-
*
|
|
194
|
-
* Use this when:
|
|
195
|
-
* - A customer disputes a delivery
|
|
196
|
-
* - A regulator asks for proof of an event
|
|
197
|
-
* - An auditor needs to confirm a record wasn't altered
|
|
198
|
-
* - Anyone needs to know: did this actually happen?
|
|
199
|
-
*
|
|
200
|
-
* verified: true → every step happened as recorded, nothing was altered
|
|
201
|
-
* verified: false → chain is incomplete or tampered
|
|
202
|
-
*/
|
|
203
|
-
export interface VerificationResult {
|
|
204
|
-
chain_id: string;
|
|
205
|
-
title: string;
|
|
206
|
-
status: 'open' | 'completed';
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* The single most important field.
|
|
210
|
-
* true = this chain is real, complete, and untampered
|
|
211
|
-
* false = incomplete or evidence of tampering
|
|
212
|
-
*/
|
|
213
|
-
verified: boolean;
|
|
214
|
-
|
|
215
|
-
/** true if any record in the chain was altered after the fact */
|
|
216
|
-
tampered: boolean;
|
|
217
|
-
|
|
218
|
-
/** SHA-256 hash of the entire chain — for external audit records */
|
|
219
|
-
integrity_hash: string | null;
|
|
220
|
-
|
|
221
|
-
/** ISO timestamp of when the final step was confirmed */
|
|
222
|
-
completed_at: string | null;
|
|
223
|
-
|
|
224
|
-
/** Number of steps confirmed in this chain */
|
|
225
|
-
handoff_count: number;
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* The verified audit trail.
|
|
229
|
-
* Each entry is a cryptographically verified ProofEvent.
|
|
230
|
-
*/
|
|
231
|
-
trail: (Pick<
|
|
232
|
-
ProofEvent,
|
|
233
|
-
'event' | 'actor' | 'confirmed_at' | 'location' | 'hash'
|
|
234
|
-
> & {
|
|
235
|
-
/** true if this specific step's hash verified correctly */
|
|
236
|
-
valid: boolean;
|
|
237
|
-
})[];
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
241
|
-
// INPUT TYPES — what you pass in
|
|
242
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* One actor in a chain — defines a step that needs to be confirmed.
|
|
246
|
-
*
|
|
247
|
-
* Use plain language that makes sense for your industry:
|
|
248
|
-
* Food: { event: "packed", actor: "Kitchen" }
|
|
249
|
-
* Pharmacy: { event: "dispensed", actor: "Pharmacist" }
|
|
250
|
-
* Banking: { event: "dispatched",actor: "Branch Officer" }
|
|
251
|
-
* Construction: { event: "milestone_complete", actor: "Contractor" }
|
|
252
|
-
* Legal: { event: "served", actor: "Process Server" }
|
|
253
|
-
* Insurance: { event: "assessed", actor: "Claims Assessor" }
|
|
254
|
-
*/
|
|
255
|
-
export interface ProofActor {
|
|
256
|
-
/** What happens at this step — use plain language from your domain */
|
|
257
|
-
event: string;
|
|
258
|
-
|
|
259
|
-
/** Who is responsible for confirming this step */
|
|
260
|
-
actor: string;
|
|
261
|
-
|
|
262
|
-
/** Optional internal note about this step */
|
|
263
|
-
note?: string;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
/** Input for creating a new chain of custody */
|
|
267
|
-
export interface CreateChainInput {
|
|
268
|
-
/**
|
|
269
|
-
* Human-readable title for this chain.
|
|
270
|
-
* Include enough context to identify it later:
|
|
271
|
-
* "Order #1234 — Amaka Johnson"
|
|
272
|
-
* "Prescription #5567 — Mrs Obi"
|
|
273
|
-
* "Site inspection — Plot 14, Lekki"
|
|
274
|
-
*/
|
|
275
|
-
title: string;
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* Any extra data you want to store with this chain.
|
|
279
|
-
* Does not affect the proof logic — purely for your reference.
|
|
280
|
-
*/
|
|
281
|
-
metadata?: Record<string, any>;
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* The steps that need to be confirmed, in order.
|
|
285
|
-
* Each one gets its own confirmation_url.
|
|
286
|
-
* Steps are confirmed independently — order is tracked by timestamp.
|
|
287
|
-
*/
|
|
288
|
-
actors: ProofActor[];
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
292
|
-
// SDK CLIENT
|
|
293
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
294
|
-
|
|
295
|
-
export interface SwiftProofConfig {
|
|
296
|
-
/** Your API key — starts with sp_live_ or sp_test_ */
|
|
297
|
-
apiKey: string;
|
|
298
|
-
|
|
299
|
-
/**
|
|
300
|
-
* API base URL.
|
|
301
|
-
* Defaults to https://swiftproof-infra.onrender.com
|
|
302
|
-
* Override for local development: http://localhost:4000
|
|
303
|
-
*/
|
|
304
|
-
baseUrl?: string;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
/**
|
|
308
|
-
* SwiftProof SDK
|
|
309
|
-
*
|
|
310
|
-
* Proof-of-work infrastructure. One API call captures tamper-proof
|
|
311
|
-
* evidence of any event in any industry.
|
|
312
|
-
*
|
|
313
|
-
* @example
|
|
314
|
-
* ```typescript
|
|
315
|
-
* const sp = new SwiftProof({ apiKey: 'sp_live_xxx' });
|
|
316
|
-
*
|
|
317
|
-
* const chain = await sp.chains.create({
|
|
318
|
-
* title: 'Order #1234 — Amaka Johnson',
|
|
319
|
-
* actors: [
|
|
320
|
-
* { event: 'packed', actor: 'Kitchen' },
|
|
321
|
-
* { event: 'pickup', actor: 'Rider — Emeka' },
|
|
322
|
-
* { event: 'delivered', actor: 'Rider — Emeka' },
|
|
323
|
-
* ],
|
|
324
|
-
* });
|
|
325
|
-
*
|
|
326
|
-
* // Send these to the relevant parties
|
|
327
|
-
* chain.handoffs[0].confirmation_url // → Kitchen
|
|
328
|
-
* chain.handoffs[1].confirmation_url // → Rider (pickup)
|
|
329
|
-
* chain.handoffs[2].confirmation_url // → Rider (delivery)
|
|
330
|
-
*
|
|
331
|
-
* // Send this to the customer
|
|
332
|
-
* chain.tracking_url
|
|
333
|
-
*
|
|
334
|
-
* // Later — verify everything happened
|
|
335
|
-
* const proof = await sp.chains.verify(chain.chain_id);
|
|
336
|
-
* proof.verified // true = happened, untampered
|
|
337
|
-
* proof.tampered // false = nothing altered
|
|
338
|
-
* ```
|
|
339
|
-
*/
|
|
340
|
-
export class SwiftProof {
|
|
341
|
-
private readonly baseUrl: string;
|
|
342
|
-
private readonly headers: Record<string, string>;
|
|
343
|
-
|
|
344
|
-
constructor(config: SwiftProofConfig) {
|
|
345
|
-
if (!config.apiKey) throw new Error('SwiftProof: apiKey is required');
|
|
346
|
-
if (!config.apiKey.startsWith('sp_')) {
|
|
347
|
-
throw new Error(
|
|
348
|
-
'SwiftProof: apiKey must start with sp_live_ or sp_test_',
|
|
349
|
-
);
|
|
350
|
-
}
|
|
351
|
-
this.baseUrl = (
|
|
352
|
-
config.baseUrl ?? 'https://swiftproof-infra.onrender.com'
|
|
353
|
-
).replace(/\/$/, '');
|
|
354
|
-
this.headers = {
|
|
355
|
-
'Content-Type': 'application/json',
|
|
356
|
-
Authorization: `Bearer ${config.apiKey}`,
|
|
357
|
-
};
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
private async request<T>(
|
|
361
|
-
method: string,
|
|
362
|
-
path: string,
|
|
363
|
-
body?: unknown,
|
|
364
|
-
): Promise<T> {
|
|
365
|
-
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
366
|
-
method,
|
|
367
|
-
headers: this.headers,
|
|
368
|
-
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
369
|
-
});
|
|
370
|
-
|
|
371
|
-
const data = await res.json().catch(() => ({}));
|
|
372
|
-
|
|
373
|
-
if (!res.ok) {
|
|
374
|
-
const message =
|
|
375
|
-
(data as any)?.message ?? `Request failed with status ${res.status}`;
|
|
376
|
-
const err = new Error(`SwiftProof API error: ${message}`) as any;
|
|
377
|
-
err.status = res.status;
|
|
378
|
-
err.response = data;
|
|
379
|
-
throw err;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
return data as T;
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
/** All chain of custody operations */
|
|
386
|
-
readonly chains = {
|
|
387
|
-
/**
|
|
388
|
-
* Create a new chain of custody.
|
|
389
|
-
*
|
|
390
|
-
* Call this when a real-world handoff event begins:
|
|
391
|
-
* an order is placed, a delivery is dispatched,
|
|
392
|
-
* an inspection is scheduled, a document is to be served.
|
|
393
|
-
*
|
|
394
|
-
* Returns confirmation URLs for each actor and a
|
|
395
|
-
* tracking URL for the end recipient.
|
|
396
|
-
*
|
|
397
|
-
* @example
|
|
398
|
-
* ```typescript
|
|
399
|
-
* const chain = await sp.chains.create({
|
|
400
|
-
* title: 'Order #1234',
|
|
401
|
-
* actors: [
|
|
402
|
-
* { event: 'packed', actor: 'Kitchen' },
|
|
403
|
-
* { event: 'delivered', actor: 'Rider' },
|
|
404
|
-
* ],
|
|
405
|
-
* });
|
|
406
|
-
*
|
|
407
|
-
* // Kitchen taps this link → GPS captured → "packed" confirmed
|
|
408
|
-
* chain.handoffs[0].confirmation_url
|
|
409
|
-
*
|
|
410
|
-
* // Customer opens this → sees live status timeline
|
|
411
|
-
* chain.tracking_url
|
|
412
|
-
* ```
|
|
413
|
-
*/
|
|
414
|
-
create: (input: CreateChainInput): Promise<Chain> =>
|
|
415
|
-
this.request<Chain>('POST', '/v1/chains', input),
|
|
416
|
-
|
|
417
|
-
/**
|
|
418
|
-
* Get the full audit trail for a chain.
|
|
419
|
-
*
|
|
420
|
-
* Shows every step — confirmed or pending —
|
|
421
|
-
* with all captured evidence: GPS, timestamp, photo, hash.
|
|
422
|
-
*
|
|
423
|
-
* @example
|
|
424
|
-
* ```typescript
|
|
425
|
-
* const detail = await sp.chains.get('sp_ch_abc123');
|
|
426
|
-
* detail.trail.forEach(step => {
|
|
427
|
-
* console.log(step.event, step.confirmed_at, step.location);
|
|
428
|
-
* });
|
|
429
|
-
* ```
|
|
430
|
-
*/
|
|
431
|
-
get: (chainId: string): Promise<ChainDetail> =>
|
|
432
|
-
this.request<ChainDetail>('GET', `/v1/chains/${chainId}`),
|
|
433
|
-
|
|
434
|
-
/**
|
|
435
|
-
* Verify a completed chain of custody.
|
|
436
|
-
*
|
|
437
|
-
* Recomputes every hash in the chain and confirms nothing
|
|
438
|
-
* was altered after the fact.
|
|
439
|
-
*
|
|
440
|
-
* Use this when:
|
|
441
|
-
* - A customer disputes a delivery
|
|
442
|
-
* - A regulator asks for proof
|
|
443
|
-
* - An auditor needs to confirm records are untampered
|
|
444
|
-
*
|
|
445
|
-
* @returns VerificationResult with verified: true if chain is
|
|
446
|
-
* complete and cryptographically intact
|
|
447
|
-
*
|
|
448
|
-
* @example
|
|
449
|
-
* ```typescript
|
|
450
|
-
* const proof = await sp.chains.verify('sp_ch_abc123');
|
|
451
|
-
*
|
|
452
|
-
* if (proof.verified) {
|
|
453
|
-
* // Chain is complete and untampered
|
|
454
|
-
* // Show proof.trail as evidence
|
|
455
|
-
* } else if (proof.tampered) {
|
|
456
|
-
* // Records were altered — do not use as evidence
|
|
457
|
-
* } else {
|
|
458
|
-
* // Chain not yet complete — some steps pending
|
|
459
|
-
* }
|
|
460
|
-
* ```
|
|
461
|
-
*/
|
|
462
|
-
verify: (chainId: string): Promise<VerificationResult> =>
|
|
463
|
-
this.request<VerificationResult>('GET', `/v1/chains/${chainId}/verify`),
|
|
464
|
-
};
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
468
|
-
// DEFAULT EXPORT
|
|
469
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
470
|
-
|
|
471
|
-
export default SwiftProof;
|
package/src/types.js
DELETED
package/src/types.ts
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
export interface SwiftProofConfig {
|
|
2
|
-
apiKey: string;
|
|
3
|
-
baseUrl?: string;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
export interface Actor {
|
|
7
|
-
event: string;
|
|
8
|
-
actor: string;
|
|
9
|
-
note?: string;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface CreateChainParams {
|
|
13
|
-
title: string;
|
|
14
|
-
metadata?: Record<string, any>;
|
|
15
|
-
actors: Actor[];
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface ConfirmHandoffParams {
|
|
19
|
-
lat?: number;
|
|
20
|
-
lng?: number;
|
|
21
|
-
note?: string;
|
|
22
|
-
metadata?: Record<string, any>;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface Handoff {
|
|
26
|
-
id: string;
|
|
27
|
-
event: string;
|
|
28
|
-
actor: string;
|
|
29
|
-
confirmed: boolean;
|
|
30
|
-
confirmed_at: string | null;
|
|
31
|
-
location: string | null;
|
|
32
|
-
lat: number | null;
|
|
33
|
-
lng: number | null;
|
|
34
|
-
note: string | null;
|
|
35
|
-
hash: string | null;
|
|
36
|
-
confirmation_url: string | null;
|
|
37
|
-
confirmation_token?: string;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export interface Chain {
|
|
41
|
-
chain_id: string;
|
|
42
|
-
title: string;
|
|
43
|
-
status: 'open' | 'completed' | 'cancelled';
|
|
44
|
-
tracking_url: string;
|
|
45
|
-
tracking_token: string;
|
|
46
|
-
metadata?: Record<string, any>;
|
|
47
|
-
integrity_hash: string | null;
|
|
48
|
-
tampered: boolean;
|
|
49
|
-
verified: boolean;
|
|
50
|
-
created_at: string;
|
|
51
|
-
trail?: Handoff[];
|
|
52
|
-
handoffs?: Handoff[];
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export interface VerifyResult {
|
|
56
|
-
chain_id: string;
|
|
57
|
-
title: string;
|
|
58
|
-
status: string;
|
|
59
|
-
verified: boolean;
|
|
60
|
-
tampered: boolean;
|
|
61
|
-
integrity_hash: string | null;
|
|
62
|
-
completed_at: string | null;
|
|
63
|
-
handoff_count: number;
|
|
64
|
-
trail: Array<{
|
|
65
|
-
event: string;
|
|
66
|
-
actor: string;
|
|
67
|
-
confirmed_at: string;
|
|
68
|
-
location: string | null;
|
|
69
|
-
hash: string;
|
|
70
|
-
valid: boolean;
|
|
71
|
-
}>;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export interface ConfirmResult {
|
|
75
|
-
success: boolean;
|
|
76
|
-
handoff_id: string;
|
|
77
|
-
event: string;
|
|
78
|
-
actor: string;
|
|
79
|
-
confirmed_at: string;
|
|
80
|
-
location: string | null;
|
|
81
|
-
chain_status: string;
|
|
82
|
-
message: string;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export interface TrackingResult {
|
|
86
|
-
title: string;
|
|
87
|
-
status: string;
|
|
88
|
-
current_location: string | null;
|
|
89
|
-
current_lat: number | null;
|
|
90
|
-
current_lng: number | null;
|
|
91
|
-
last_update: string | null;
|
|
92
|
-
trail: Array<{
|
|
93
|
-
event: string;
|
|
94
|
-
actor: string;
|
|
95
|
-
confirmed: boolean;
|
|
96
|
-
confirmed_at: string | null;
|
|
97
|
-
location: string | null;
|
|
98
|
-
lat: number | null;
|
|
99
|
-
lng: number | null;
|
|
100
|
-
}>;
|
|
101
|
-
}
|
package/tsconfig.json
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2018",
|
|
4
|
-
"module": "commonjs",
|
|
5
|
-
|
|
6
|
-
"declaration": true,
|
|
7
|
-
"declarationMap": true,
|
|
8
|
-
"sourceMap": true,
|
|
9
|
-
"lib": ["ES2020", "DOM"],
|
|
10
|
-
"outDir": "./dist",
|
|
11
|
-
"strict": true,
|
|
12
|
-
"esModuleInterop": true,
|
|
13
|
-
"skipLibCheck": true,
|
|
14
|
-
"forceConsistentCasingInFileNames": true,
|
|
15
|
-
"ignoreDeprecations": "5.0"
|
|
16
|
-
},
|
|
17
|
-
"include": ["src/**/*"],
|
|
18
|
-
"exclude": ["node_modules", "dist"]
|
|
19
|
-
}
|
package/tsup.config.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const tsup_1 = require("tsup");
|
|
4
|
-
exports.default = (0, tsup_1.defineConfig)({
|
|
5
|
-
entry: ['src/index.ts'],
|
|
6
|
-
format: ['cjs', 'esm'],
|
|
7
|
-
dts: false,
|
|
8
|
-
clean: true,
|
|
9
|
-
sourcemap: true,
|
|
10
|
-
treeshake: true,
|
|
11
|
-
tsconfig: './tsconfig.json',
|
|
12
|
-
});
|
package/tsup.config.ts
DELETED