swiftproof 0.1.0

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/dist/index.js ADDED
@@ -0,0 +1,119 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ // src/index.ts
6
+ var SwiftProof = class {
7
+ constructor(config) {
8
+ /** All chain of custody operations */
9
+ this.chains = {
10
+ /**
11
+ * Create a new chain of custody.
12
+ *
13
+ * Call this when a real-world handoff event begins:
14
+ * an order is placed, a delivery is dispatched,
15
+ * an inspection is scheduled, a document is to be served.
16
+ *
17
+ * Returns confirmation URLs for each actor and a
18
+ * tracking URL for the end recipient.
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * const chain = await sp.chains.create({
23
+ * title: 'Order #1234',
24
+ * actors: [
25
+ * { event: 'packed', actor: 'Kitchen' },
26
+ * { event: 'delivered', actor: 'Rider' },
27
+ * ],
28
+ * });
29
+ *
30
+ * // Kitchen taps this link → GPS captured → "packed" confirmed
31
+ * chain.handoffs[0].confirmation_url
32
+ *
33
+ * // Customer opens this → sees live status timeline
34
+ * chain.tracking_url
35
+ * ```
36
+ */
37
+ create: (input) => this.request("POST", "/v1/chains", input),
38
+ /**
39
+ * Get the full audit trail for a chain.
40
+ *
41
+ * Shows every step — confirmed or pending —
42
+ * with all captured evidence: GPS, timestamp, photo, hash.
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * const detail = await sp.chains.get('sp_ch_abc123');
47
+ * detail.trail.forEach(step => {
48
+ * console.log(step.event, step.confirmed_at, step.location);
49
+ * });
50
+ * ```
51
+ */
52
+ get: (chainId) => this.request("GET", `/v1/chains/${chainId}`),
53
+ /**
54
+ * Verify a completed chain of custody.
55
+ *
56
+ * Recomputes every hash in the chain and confirms nothing
57
+ * was altered after the fact.
58
+ *
59
+ * Use this when:
60
+ * - A customer disputes a delivery
61
+ * - A regulator asks for proof
62
+ * - An auditor needs to confirm records are untampered
63
+ *
64
+ * @returns VerificationResult with verified: true if chain is
65
+ * complete and cryptographically intact
66
+ *
67
+ * @example
68
+ * ```typescript
69
+ * const proof = await sp.chains.verify('sp_ch_abc123');
70
+ *
71
+ * if (proof.verified) {
72
+ * // Chain is complete and untampered
73
+ * // Show proof.trail as evidence
74
+ * } else if (proof.tampered) {
75
+ * // Records were altered — do not use as evidence
76
+ * } else {
77
+ * // Chain not yet complete — some steps pending
78
+ * }
79
+ * ```
80
+ */
81
+ verify: (chainId) => this.request("GET", `/v1/chains/${chainId}/verify`)
82
+ };
83
+ var _a;
84
+ if (!config.apiKey) throw new Error("SwiftProof: apiKey is required");
85
+ if (!config.apiKey.startsWith("sp_")) {
86
+ throw new Error(
87
+ "SwiftProof: apiKey must start with sp_live_ or sp_test_"
88
+ );
89
+ }
90
+ this.baseUrl = ((_a = config.baseUrl) != null ? _a : "https://swiftproof-infra.onrender.com").replace(/\/$/, "");
91
+ this.headers = {
92
+ "Content-Type": "application/json",
93
+ Authorization: `Bearer ${config.apiKey}`
94
+ };
95
+ }
96
+ async request(method, path, body) {
97
+ var _a;
98
+ const res = await fetch(`${this.baseUrl}${path}`, {
99
+ method,
100
+ headers: this.headers,
101
+ body: body !== void 0 ? JSON.stringify(body) : void 0
102
+ });
103
+ const data = await res.json().catch(() => ({}));
104
+ if (!res.ok) {
105
+ const message = (_a = data == null ? void 0 : data.message) != null ? _a : `Request failed with status ${res.status}`;
106
+ const err = new Error(`SwiftProof API error: ${message}`);
107
+ err.status = res.status;
108
+ err.response = data;
109
+ throw err;
110
+ }
111
+ return data;
112
+ }
113
+ };
114
+ var index_default = SwiftProof;
115
+
116
+ exports.SwiftProof = SwiftProof;
117
+ exports.default = index_default;
118
+ //# sourceMappingURL=index.js.map
119
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";;;;;AAmVO,IAAM,aAAN,MAAiB;AAAA,EAItB,YAAY,MAAA,EAA0B;AA0CtC;AAAA,IAAA,IAAA,CAAS,MAAA,GAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MA4BhB,QAAQ,CAAC,KAAA,KACP,KAAK,OAAA,CAAe,MAAA,EAAQ,cAAc,KAAK,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAgBjD,GAAA,EAAK,CAAC,OAAA,KACJ,IAAA,CAAK,QAAqB,KAAA,EAAO,CAAA,WAAA,EAAc,OAAO,CAAA,CAAE,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MA8B1D,MAAA,EAAQ,CAAC,OAAA,KACP,IAAA,CAAK,QAA4B,KAAA,EAAO,CAAA,WAAA,EAAc,OAAO,CAAA,OAAA,CAAS;AAAA,KAC1E;AA/cF,IAAA,IAAA,EAAA;AAwVI,IAAA,IAAI,CAAC,MAAA,CAAO,MAAA,EAAQ,MAAM,IAAI,MAAM,gCAAgC,CAAA;AACpE,IAAA,IAAI,CAAC,MAAA,CAAO,MAAA,CAAO,UAAA,CAAW,KAAK,CAAA,EAAG;AACpC,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AACA,IAAA,IAAA,CAAK,YACH,EAAA,GAAA,MAAA,CAAO,OAAA,KAAP,YAAkB,uCAAA,EAClB,OAAA,CAAQ,OAAO,EAAE,CAAA;AACnB,IAAA,IAAA,CAAK,OAAA,GAAU;AAAA,MACb,cAAA,EAAgB,kBAAA;AAAA,MAChB,aAAA,EAAe,CAAA,OAAA,EAAU,MAAA,CAAO,MAAM,CAAA;AAAA,KACxC;AAAA,EACF;AAAA,EAEA,MAAc,OAAA,CACZ,MAAA,EACA,IAAA,EACA,IAAA,EACY;AA3WhB,IAAA,IAAA,EAAA;AA4WI,IAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,CAAA,EAAG,KAAK,OAAO,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI;AAAA,MAChD,MAAA;AAAA,MACA,SAAS,IAAA,CAAK,OAAA;AAAA,MACd,MAAM,IAAA,KAAS,MAAA,GAAY,IAAA,CAAK,SAAA,CAAU,IAAI,CAAA,GAAI;AAAA,KACnD,CAAA;AAED,IAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAAI,IAAA,GAAO,KAAA,CAAM,OAAO,EAAC,CAAE,CAAA;AAE9C,IAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,MAAA,MAAM,WACH,EAAA,GAAA,IAAA,IAAA,IAAA,GAAA,MAAA,GAAA,IAAA,CAAc,OAAA,KAAd,IAAA,GAAA,EAAA,GAAyB,CAAA,2BAAA,EAA8B,IAAI,MAAM,CAAA,CAAA;AACpE,MAAA,MAAM,GAAA,GAAM,IAAI,KAAA,CAAM,CAAA,sBAAA,EAAyB,OAAO,CAAA,CAAE,CAAA;AACxD,MAAA,GAAA,CAAI,SAAS,GAAA,CAAI,MAAA;AACjB,MAAA,GAAA,CAAI,QAAA,GAAW,IAAA;AACf,MAAA,MAAM,GAAA;AAAA,IACR;AAEA,IAAA,OAAO,IAAA;AAAA,EACT;AAkFF;AAMA,IAAO,aAAA,GAAQ","file":"index.js","sourcesContent":["/**\r\n * SwiftProof JavaScript/TypeScript SDK\r\n *\r\n * Proof-of-work infrastructure for any industry where\r\n * something needs to be verified.\r\n *\r\n * npm install swiftproof\r\n */\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// PROOF EVENT — the core object every chain produces\r\n// Every confirmation in every industry maps to this same shape\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * A single verified proof event — one step in a chain of custody.\r\n *\r\n * Every proof event answers five universal questions:\r\n * → who did it = actor\r\n * → what happened = event\r\n * → when it happened = confirmed_at\r\n * → where it happened = location + lat + lng\r\n * → evidence = photo_url\r\n *\r\n * The hash field makes it tamper-proof:\r\n * each hash includes the previous one, so altering any\r\n * record breaks the entire chain and is caught by verify().\r\n */\r\nexport interface ProofEvent {\r\n /** What happened — e.g. \"delivered\", \"inspected\", \"dispensed\", \"signed\" */\r\n event: string;\r\n\r\n /** Who did it — e.g. \"Rider — Emeka\", \"Pharmacist\", \"Inspector\" */\r\n actor: string;\r\n\r\n /** Whether this step has been confirmed yet */\r\n confirmed: boolean;\r\n\r\n /** Exact ISO timestamp of when the actor tapped confirm — null if not yet confirmed */\r\n confirmed_at: string | null;\r\n\r\n /** Human-readable location name (reverse geocoded) — e.g. \"Allen Avenue, Ikeja\" */\r\n location: string | null;\r\n\r\n /** GPS latitude at moment of confirmation */\r\n lat: number | null;\r\n\r\n /** GPS longitude at moment of confirmation */\r\n lng: number | null;\r\n\r\n /** Photo evidence URL (Cloudinary) — attached by actor at confirmation */\r\n photo_url: string | null;\r\n\r\n /**\r\n * SHA-256 hash of this event chained to the previous one.\r\n * If this hash doesn't match when verify() recomputes it,\r\n * the chain is flagged as tampered.\r\n */\r\n hash: string | null;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// HANDOFF — a pending proof event (before confirmation)\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * A pending step in a chain — waiting for the actor to confirm.\r\n * Returned when you create a chain, before any confirmation happens.\r\n */\r\nexport interface PendingHandoff {\r\n /** Unique handoff ID */\r\n id: string;\r\n\r\n /** What this step is — e.g. \"pickup\", \"delivered\" */\r\n event: string;\r\n\r\n /** Who needs to confirm this step */\r\n actor: string;\r\n\r\n /**\r\n * The URL to send to the actor.\r\n * When they open this link on any phone browser and tap confirm,\r\n * their GPS is captured automatically and the proof event is recorded.\r\n * No app download. No account. Just tap.\r\n */\r\n confirmation_url: string;\r\n\r\n /** Raw token embedded in confirmation_url — for reference only */\r\n confirmation_token: string;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// CHAIN — a complete chain of custody\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * A chain of custody — ties multiple proof events together\r\n * under one verifiable record.\r\n *\r\n * One chain per real-world event:\r\n * - one food delivery\r\n * - one pharmaceutical dispensing\r\n * - one field agent visit\r\n * - one court document service\r\n * - one construction milestone\r\n */\r\nexport interface Chain {\r\n /** Unique chain ID — reference this when calling getChain() or verify() */\r\n chain_id: string;\r\n\r\n /** Human-readable title — e.g. \"Order #1234 — Amaka Johnson\" */\r\n title: string;\r\n\r\n /**\r\n * Current status of the chain:\r\n * - \"open\" → some steps still pending confirmation\r\n * - \"completed\" → all steps confirmed, chain is sealed\r\n */\r\n status: 'open' | 'completed';\r\n\r\n /**\r\n * Public tracking URL — send this to whoever is waiting on the delivery.\r\n * They open it in any browser. No account needed.\r\n * Updates automatically every 15 seconds as steps are confirmed.\r\n */\r\n tracking_url: string;\r\n\r\n /** Raw tracking token embedded in tracking_url */\r\n tracking_token: string;\r\n\r\n /**\r\n * One per actor you defined when creating the chain.\r\n * Send each confirmation_url to the relevant party:\r\n * chain.handoffs[0].confirmation_url → Kitchen\r\n * chain.handoffs[1].confirmation_url → Rider (pickup)\r\n * chain.handoffs[2].confirmation_url → Rider (delivery)\r\n */\r\n handoffs: PendingHandoff[];\r\n\r\n /** ISO timestamp of when this chain was created */\r\n created_at: string;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// CHAIN DETAIL — full audit trail (from getChain())\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Full chain detail including the complete audit trail.\r\n * Returned by getChain() — use this to see the current state\r\n * of every step and all captured evidence.\r\n */\r\nexport interface ChainDetail {\r\n chain_id: string;\r\n title: string;\r\n status: 'open' | 'completed';\r\n metadata: Record<string, any>;\r\n tracking_url: string;\r\n\r\n /** SHA-256 hash of the entire completed chain — null until all steps confirmed */\r\n integrity_hash: string | null;\r\n\r\n /** Whether the chain has been tampered with */\r\n tampered: boolean;\r\n\r\n /**\r\n * True only when:\r\n * - status === \"completed\" (all steps confirmed)\r\n * - tampered === false (no records altered)\r\n */\r\n verified: boolean;\r\n\r\n created_at: string;\r\n\r\n /**\r\n * The complete audit trail — one ProofEvent per actor.\r\n * Each event answers: who, what, when, where, evidence.\r\n */\r\n trail: (ProofEvent & {\r\n id: string;\r\n note: string | null;\r\n /** confirmation_url is null once the step has been confirmed (link is spent) */\r\n confirmation_url: string | null;\r\n })[];\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// VERIFICATION RESULT — from verify()\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Result of calling verify() on a completed chain.\r\n *\r\n * Use this when:\r\n * - A customer disputes a delivery\r\n * - A regulator asks for proof of an event\r\n * - An auditor needs to confirm a record wasn't altered\r\n * - Anyone needs to know: did this actually happen?\r\n *\r\n * verified: true → every step happened as recorded, nothing was altered\r\n * verified: false → chain is incomplete or tampered\r\n */\r\nexport interface VerificationResult {\r\n chain_id: string;\r\n title: string;\r\n status: 'open' | 'completed';\r\n\r\n /**\r\n * The single most important field.\r\n * true = this chain is real, complete, and untampered\r\n * false = incomplete or evidence of tampering\r\n */\r\n verified: boolean;\r\n\r\n /** true if any record in the chain was altered after the fact */\r\n tampered: boolean;\r\n\r\n /** SHA-256 hash of the entire chain — for external audit records */\r\n integrity_hash: string | null;\r\n\r\n /** ISO timestamp of when the final step was confirmed */\r\n completed_at: string | null;\r\n\r\n /** Number of steps confirmed in this chain */\r\n handoff_count: number;\r\n\r\n /**\r\n * The verified audit trail.\r\n * Each entry is a cryptographically verified ProofEvent.\r\n */\r\n trail: (Pick<\r\n ProofEvent,\r\n 'event' | 'actor' | 'confirmed_at' | 'location' | 'hash'\r\n > & {\r\n /** true if this specific step's hash verified correctly */\r\n valid: boolean;\r\n })[];\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// INPUT TYPES — what you pass in\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * One actor in a chain — defines a step that needs to be confirmed.\r\n *\r\n * Use plain language that makes sense for your industry:\r\n * Food: { event: \"packed\", actor: \"Kitchen\" }\r\n * Pharmacy: { event: \"dispensed\", actor: \"Pharmacist\" }\r\n * Banking: { event: \"dispatched\",actor: \"Branch Officer\" }\r\n * Construction: { event: \"milestone_complete\", actor: \"Contractor\" }\r\n * Legal: { event: \"served\", actor: \"Process Server\" }\r\n * Insurance: { event: \"assessed\", actor: \"Claims Assessor\" }\r\n */\r\nexport interface ProofActor {\r\n /** What happens at this step — use plain language from your domain */\r\n event: string;\r\n\r\n /** Who is responsible for confirming this step */\r\n actor: string;\r\n\r\n /** Optional internal note about this step */\r\n note?: string;\r\n}\r\n\r\n/** Input for creating a new chain of custody */\r\nexport interface CreateChainInput {\r\n /**\r\n * Human-readable title for this chain.\r\n * Include enough context to identify it later:\r\n * \"Order #1234 — Amaka Johnson\"\r\n * \"Prescription #5567 — Mrs Obi\"\r\n * \"Site inspection — Plot 14, Lekki\"\r\n */\r\n title: string;\r\n\r\n /**\r\n * Any extra data you want to store with this chain.\r\n * Does not affect the proof logic — purely for your reference.\r\n */\r\n metadata?: Record<string, any>;\r\n\r\n /**\r\n * The steps that need to be confirmed, in order.\r\n * Each one gets its own confirmation_url.\r\n * Steps are confirmed independently — order is tracked by timestamp.\r\n */\r\n actors: ProofActor[];\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// SDK CLIENT\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nexport interface SwiftProofConfig {\r\n /** Your API key — starts with sp_live_ or sp_test_ */\r\n apiKey: string;\r\n\r\n /**\r\n * API base URL.\r\n * Defaults to https://swiftproof-infra.onrender.com\r\n * Override for local development: http://localhost:4000\r\n */\r\n baseUrl?: string;\r\n}\r\n\r\n/**\r\n * SwiftProof SDK\r\n *\r\n * Proof-of-work infrastructure. One API call captures tamper-proof\r\n * evidence of any event in any industry.\r\n *\r\n * @example\r\n * ```typescript\r\n * const sp = new SwiftProof({ apiKey: 'sp_live_xxx' });\r\n *\r\n * const chain = await sp.chains.create({\r\n * title: 'Order #1234 — Amaka Johnson',\r\n * actors: [\r\n * { event: 'packed', actor: 'Kitchen' },\r\n * { event: 'pickup', actor: 'Rider — Emeka' },\r\n * { event: 'delivered', actor: 'Rider — Emeka' },\r\n * ],\r\n * });\r\n *\r\n * // Send these to the relevant parties\r\n * chain.handoffs[0].confirmation_url // → Kitchen\r\n * chain.handoffs[1].confirmation_url // → Rider (pickup)\r\n * chain.handoffs[2].confirmation_url // → Rider (delivery)\r\n *\r\n * // Send this to the customer\r\n * chain.tracking_url\r\n *\r\n * // Later — verify everything happened\r\n * const proof = await sp.chains.verify(chain.chain_id);\r\n * proof.verified // true = happened, untampered\r\n * proof.tampered // false = nothing altered\r\n * ```\r\n */\r\nexport class SwiftProof {\r\n private readonly baseUrl: string;\r\n private readonly headers: Record<string, string>;\r\n\r\n constructor(config: SwiftProofConfig) {\r\n if (!config.apiKey) throw new Error('SwiftProof: apiKey is required');\r\n if (!config.apiKey.startsWith('sp_')) {\r\n throw new Error(\r\n 'SwiftProof: apiKey must start with sp_live_ or sp_test_',\r\n );\r\n }\r\n this.baseUrl = (\r\n config.baseUrl ?? 'https://swiftproof-infra.onrender.com'\r\n ).replace(/\\/$/, '');\r\n this.headers = {\r\n 'Content-Type': 'application/json',\r\n Authorization: `Bearer ${config.apiKey}`,\r\n };\r\n }\r\n\r\n private async request<T>(\r\n method: string,\r\n path: string,\r\n body?: unknown,\r\n ): Promise<T> {\r\n const res = await fetch(`${this.baseUrl}${path}`, {\r\n method,\r\n headers: this.headers,\r\n body: body !== undefined ? JSON.stringify(body) : undefined,\r\n });\r\n\r\n const data = await res.json().catch(() => ({}));\r\n\r\n if (!res.ok) {\r\n const message =\r\n (data as any)?.message ?? `Request failed with status ${res.status}`;\r\n const err = new Error(`SwiftProof API error: ${message}`) as any;\r\n err.status = res.status;\r\n err.response = data;\r\n throw err;\r\n }\r\n\r\n return data as T;\r\n }\r\n\r\n /** All chain of custody operations */\r\n readonly chains = {\r\n /**\r\n * Create a new chain of custody.\r\n *\r\n * Call this when a real-world handoff event begins:\r\n * an order is placed, a delivery is dispatched,\r\n * an inspection is scheduled, a document is to be served.\r\n *\r\n * Returns confirmation URLs for each actor and a\r\n * tracking URL for the end recipient.\r\n *\r\n * @example\r\n * ```typescript\r\n * const chain = await sp.chains.create({\r\n * title: 'Order #1234',\r\n * actors: [\r\n * { event: 'packed', actor: 'Kitchen' },\r\n * { event: 'delivered', actor: 'Rider' },\r\n * ],\r\n * });\r\n *\r\n * // Kitchen taps this link → GPS captured → \"packed\" confirmed\r\n * chain.handoffs[0].confirmation_url\r\n *\r\n * // Customer opens this → sees live status timeline\r\n * chain.tracking_url\r\n * ```\r\n */\r\n create: (input: CreateChainInput): Promise<Chain> =>\r\n this.request<Chain>('POST', '/v1/chains', input),\r\n\r\n /**\r\n * Get the full audit trail for a chain.\r\n *\r\n * Shows every step — confirmed or pending —\r\n * with all captured evidence: GPS, timestamp, photo, hash.\r\n *\r\n * @example\r\n * ```typescript\r\n * const detail = await sp.chains.get('sp_ch_abc123');\r\n * detail.trail.forEach(step => {\r\n * console.log(step.event, step.confirmed_at, step.location);\r\n * });\r\n * ```\r\n */\r\n get: (chainId: string): Promise<ChainDetail> =>\r\n this.request<ChainDetail>('GET', `/v1/chains/${chainId}`),\r\n\r\n /**\r\n * Verify a completed chain of custody.\r\n *\r\n * Recomputes every hash in the chain and confirms nothing\r\n * was altered after the fact.\r\n *\r\n * Use this when:\r\n * - A customer disputes a delivery\r\n * - A regulator asks for proof\r\n * - An auditor needs to confirm records are untampered\r\n *\r\n * @returns VerificationResult with verified: true if chain is\r\n * complete and cryptographically intact\r\n *\r\n * @example\r\n * ```typescript\r\n * const proof = await sp.chains.verify('sp_ch_abc123');\r\n *\r\n * if (proof.verified) {\r\n * // Chain is complete and untampered\r\n * // Show proof.trail as evidence\r\n * } else if (proof.tampered) {\r\n * // Records were altered — do not use as evidence\r\n * } else {\r\n * // Chain not yet complete — some steps pending\r\n * }\r\n * ```\r\n */\r\n verify: (chainId: string): Promise<VerificationResult> =>\r\n this.request<VerificationResult>('GET', `/v1/chains/${chainId}/verify`),\r\n };\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// DEFAULT EXPORT\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nexport default SwiftProof;\r\n"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,114 @@
1
+ // src/index.ts
2
+ var SwiftProof = class {
3
+ constructor(config) {
4
+ /** All chain of custody operations */
5
+ this.chains = {
6
+ /**
7
+ * Create a new chain of custody.
8
+ *
9
+ * Call this when a real-world handoff event begins:
10
+ * an order is placed, a delivery is dispatched,
11
+ * an inspection is scheduled, a document is to be served.
12
+ *
13
+ * Returns confirmation URLs for each actor and a
14
+ * tracking URL for the end recipient.
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * const chain = await sp.chains.create({
19
+ * title: 'Order #1234',
20
+ * actors: [
21
+ * { event: 'packed', actor: 'Kitchen' },
22
+ * { event: 'delivered', actor: 'Rider' },
23
+ * ],
24
+ * });
25
+ *
26
+ * // Kitchen taps this link → GPS captured → "packed" confirmed
27
+ * chain.handoffs[0].confirmation_url
28
+ *
29
+ * // Customer opens this → sees live status timeline
30
+ * chain.tracking_url
31
+ * ```
32
+ */
33
+ create: (input) => this.request("POST", "/v1/chains", input),
34
+ /**
35
+ * Get the full audit trail for a chain.
36
+ *
37
+ * Shows every step — confirmed or pending —
38
+ * with all captured evidence: GPS, timestamp, photo, hash.
39
+ *
40
+ * @example
41
+ * ```typescript
42
+ * const detail = await sp.chains.get('sp_ch_abc123');
43
+ * detail.trail.forEach(step => {
44
+ * console.log(step.event, step.confirmed_at, step.location);
45
+ * });
46
+ * ```
47
+ */
48
+ get: (chainId) => this.request("GET", `/v1/chains/${chainId}`),
49
+ /**
50
+ * Verify a completed chain of custody.
51
+ *
52
+ * Recomputes every hash in the chain and confirms nothing
53
+ * was altered after the fact.
54
+ *
55
+ * Use this when:
56
+ * - A customer disputes a delivery
57
+ * - A regulator asks for proof
58
+ * - An auditor needs to confirm records are untampered
59
+ *
60
+ * @returns VerificationResult with verified: true if chain is
61
+ * complete and cryptographically intact
62
+ *
63
+ * @example
64
+ * ```typescript
65
+ * const proof = await sp.chains.verify('sp_ch_abc123');
66
+ *
67
+ * if (proof.verified) {
68
+ * // Chain is complete and untampered
69
+ * // Show proof.trail as evidence
70
+ * } else if (proof.tampered) {
71
+ * // Records were altered — do not use as evidence
72
+ * } else {
73
+ * // Chain not yet complete — some steps pending
74
+ * }
75
+ * ```
76
+ */
77
+ verify: (chainId) => this.request("GET", `/v1/chains/${chainId}/verify`)
78
+ };
79
+ var _a;
80
+ if (!config.apiKey) throw new Error("SwiftProof: apiKey is required");
81
+ if (!config.apiKey.startsWith("sp_")) {
82
+ throw new Error(
83
+ "SwiftProof: apiKey must start with sp_live_ or sp_test_"
84
+ );
85
+ }
86
+ this.baseUrl = ((_a = config.baseUrl) != null ? _a : "https://swiftproof-infra.onrender.com").replace(/\/$/, "");
87
+ this.headers = {
88
+ "Content-Type": "application/json",
89
+ Authorization: `Bearer ${config.apiKey}`
90
+ };
91
+ }
92
+ async request(method, path, body) {
93
+ var _a;
94
+ const res = await fetch(`${this.baseUrl}${path}`, {
95
+ method,
96
+ headers: this.headers,
97
+ body: body !== void 0 ? JSON.stringify(body) : void 0
98
+ });
99
+ const data = await res.json().catch(() => ({}));
100
+ if (!res.ok) {
101
+ const message = (_a = data == null ? void 0 : data.message) != null ? _a : `Request failed with status ${res.status}`;
102
+ const err = new Error(`SwiftProof API error: ${message}`);
103
+ err.status = res.status;
104
+ err.response = data;
105
+ throw err;
106
+ }
107
+ return data;
108
+ }
109
+ };
110
+ var index_default = SwiftProof;
111
+
112
+ export { SwiftProof, index_default as default };
113
+ //# sourceMappingURL=index.mjs.map
114
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";AAmVO,IAAM,aAAN,MAAiB;AAAA,EAItB,YAAY,MAAA,EAA0B;AA0CtC;AAAA,IAAA,IAAA,CAAS,MAAA,GAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MA4BhB,QAAQ,CAAC,KAAA,KACP,KAAK,OAAA,CAAe,MAAA,EAAQ,cAAc,KAAK,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAgBjD,GAAA,EAAK,CAAC,OAAA,KACJ,IAAA,CAAK,QAAqB,KAAA,EAAO,CAAA,WAAA,EAAc,OAAO,CAAA,CAAE,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MA8B1D,MAAA,EAAQ,CAAC,OAAA,KACP,IAAA,CAAK,QAA4B,KAAA,EAAO,CAAA,WAAA,EAAc,OAAO,CAAA,OAAA,CAAS;AAAA,KAC1E;AA/cF,IAAA,IAAA,EAAA;AAwVI,IAAA,IAAI,CAAC,MAAA,CAAO,MAAA,EAAQ,MAAM,IAAI,MAAM,gCAAgC,CAAA;AACpE,IAAA,IAAI,CAAC,MAAA,CAAO,MAAA,CAAO,UAAA,CAAW,KAAK,CAAA,EAAG;AACpC,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AACA,IAAA,IAAA,CAAK,YACH,EAAA,GAAA,MAAA,CAAO,OAAA,KAAP,YAAkB,uCAAA,EAClB,OAAA,CAAQ,OAAO,EAAE,CAAA;AACnB,IAAA,IAAA,CAAK,OAAA,GAAU;AAAA,MACb,cAAA,EAAgB,kBAAA;AAAA,MAChB,aAAA,EAAe,CAAA,OAAA,EAAU,MAAA,CAAO,MAAM,CAAA;AAAA,KACxC;AAAA,EACF;AAAA,EAEA,MAAc,OAAA,CACZ,MAAA,EACA,IAAA,EACA,IAAA,EACY;AA3WhB,IAAA,IAAA,EAAA;AA4WI,IAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,CAAA,EAAG,KAAK,OAAO,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI;AAAA,MAChD,MAAA;AAAA,MACA,SAAS,IAAA,CAAK,OAAA;AAAA,MACd,MAAM,IAAA,KAAS,MAAA,GAAY,IAAA,CAAK,SAAA,CAAU,IAAI,CAAA,GAAI;AAAA,KACnD,CAAA;AAED,IAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAAI,IAAA,GAAO,KAAA,CAAM,OAAO,EAAC,CAAE,CAAA;AAE9C,IAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,MAAA,MAAM,WACH,EAAA,GAAA,IAAA,IAAA,IAAA,GAAA,MAAA,GAAA,IAAA,CAAc,OAAA,KAAd,IAAA,GAAA,EAAA,GAAyB,CAAA,2BAAA,EAA8B,IAAI,MAAM,CAAA,CAAA;AACpE,MAAA,MAAM,GAAA,GAAM,IAAI,KAAA,CAAM,CAAA,sBAAA,EAAyB,OAAO,CAAA,CAAE,CAAA;AACxD,MAAA,GAAA,CAAI,SAAS,GAAA,CAAI,MAAA;AACjB,MAAA,GAAA,CAAI,QAAA,GAAW,IAAA;AACf,MAAA,MAAM,GAAA;AAAA,IACR;AAEA,IAAA,OAAO,IAAA;AAAA,EACT;AAkFF;AAMA,IAAO,aAAA,GAAQ","file":"index.mjs","sourcesContent":["/**\r\n * SwiftProof JavaScript/TypeScript SDK\r\n *\r\n * Proof-of-work infrastructure for any industry where\r\n * something needs to be verified.\r\n *\r\n * npm install swiftproof\r\n */\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// PROOF EVENT — the core object every chain produces\r\n// Every confirmation in every industry maps to this same shape\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * A single verified proof event — one step in a chain of custody.\r\n *\r\n * Every proof event answers five universal questions:\r\n * → who did it = actor\r\n * → what happened = event\r\n * → when it happened = confirmed_at\r\n * → where it happened = location + lat + lng\r\n * → evidence = photo_url\r\n *\r\n * The hash field makes it tamper-proof:\r\n * each hash includes the previous one, so altering any\r\n * record breaks the entire chain and is caught by verify().\r\n */\r\nexport interface ProofEvent {\r\n /** What happened — e.g. \"delivered\", \"inspected\", \"dispensed\", \"signed\" */\r\n event: string;\r\n\r\n /** Who did it — e.g. \"Rider — Emeka\", \"Pharmacist\", \"Inspector\" */\r\n actor: string;\r\n\r\n /** Whether this step has been confirmed yet */\r\n confirmed: boolean;\r\n\r\n /** Exact ISO timestamp of when the actor tapped confirm — null if not yet confirmed */\r\n confirmed_at: string | null;\r\n\r\n /** Human-readable location name (reverse geocoded) — e.g. \"Allen Avenue, Ikeja\" */\r\n location: string | null;\r\n\r\n /** GPS latitude at moment of confirmation */\r\n lat: number | null;\r\n\r\n /** GPS longitude at moment of confirmation */\r\n lng: number | null;\r\n\r\n /** Photo evidence URL (Cloudinary) — attached by actor at confirmation */\r\n photo_url: string | null;\r\n\r\n /**\r\n * SHA-256 hash of this event chained to the previous one.\r\n * If this hash doesn't match when verify() recomputes it,\r\n * the chain is flagged as tampered.\r\n */\r\n hash: string | null;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// HANDOFF — a pending proof event (before confirmation)\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * A pending step in a chain — waiting for the actor to confirm.\r\n * Returned when you create a chain, before any confirmation happens.\r\n */\r\nexport interface PendingHandoff {\r\n /** Unique handoff ID */\r\n id: string;\r\n\r\n /** What this step is — e.g. \"pickup\", \"delivered\" */\r\n event: string;\r\n\r\n /** Who needs to confirm this step */\r\n actor: string;\r\n\r\n /**\r\n * The URL to send to the actor.\r\n * When they open this link on any phone browser and tap confirm,\r\n * their GPS is captured automatically and the proof event is recorded.\r\n * No app download. No account. Just tap.\r\n */\r\n confirmation_url: string;\r\n\r\n /** Raw token embedded in confirmation_url — for reference only */\r\n confirmation_token: string;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// CHAIN — a complete chain of custody\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * A chain of custody — ties multiple proof events together\r\n * under one verifiable record.\r\n *\r\n * One chain per real-world event:\r\n * - one food delivery\r\n * - one pharmaceutical dispensing\r\n * - one field agent visit\r\n * - one court document service\r\n * - one construction milestone\r\n */\r\nexport interface Chain {\r\n /** Unique chain ID — reference this when calling getChain() or verify() */\r\n chain_id: string;\r\n\r\n /** Human-readable title — e.g. \"Order #1234 — Amaka Johnson\" */\r\n title: string;\r\n\r\n /**\r\n * Current status of the chain:\r\n * - \"open\" → some steps still pending confirmation\r\n * - \"completed\" → all steps confirmed, chain is sealed\r\n */\r\n status: 'open' | 'completed';\r\n\r\n /**\r\n * Public tracking URL — send this to whoever is waiting on the delivery.\r\n * They open it in any browser. No account needed.\r\n * Updates automatically every 15 seconds as steps are confirmed.\r\n */\r\n tracking_url: string;\r\n\r\n /** Raw tracking token embedded in tracking_url */\r\n tracking_token: string;\r\n\r\n /**\r\n * One per actor you defined when creating the chain.\r\n * Send each confirmation_url to the relevant party:\r\n * chain.handoffs[0].confirmation_url → Kitchen\r\n * chain.handoffs[1].confirmation_url → Rider (pickup)\r\n * chain.handoffs[2].confirmation_url → Rider (delivery)\r\n */\r\n handoffs: PendingHandoff[];\r\n\r\n /** ISO timestamp of when this chain was created */\r\n created_at: string;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// CHAIN DETAIL — full audit trail (from getChain())\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Full chain detail including the complete audit trail.\r\n * Returned by getChain() — use this to see the current state\r\n * of every step and all captured evidence.\r\n */\r\nexport interface ChainDetail {\r\n chain_id: string;\r\n title: string;\r\n status: 'open' | 'completed';\r\n metadata: Record<string, any>;\r\n tracking_url: string;\r\n\r\n /** SHA-256 hash of the entire completed chain — null until all steps confirmed */\r\n integrity_hash: string | null;\r\n\r\n /** Whether the chain has been tampered with */\r\n tampered: boolean;\r\n\r\n /**\r\n * True only when:\r\n * - status === \"completed\" (all steps confirmed)\r\n * - tampered === false (no records altered)\r\n */\r\n verified: boolean;\r\n\r\n created_at: string;\r\n\r\n /**\r\n * The complete audit trail — one ProofEvent per actor.\r\n * Each event answers: who, what, when, where, evidence.\r\n */\r\n trail: (ProofEvent & {\r\n id: string;\r\n note: string | null;\r\n /** confirmation_url is null once the step has been confirmed (link is spent) */\r\n confirmation_url: string | null;\r\n })[];\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// VERIFICATION RESULT — from verify()\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Result of calling verify() on a completed chain.\r\n *\r\n * Use this when:\r\n * - A customer disputes a delivery\r\n * - A regulator asks for proof of an event\r\n * - An auditor needs to confirm a record wasn't altered\r\n * - Anyone needs to know: did this actually happen?\r\n *\r\n * verified: true → every step happened as recorded, nothing was altered\r\n * verified: false → chain is incomplete or tampered\r\n */\r\nexport interface VerificationResult {\r\n chain_id: string;\r\n title: string;\r\n status: 'open' | 'completed';\r\n\r\n /**\r\n * The single most important field.\r\n * true = this chain is real, complete, and untampered\r\n * false = incomplete or evidence of tampering\r\n */\r\n verified: boolean;\r\n\r\n /** true if any record in the chain was altered after the fact */\r\n tampered: boolean;\r\n\r\n /** SHA-256 hash of the entire chain — for external audit records */\r\n integrity_hash: string | null;\r\n\r\n /** ISO timestamp of when the final step was confirmed */\r\n completed_at: string | null;\r\n\r\n /** Number of steps confirmed in this chain */\r\n handoff_count: number;\r\n\r\n /**\r\n * The verified audit trail.\r\n * Each entry is a cryptographically verified ProofEvent.\r\n */\r\n trail: (Pick<\r\n ProofEvent,\r\n 'event' | 'actor' | 'confirmed_at' | 'location' | 'hash'\r\n > & {\r\n /** true if this specific step's hash verified correctly */\r\n valid: boolean;\r\n })[];\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// INPUT TYPES — what you pass in\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * One actor in a chain — defines a step that needs to be confirmed.\r\n *\r\n * Use plain language that makes sense for your industry:\r\n * Food: { event: \"packed\", actor: \"Kitchen\" }\r\n * Pharmacy: { event: \"dispensed\", actor: \"Pharmacist\" }\r\n * Banking: { event: \"dispatched\",actor: \"Branch Officer\" }\r\n * Construction: { event: \"milestone_complete\", actor: \"Contractor\" }\r\n * Legal: { event: \"served\", actor: \"Process Server\" }\r\n * Insurance: { event: \"assessed\", actor: \"Claims Assessor\" }\r\n */\r\nexport interface ProofActor {\r\n /** What happens at this step — use plain language from your domain */\r\n event: string;\r\n\r\n /** Who is responsible for confirming this step */\r\n actor: string;\r\n\r\n /** Optional internal note about this step */\r\n note?: string;\r\n}\r\n\r\n/** Input for creating a new chain of custody */\r\nexport interface CreateChainInput {\r\n /**\r\n * Human-readable title for this chain.\r\n * Include enough context to identify it later:\r\n * \"Order #1234 — Amaka Johnson\"\r\n * \"Prescription #5567 — Mrs Obi\"\r\n * \"Site inspection — Plot 14, Lekki\"\r\n */\r\n title: string;\r\n\r\n /**\r\n * Any extra data you want to store with this chain.\r\n * Does not affect the proof logic — purely for your reference.\r\n */\r\n metadata?: Record<string, any>;\r\n\r\n /**\r\n * The steps that need to be confirmed, in order.\r\n * Each one gets its own confirmation_url.\r\n * Steps are confirmed independently — order is tracked by timestamp.\r\n */\r\n actors: ProofActor[];\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// SDK CLIENT\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nexport interface SwiftProofConfig {\r\n /** Your API key — starts with sp_live_ or sp_test_ */\r\n apiKey: string;\r\n\r\n /**\r\n * API base URL.\r\n * Defaults to https://swiftproof-infra.onrender.com\r\n * Override for local development: http://localhost:4000\r\n */\r\n baseUrl?: string;\r\n}\r\n\r\n/**\r\n * SwiftProof SDK\r\n *\r\n * Proof-of-work infrastructure. One API call captures tamper-proof\r\n * evidence of any event in any industry.\r\n *\r\n * @example\r\n * ```typescript\r\n * const sp = new SwiftProof({ apiKey: 'sp_live_xxx' });\r\n *\r\n * const chain = await sp.chains.create({\r\n * title: 'Order #1234 — Amaka Johnson',\r\n * actors: [\r\n * { event: 'packed', actor: 'Kitchen' },\r\n * { event: 'pickup', actor: 'Rider — Emeka' },\r\n * { event: 'delivered', actor: 'Rider — Emeka' },\r\n * ],\r\n * });\r\n *\r\n * // Send these to the relevant parties\r\n * chain.handoffs[0].confirmation_url // → Kitchen\r\n * chain.handoffs[1].confirmation_url // → Rider (pickup)\r\n * chain.handoffs[2].confirmation_url // → Rider (delivery)\r\n *\r\n * // Send this to the customer\r\n * chain.tracking_url\r\n *\r\n * // Later — verify everything happened\r\n * const proof = await sp.chains.verify(chain.chain_id);\r\n * proof.verified // true = happened, untampered\r\n * proof.tampered // false = nothing altered\r\n * ```\r\n */\r\nexport class SwiftProof {\r\n private readonly baseUrl: string;\r\n private readonly headers: Record<string, string>;\r\n\r\n constructor(config: SwiftProofConfig) {\r\n if (!config.apiKey) throw new Error('SwiftProof: apiKey is required');\r\n if (!config.apiKey.startsWith('sp_')) {\r\n throw new Error(\r\n 'SwiftProof: apiKey must start with sp_live_ or sp_test_',\r\n );\r\n }\r\n this.baseUrl = (\r\n config.baseUrl ?? 'https://swiftproof-infra.onrender.com'\r\n ).replace(/\\/$/, '');\r\n this.headers = {\r\n 'Content-Type': 'application/json',\r\n Authorization: `Bearer ${config.apiKey}`,\r\n };\r\n }\r\n\r\n private async request<T>(\r\n method: string,\r\n path: string,\r\n body?: unknown,\r\n ): Promise<T> {\r\n const res = await fetch(`${this.baseUrl}${path}`, {\r\n method,\r\n headers: this.headers,\r\n body: body !== undefined ? JSON.stringify(body) : undefined,\r\n });\r\n\r\n const data = await res.json().catch(() => ({}));\r\n\r\n if (!res.ok) {\r\n const message =\r\n (data as any)?.message ?? `Request failed with status ${res.status}`;\r\n const err = new Error(`SwiftProof API error: ${message}`) as any;\r\n err.status = res.status;\r\n err.response = data;\r\n throw err;\r\n }\r\n\r\n return data as T;\r\n }\r\n\r\n /** All chain of custody operations */\r\n readonly chains = {\r\n /**\r\n * Create a new chain of custody.\r\n *\r\n * Call this when a real-world handoff event begins:\r\n * an order is placed, a delivery is dispatched,\r\n * an inspection is scheduled, a document is to be served.\r\n *\r\n * Returns confirmation URLs for each actor and a\r\n * tracking URL for the end recipient.\r\n *\r\n * @example\r\n * ```typescript\r\n * const chain = await sp.chains.create({\r\n * title: 'Order #1234',\r\n * actors: [\r\n * { event: 'packed', actor: 'Kitchen' },\r\n * { event: 'delivered', actor: 'Rider' },\r\n * ],\r\n * });\r\n *\r\n * // Kitchen taps this link → GPS captured → \"packed\" confirmed\r\n * chain.handoffs[0].confirmation_url\r\n *\r\n * // Customer opens this → sees live status timeline\r\n * chain.tracking_url\r\n * ```\r\n */\r\n create: (input: CreateChainInput): Promise<Chain> =>\r\n this.request<Chain>('POST', '/v1/chains', input),\r\n\r\n /**\r\n * Get the full audit trail for a chain.\r\n *\r\n * Shows every step — confirmed or pending —\r\n * with all captured evidence: GPS, timestamp, photo, hash.\r\n *\r\n * @example\r\n * ```typescript\r\n * const detail = await sp.chains.get('sp_ch_abc123');\r\n * detail.trail.forEach(step => {\r\n * console.log(step.event, step.confirmed_at, step.location);\r\n * });\r\n * ```\r\n */\r\n get: (chainId: string): Promise<ChainDetail> =>\r\n this.request<ChainDetail>('GET', `/v1/chains/${chainId}`),\r\n\r\n /**\r\n * Verify a completed chain of custody.\r\n *\r\n * Recomputes every hash in the chain and confirms nothing\r\n * was altered after the fact.\r\n *\r\n * Use this when:\r\n * - A customer disputes a delivery\r\n * - A regulator asks for proof\r\n * - An auditor needs to confirm records are untampered\r\n *\r\n * @returns VerificationResult with verified: true if chain is\r\n * complete and cryptographically intact\r\n *\r\n * @example\r\n * ```typescript\r\n * const proof = await sp.chains.verify('sp_ch_abc123');\r\n *\r\n * if (proof.verified) {\r\n * // Chain is complete and untampered\r\n * // Show proof.trail as evidence\r\n * } else if (proof.tampered) {\r\n * // Records were altered — do not use as evidence\r\n * } else {\r\n * // Chain not yet complete — some steps pending\r\n * }\r\n * ```\r\n */\r\n verify: (chainId: string): Promise<VerificationResult> =>\r\n this.request<VerificationResult>('GET', `/v1/chains/${chainId}/verify`),\r\n };\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// DEFAULT EXPORT\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nexport default SwiftProof;\r\n"]}
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "swiftproof",
3
+ "version": "0.1.0",
4
+ "description": "SwiftProof chain of custody SDK — plug and play proof infrastructure",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.mjs",
11
+ "require": "./dist/index.js"
12
+ }
13
+ },
14
+ "scripts": {
15
+ "build": "tsup",
16
+ "dev": "tsup --watch"
17
+ },
18
+ "keywords": [
19
+ "chain-of-custody",
20
+ "proof",
21
+ "logistics",
22
+ "delivery",
23
+ "audit-trail"
24
+ ],
25
+ "license": "MIT"
26
+ }
package/src/index.js ADDED
@@ -0,0 +1,130 @@
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 ADDED
@@ -0,0 +1,471 @@
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 ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/src/types.ts ADDED
@@ -0,0 +1,101 @@
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 ADDED
@@ -0,0 +1,19 @@
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 ADDED
@@ -0,0 +1,12 @@
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 ADDED
@@ -0,0 +1,11 @@
1
+ import { defineConfig } from 'tsup';
2
+
3
+ export default defineConfig({
4
+ entry: ['src/index.ts'],
5
+ format: ['cjs', 'esm'],
6
+ dts: false,
7
+ clean: true,
8
+ sourcemap: true,
9
+ treeshake: true,
10
+ tsconfig: './tsconfig.json',
11
+ });