@horae.io/passport-core 1.0.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.cjs +307 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +219 -0
- package/dist/index.d.ts +219 -0
- package/dist/index.js +261 -0
- package/dist/index.js.map +1 -0
- package/package.json +32 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
createClient: () => createClient,
|
|
34
|
+
createClientWithAuth: () => createClientWithAuth,
|
|
35
|
+
createCustomStrategy: () => createCustomStrategy,
|
|
36
|
+
createEventBus: () => createEventBus,
|
|
37
|
+
createJwtStrategy: () => createJwtStrategy,
|
|
38
|
+
createPassportApi: () => createPassportApi,
|
|
39
|
+
createPassportSDK: () => createPassportSDK,
|
|
40
|
+
mountApp: () => mountApp,
|
|
41
|
+
renderPassport: () => renderPassport,
|
|
42
|
+
renderPassportList: () => renderPassportList
|
|
43
|
+
});
|
|
44
|
+
module.exports = __toCommonJS(index_exports);
|
|
45
|
+
|
|
46
|
+
// src/events.ts
|
|
47
|
+
function createEventBus() {
|
|
48
|
+
const listeners = /* @__PURE__ */ new Map();
|
|
49
|
+
function on(event, fn) {
|
|
50
|
+
if (!listeners.has(event)) listeners.set(event, /* @__PURE__ */ new Set());
|
|
51
|
+
listeners.get(event).add(fn);
|
|
52
|
+
return () => listeners.get(event)?.delete(fn);
|
|
53
|
+
}
|
|
54
|
+
function emit(event, payload) {
|
|
55
|
+
listeners.get(event)?.forEach((fn) => fn(payload));
|
|
56
|
+
}
|
|
57
|
+
return { on, emit };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// src/auth/strategies/jwt.ts
|
|
61
|
+
function createJwtStrategy(options = {}) {
|
|
62
|
+
let token = options.token ?? null;
|
|
63
|
+
const storageKey = options.storageKey ?? null;
|
|
64
|
+
const storage = typeof window !== "undefined" ? window.localStorage : void 0;
|
|
65
|
+
if (storage && storageKey) {
|
|
66
|
+
try {
|
|
67
|
+
const stored = storage.getItem(storageKey);
|
|
68
|
+
if (stored) token = stored;
|
|
69
|
+
} catch {
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
async getToken() {
|
|
74
|
+
return token;
|
|
75
|
+
},
|
|
76
|
+
setToken(next) {
|
|
77
|
+
token = next;
|
|
78
|
+
if (storage && storageKey) {
|
|
79
|
+
try {
|
|
80
|
+
if (next) storage.setItem(storageKey, next);
|
|
81
|
+
else storage.removeItem(storageKey);
|
|
82
|
+
} catch {
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
clearToken() {
|
|
87
|
+
token = null;
|
|
88
|
+
if (storage && storageKey) {
|
|
89
|
+
try {
|
|
90
|
+
storage.removeItem(storageKey);
|
|
91
|
+
} catch {
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// src/auth/strategies/custom.ts
|
|
99
|
+
function createCustomStrategy(getToken) {
|
|
100
|
+
return { getToken };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// src/client.ts
|
|
104
|
+
var import_axios = __toESM(require("axios"), 1);
|
|
105
|
+
function createClient(options) {
|
|
106
|
+
const { apiBase, getToken } = options;
|
|
107
|
+
const client = import_axios.default.create({
|
|
108
|
+
baseURL: apiBase.replace(/\/v1\/?$/, "") + "/v1"
|
|
109
|
+
});
|
|
110
|
+
client.interceptors.request.use(async (config) => {
|
|
111
|
+
const token = await getToken();
|
|
112
|
+
if (token) {
|
|
113
|
+
config.headers.Authorization = `Bearer ${token}`;
|
|
114
|
+
}
|
|
115
|
+
config.headers["ngrok-skip-browser-warning"] = "true";
|
|
116
|
+
return config;
|
|
117
|
+
});
|
|
118
|
+
return client;
|
|
119
|
+
}
|
|
120
|
+
function createClientWithAuth(auth, apiBase) {
|
|
121
|
+
return createClient({
|
|
122
|
+
apiBase,
|
|
123
|
+
getToken: () => auth.getToken()
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// src/api.ts
|
|
128
|
+
function unwrapItems(response) {
|
|
129
|
+
const data = response.data;
|
|
130
|
+
if (Array.isArray(data)) return data;
|
|
131
|
+
const inner = data && typeof data === "object" && "data" in data ? data.data : data;
|
|
132
|
+
const items = inner && typeof inner === "object" && "items" in inner ? inner.items : Array.isArray(inner) ? inner : [];
|
|
133
|
+
return items ?? [];
|
|
134
|
+
}
|
|
135
|
+
function unwrapData(response) {
|
|
136
|
+
const data = response.data;
|
|
137
|
+
return data && typeof data === "object" && "data" in data ? data.data : data;
|
|
138
|
+
}
|
|
139
|
+
function createPassportApi(api) {
|
|
140
|
+
return {
|
|
141
|
+
/** List passports claimed by current user */
|
|
142
|
+
async getMyClaimedPassports() {
|
|
143
|
+
const res = await api.get("/api/passport/claimed");
|
|
144
|
+
return unwrapItems(res);
|
|
145
|
+
},
|
|
146
|
+
/** List passports by owner wallet (e.g. Shopify) */
|
|
147
|
+
async getPassportsByOwner(ownerWallet) {
|
|
148
|
+
const res = await api.get("/api/passport/claimed", { params: { owner: ownerWallet } });
|
|
149
|
+
return unwrapItems(res);
|
|
150
|
+
},
|
|
151
|
+
/** Single passport (authenticated) */
|
|
152
|
+
async getPassport(tokenId, isAuthenticated = true) {
|
|
153
|
+
const path = isAuthenticated ? `/api/passport/${tokenId}` : `/api/passport/public/${tokenId}`;
|
|
154
|
+
const res = await api.get(path);
|
|
155
|
+
return unwrapData(res);
|
|
156
|
+
},
|
|
157
|
+
/** Claim passport */
|
|
158
|
+
async claimPassport(data) {
|
|
159
|
+
await api.post("/api/passport/claim", data);
|
|
160
|
+
},
|
|
161
|
+
/** Shopify claim (serialNumber + optional email) */
|
|
162
|
+
async claimShopifyPassport(data) {
|
|
163
|
+
await api.post("/api/passport/shopify-claim", data);
|
|
164
|
+
},
|
|
165
|
+
/** Current user profile */
|
|
166
|
+
async getMe() {
|
|
167
|
+
const res = await api.get("/users/me");
|
|
168
|
+
const raw = unwrapData(res);
|
|
169
|
+
const doc = raw && typeof raw === "object" && "_doc" in raw ? raw._doc : raw;
|
|
170
|
+
return doc;
|
|
171
|
+
},
|
|
172
|
+
/** Documents for a passport (owner gets file URLs) */
|
|
173
|
+
async getDocuments(tokenId) {
|
|
174
|
+
const res = await api.get(`/api/passport/${tokenId}/documents`);
|
|
175
|
+
const responseData = res.data?.data ?? res.data;
|
|
176
|
+
if (responseData && typeof responseData === "object" && "data" in responseData && Array.isArray(responseData.data))
|
|
177
|
+
return responseData.data;
|
|
178
|
+
if (Array.isArray(responseData)) return responseData;
|
|
179
|
+
return [];
|
|
180
|
+
},
|
|
181
|
+
/** Transfer passport to user by email */
|
|
182
|
+
async transferPassportToUser(data) {
|
|
183
|
+
await api.post("/api/passport/transfer-to-user", data);
|
|
184
|
+
},
|
|
185
|
+
/** Set stolen status */
|
|
186
|
+
async setStolenStatus(data) {
|
|
187
|
+
await api.post("/api/passport/set-stolen", data);
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// src/ui.ts
|
|
193
|
+
function prepareContainer(options, apiBase, token) {
|
|
194
|
+
const containerEl = document.querySelector(options.container);
|
|
195
|
+
if (!containerEl) {
|
|
196
|
+
throw new Error(`PassportSDK: container element '${options.container}' not found.`);
|
|
197
|
+
}
|
|
198
|
+
if (containerEl instanceof HTMLElement) {
|
|
199
|
+
containerEl.dataset.horaeApiBase = apiBase;
|
|
200
|
+
if (options.initialTab) {
|
|
201
|
+
containerEl.dataset.initialTab = options.initialTab;
|
|
202
|
+
}
|
|
203
|
+
if (token) {
|
|
204
|
+
containerEl.dataset.horaeToken = token;
|
|
205
|
+
}
|
|
206
|
+
containerEl.classList.add("horae-sdk-root");
|
|
207
|
+
containerEl.dataset.containerSelector = options.container;
|
|
208
|
+
}
|
|
209
|
+
return containerEl;
|
|
210
|
+
}
|
|
211
|
+
function loadUIBundle(uiHost) {
|
|
212
|
+
const SCRIPT_ID = "horae-passport-sdk-ui";
|
|
213
|
+
return new Promise((resolve, reject) => {
|
|
214
|
+
if (document.getElementById(SCRIPT_ID)) {
|
|
215
|
+
resolve();
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
const script = document.createElement("script");
|
|
219
|
+
script.id = SCRIPT_ID;
|
|
220
|
+
script.type = "module";
|
|
221
|
+
script.src = `${uiHost.replace(/\/$/, "")}/sdk-embed.js`;
|
|
222
|
+
script.onload = () => resolve();
|
|
223
|
+
script.onerror = () => reject(new Error(`PassportSDK: Failed to load UI bundle from ${script.src}`));
|
|
224
|
+
document.head.appendChild(script);
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
async function mountApp(options, apiBase, token) {
|
|
228
|
+
prepareContainer(options, apiBase, token);
|
|
229
|
+
const host = options.uiHost || "https://app.horae.io";
|
|
230
|
+
await loadUIBundle(host);
|
|
231
|
+
window.dispatchEvent(new CustomEvent("horae:sdk:mount", {
|
|
232
|
+
detail: { container: options.container }
|
|
233
|
+
}));
|
|
234
|
+
}
|
|
235
|
+
async function renderPassportList(options, apiBase, token) {
|
|
236
|
+
return mountApp({ ...options, initialTab: "passports" }, apiBase, token);
|
|
237
|
+
}
|
|
238
|
+
async function renderPassport(options, apiBase, token) {
|
|
239
|
+
const containerEl = prepareContainer(options, apiBase, token);
|
|
240
|
+
if (containerEl instanceof HTMLElement) {
|
|
241
|
+
containerEl.dataset.passportId = options.passportId;
|
|
242
|
+
containerEl.dataset.initialTab = "detail";
|
|
243
|
+
}
|
|
244
|
+
const host = options.uiHost || "https://app.horae.io";
|
|
245
|
+
await loadUIBundle(host);
|
|
246
|
+
window.dispatchEvent(new CustomEvent("horae:sdk:mount", {
|
|
247
|
+
detail: { container: options.container, passportId: options.passportId }
|
|
248
|
+
}));
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// src/index.ts
|
|
252
|
+
function createPassportSDK(config) {
|
|
253
|
+
const events = createEventBus();
|
|
254
|
+
let auth;
|
|
255
|
+
switch (config.authStrategy) {
|
|
256
|
+
case "jwt": {
|
|
257
|
+
const jwt = createJwtStrategy({
|
|
258
|
+
token: config.token ?? void 0,
|
|
259
|
+
storageKey: config.tokenStorageKey ?? void 0
|
|
260
|
+
});
|
|
261
|
+
auth = jwt;
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
|
+
case "custom": {
|
|
265
|
+
if (!config.getToken) throw new Error("PassportSDK: getToken required for authStrategy 'custom'");
|
|
266
|
+
auth = createCustomStrategy(config.getToken);
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
case "shopify":
|
|
270
|
+
auth = createJwtStrategy({
|
|
271
|
+
token: config.token ?? void 0,
|
|
272
|
+
storageKey: config.tokenStorageKey ?? "shopify_access_token"
|
|
273
|
+
});
|
|
274
|
+
break;
|
|
275
|
+
case "privy":
|
|
276
|
+
if (!config.getToken) throw new Error("PassportSDK: getToken required for authStrategy 'privy' (pass Privy getAccessToken)");
|
|
277
|
+
auth = createCustomStrategy(config.getToken);
|
|
278
|
+
break;
|
|
279
|
+
default:
|
|
280
|
+
throw new Error(`PassportSDK: unknown authStrategy ${String(config.authStrategy)}`);
|
|
281
|
+
}
|
|
282
|
+
const client = createClient({ apiBase: config.apiBase, getToken: () => auth.getToken() });
|
|
283
|
+
const api = createPassportApi(client);
|
|
284
|
+
return {
|
|
285
|
+
api,
|
|
286
|
+
events,
|
|
287
|
+
auth,
|
|
288
|
+
config: Object.freeze({ ...config }),
|
|
289
|
+
mount: (options) => mountApp(options, config.apiBase, config.token || null),
|
|
290
|
+
renderPassportList: (options) => renderPassportList(options, config.apiBase, config.token || null),
|
|
291
|
+
renderPassport: (options) => renderPassport(options, config.apiBase, config.token || null)
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
295
|
+
0 && (module.exports = {
|
|
296
|
+
createClient,
|
|
297
|
+
createClientWithAuth,
|
|
298
|
+
createCustomStrategy,
|
|
299
|
+
createEventBus,
|
|
300
|
+
createJwtStrategy,
|
|
301
|
+
createPassportApi,
|
|
302
|
+
createPassportSDK,
|
|
303
|
+
mountApp,
|
|
304
|
+
renderPassport,
|
|
305
|
+
renderPassportList
|
|
306
|
+
});
|
|
307
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/events.ts","../src/auth/strategies/jwt.ts","../src/auth/strategies/custom.ts","../src/client.ts","../src/api.ts","../src/ui.ts"],"sourcesContent":["/**\n * @horae/passport-core\n * Reusable Passport SDK — auth strategies, Horae API client, events.\n * Token-based auth only; no cross-domain cookies.\n */\n\nexport type { PassportSDKConfig, AuthStrategy, PassportListItem, PassportDetail, User } from \"./types.js\";\nexport { createEventBus } from \"./events.js\";\nexport type { SDKEvent, EventMap, EventBus } from \"./events.js\";\nexport { createJwtStrategy, createCustomStrategy } from \"./auth/index.js\";\nexport type { AuthStrategyBase } from \"./auth/index.js\";\nexport { createClient, createClientWithAuth } from \"./client.js\";\nexport type { CreateClientOptions } from \"./client.js\";\nexport { createPassportApi } from \"./api.js\";\nexport * from \"./ui.js\";\n\nimport type { PassportSDKConfig } from \"./types.js\";\nimport { createEventBus } from \"./events.js\";\nimport { createJwtStrategy } from \"./auth/strategies/jwt.js\";\nimport { createCustomStrategy } from \"./auth/strategies/custom.js\";\nimport { createClient } from \"./client.js\";\nimport { createPassportApi } from \"./api.js\";\nimport type { AuthStrategyBase } from \"./auth/index.js\";\nimport { mountApp, renderPassportList, renderPassport } from \"./ui.js\";\nimport type { RenderOptions, RenderPassportOptions } from \"./ui.js\";\n\nexport interface PassportSDK {\n /** Horae API client (axios) */\n readonly api: ReturnType<typeof createPassportApi>;\n /** Event bus for auth:ready, auth:error, passport:claimed, etc. */\n readonly events: ReturnType<typeof createEventBus>;\n /** Auth strategy instance (for setToken/clearToken when available) */\n readonly auth: AuthStrategyBase;\n /** Config */\n readonly config: Readonly<PassportSDKConfig>;\n /** Mount the generic SDK App in headless mode */\n mount(options: RenderOptions): Promise<void>;\n /** Render the Passport List view */\n renderPassportList(options: RenderOptions): Promise<void>;\n /** Render a Single Passport Detail view */\n renderPassport(options: RenderPassportOptions): Promise<void>;\n}\n\n/**\n * Create the Passport SDK instance.\n * - authStrategy \"jwt\": pass token in config or call setToken on auth after getting JWT from your backend.\n * - authStrategy \"custom\": pass getToken in config.\n * - authStrategy \"shopify\": use @horae/passport-shopify to get JWT from bridge, then use jwt or custom.\n */\nexport function createPassportSDK(config: PassportSDKConfig): PassportSDK {\n const events = createEventBus();\n let auth: AuthStrategyBase;\n\n switch (config.authStrategy) {\n case \"jwt\": {\n const jwt = createJwtStrategy({\n token: config.token ?? undefined,\n storageKey: config.tokenStorageKey ?? undefined,\n });\n auth = jwt;\n break;\n }\n case \"custom\": {\n if (!config.getToken) throw new Error(\"PassportSDK: getToken required for authStrategy 'custom'\");\n auth = createCustomStrategy(config.getToken);\n break;\n }\n case \"shopify\":\n // Shopify: use JWT strategy; token is set by @horae/passport-shopify bridge\n auth = createJwtStrategy({\n token: config.token ?? undefined,\n storageKey: config.tokenStorageKey ?? \"shopify_access_token\",\n });\n break;\n case \"privy\":\n // Privy: use custom strategy with getToken from host (e.g. getAccessToken from @privy-io/react-auth)\n if (!config.getToken) throw new Error(\"PassportSDK: getToken required for authStrategy 'privy' (pass Privy getAccessToken)\");\n auth = createCustomStrategy(config.getToken);\n break;\n default:\n throw new Error(`PassportSDK: unknown authStrategy ${String((config as { authStrategy?: string }).authStrategy)}`);\n }\n\n const client = createClient({ apiBase: config.apiBase, getToken: () => auth.getToken() });\n const api = createPassportApi(client);\n\n return {\n api,\n events,\n auth,\n config: Object.freeze({ ...config }),\n mount: (options) => mountApp(options, config.apiBase, config.token || null),\n renderPassportList: (options) => renderPassportList(options, config.apiBase, config.token || null),\n renderPassport: (options) => renderPassport(options, config.apiBase, config.token || null),\n };\n}\n","/**\n * @horae/passport-core — event emitter for auth and passport events\n */\n\nexport type SDKEvent =\n | \"auth:ready\"\n | \"auth:error\"\n | \"passport:claimed\"\n | \"passport:loaded\"\n | \"passport:list:loaded\"\n | \"token:expired\";\n\nexport type EventMap = {\n \"auth:ready\": void;\n \"auth:error\": { message: string; code?: string };\n \"passport:claimed\": { tokenId: string };\n \"passport:loaded\": { tokenId: string };\n \"passport:list:loaded\": { count: number };\n \"token:expired\": void;\n};\n\ntype Listener<T> = (payload: T) => void;\n\nexport function createEventBus() {\n const listeners = new Map<SDKEvent, Set<Listener<unknown>>>();\n\n function on<E extends SDKEvent>(event: E, fn: Listener<EventMap[E]>) {\n if (!listeners.has(event)) listeners.set(event, new Set());\n listeners.get(event)!.add(fn as Listener<unknown>);\n return () => listeners.get(event)?.delete(fn as Listener<unknown>);\n }\n\n function emit<E extends SDKEvent>(event: E, payload?: EventMap[E]) {\n listeners.get(event)?.forEach((fn) => (fn as Listener<EventMap[E]>)(payload as EventMap[E]));\n }\n\n return { on, emit };\n}\n\nexport type EventBus = ReturnType<typeof createEventBus>;\n","/**\n * JWT strategy — token from config or setToken; optional localStorage persistence\n */\n\nimport type { AuthStrategyBase } from \"../types.js\";\n\nexport interface JwtStrategyOptions {\n /** Initial token */\n token?: string | null;\n /** localStorage key for persistence (omit for in-memory only) */\n storageKey?: string | null;\n}\n\nexport function createJwtStrategy(options: JwtStrategyOptions = {}): AuthStrategyBase {\n let token: string | null = options.token ?? null;\n const storageKey = options.storageKey ?? null;\n\n const storage = typeof window !== \"undefined\" ? window.localStorage : undefined;\n if (storage && storageKey) {\n try {\n const stored = storage.getItem(storageKey);\n if (stored) token = stored;\n } catch {\n // ignore\n }\n }\n\n return {\n async getToken() {\n return token;\n },\n setToken(next: string | null) {\n token = next;\n if (storage && storageKey) {\n try {\n if (next) storage.setItem(storageKey, next);\n else storage.removeItem(storageKey);\n } catch {\n // ignore\n }\n }\n },\n clearToken() {\n token = null;\n if (storage && storageKey) {\n try {\n storage.removeItem(storageKey);\n } catch {\n // ignore\n }\n }\n },\n };\n}\n","/**\n * Custom strategy — caller supplies getToken (e.g. from backend or Privy)\n */\n\nimport type { AuthStrategyBase } from \"../types.js\";\n\nexport function createCustomStrategy(\n getToken: () => Promise<string | null>,\n): AuthStrategyBase {\n return { getToken };\n}\n","/**\n * Horae API client — axios instance with Bearer token from auth strategy\n */\n\nimport axios, { type AxiosInstance } from \"axios\";\nimport type { AuthStrategyBase } from \"./auth/index.js\";\n\nexport interface CreateClientOptions {\n apiBase: string;\n getToken: () => Promise<string | null>;\n}\n\nexport function createClient(options: CreateClientOptions): AxiosInstance {\n const { apiBase, getToken } = options;\n const client = axios.create({\n baseURL: apiBase.replace(/\\/v1\\/?$/, \"\") + \"/v1\",\n });\n\n client.interceptors.request.use(async (config) => {\n const token = await getToken();\n if (token) {\n config.headers.Authorization = `Bearer ${token}`;\n }\n config.headers[\"ngrok-skip-browser-warning\"] = \"true\";\n return config;\n });\n\n return client;\n}\n\nexport function createClientWithAuth(auth: AuthStrategyBase, apiBase: string): AxiosInstance {\n return createClient({\n apiBase,\n getToken: () => auth.getToken(),\n });\n}\n","/**\n * Horae Passport API — list, single, claim, user, documents\n * Mirrors passport-web shared/api/api.service.ts (token-based auth)\n */\n\nimport type { AxiosInstance } from \"axios\";\nimport type { PassportListItem, PassportDetail, User } from \"./types.js\";\n\nfunction unwrapItems<T>(response: { data?: unknown }): T[] {\n const data = response.data as { data?: { items?: T[] }; items?: T[] } | T[] | undefined;\n if (Array.isArray(data)) return data;\n const inner = data && typeof data === \"object\" && \"data\" in data ? (data as { data?: { items?: T[] } }).data : data;\n const items = (inner && typeof inner === \"object\" && \"items\" in inner\n ? (inner as { items?: T[] }).items\n : Array.isArray(inner)\n ? inner\n : []) as T[];\n return items ?? [];\n}\n\nfunction unwrapData<T>(response: { data?: unknown }): T {\n const data = response.data as { data?: T } | T;\n return (data && typeof data === \"object\" && \"data\" in data ? (data as { data: T }).data : data) as T;\n}\n\nexport function createPassportApi(api: AxiosInstance) {\n return {\n /** List passports claimed by current user */\n async getMyClaimedPassports(): Promise<PassportListItem[]> {\n const res = await api.get(\"/api/passport/claimed\");\n return unwrapItems<PassportListItem>(res) as PassportListItem[];\n },\n\n /** List passports by owner wallet (e.g. Shopify) */\n async getPassportsByOwner(ownerWallet: string): Promise<PassportListItem[]> {\n const res = await api.get(\"/api/passport/claimed\", { params: { owner: ownerWallet } });\n return unwrapItems<PassportListItem>(res) as PassportListItem[];\n },\n\n /** Single passport (authenticated) */\n async getPassport(tokenId: string, isAuthenticated = true): Promise<PassportDetail> {\n const path = isAuthenticated ? `/api/passport/${tokenId}` : `/api/passport/public/${tokenId}`;\n const res = await api.get(path);\n return unwrapData<PassportDetail>(res);\n },\n\n /** Claim passport */\n async claimPassport(data: { tokenId: string; toAddress: string; claimKey: string }) {\n await api.post(\"/api/passport/claim\", data);\n },\n\n /** Shopify claim (serialNumber + optional email) */\n async claimShopifyPassport(data: { serialNumber: string; email?: string }) {\n await api.post(\"/api/passport/shopify-claim\", data);\n },\n\n /** Current user profile */\n async getMe(): Promise<User> {\n const res = await api.get(\"/users/me\");\n const raw = unwrapData<unknown>(res);\n const doc = raw && typeof raw === \"object\" && \"_doc\" in raw ? (raw as { _doc: User })._doc : raw;\n return doc as User;\n },\n\n /** Documents for a passport (owner gets file URLs) */\n async getDocuments(tokenId: string): Promise<{ url?: string; name?: string; [k: string]: unknown }[]> {\n const res = await api.get(`/api/passport/${tokenId}/documents`);\n const responseData = (res.data as { data?: unknown })?.data ?? res.data;\n if (responseData && typeof responseData === \"object\" && \"data\" in responseData && Array.isArray((responseData as { data: unknown[] }).data))\n return (responseData as { data: unknown[] }).data as { url?: string; name?: string; [k: string]: unknown }[];\n if (Array.isArray(responseData)) return responseData as { url?: string; name?: string; [k: string]: unknown }[];\n return [];\n },\n\n /** Transfer passport to user by email */\n async transferPassportToUser(data: { tokenId: string; recipientEmail: string }) {\n await api.post(\"/api/passport/transfer-to-user\", data);\n },\n\n /** Set stolen status */\n async setStolenStatus(data: { tokenId: string; isStolen: boolean }) {\n await api.post(\"/api/passport/set-stolen\", data);\n },\n };\n}\n","/**\n * UI Renderer for @horae/passport-core\n * Dynamically injects the pre-built React SDK bundle to render the UI on the host page.\n */\n\nexport interface RenderOptions {\n /** CSS selector of the container element (e.g. \"#passport-app\") */\n container: string;\n /** UI Host base URL where the bundle is served (default: https://app.horae.io) */\n uiHost?: string;\n /** Initial tab/view (e.g. \"passports\", \"claim\", \"settings\") */\n initialTab?: string;\n}\n\nexport interface RenderPassportOptions extends RenderOptions {\n /** The ID of the passport to show */\n passportId: string;\n}\n\n/**\n * Configure the container with data attributes so the UI bundle can read them upon mounting.\n */\nfunction prepareContainer(options: RenderOptions, apiBase: string, token: string | null) {\n const containerEl = document.querySelector(options.container);\n if (!containerEl) {\n throw new Error(`PassportSDK: container element '${options.container}' not found.`);\n }\n\n // Set necessary data attributes for the UI bundle (similar to Shopify embed contract)\n if (containerEl instanceof HTMLElement) {\n containerEl.dataset.horaeApiBase = apiBase;\n if (options.initialTab) {\n containerEl.dataset.initialTab = options.initialTab;\n }\n\n // Inject the token (the UI bundle will look for this or use its own mechanism)\n if (token) {\n containerEl.dataset.horaeToken = token;\n }\n\n // Add a signature class/id to ensure the embed root is identifiable\n containerEl.classList.add(\"horae-sdk-root\");\n\n // Allow the bundle to easily find its mount point if it uses querySelector internally\n // (We also pass the container selector directly to the mount event later if needed)\n containerEl.dataset.containerSelector = options.container;\n }\n\n return containerEl;\n}\n\n/**\n * Inject the script tag for the UI bundle if it hasn't been injected yet.\n */\nfunction loadUIBundle(uiHost: string): Promise<void> {\n const SCRIPT_ID = \"horae-passport-sdk-ui\";\n\n return new Promise((resolve, reject) => {\n if (document.getElementById(SCRIPT_ID)) {\n // Script already added\n resolve();\n return;\n }\n\n const script = document.createElement(\"script\");\n script.id = SCRIPT_ID;\n script.type = \"module\";\n // Target the newly created sdk-embed entrypoint\n script.src = `${uiHost.replace(/\\/$/, '')}/sdk-embed.js`;\n\n script.onload = () => resolve();\n script.onerror = () => reject(new Error(`PassportSDK: Failed to load UI bundle from ${script.src}`));\n\n document.head.appendChild(script);\n });\n}\n\n/**\n * Render the generic SDK App\n */\nexport async function mountApp(\n options: RenderOptions,\n apiBase: string,\n token: string | null\n): Promise<void> {\n prepareContainer(options, apiBase, token);\n\n const host = options.uiHost || \"https://app.horae.io\";\n await loadUIBundle(host);\n\n // Dispatch a custom event in case the bundle is already loaded and listening for dynamic mounts\n window.dispatchEvent(new CustomEvent(\"horae:sdk:mount\", {\n detail: { container: options.container }\n }));\n}\n\n/**\n * Render specifically the Passport List (maps to \"passports\" tab/view)\n */\nexport async function renderPassportList(\n options: RenderOptions,\n apiBase: string,\n token: string | null\n): Promise<void> {\n return mountApp({ ...options, initialTab: \"passports\" }, apiBase, token);\n}\n\n/**\n * Render specifically a Single Passport (maps to a specific detail view layout)\n */\nexport async function renderPassport(\n options: RenderPassportOptions,\n apiBase: string,\n token: string | null\n): Promise<void> {\n const containerEl = prepareContainer(options, apiBase, token);\n\n if (containerEl instanceof HTMLElement) {\n containerEl.dataset.passportId = options.passportId;\n // Set initialTab to \"detail\" or a specific flag the UI recognizes\n containerEl.dataset.initialTab = \"detail\";\n }\n\n const host = options.uiHost || \"https://app.horae.io\";\n await loadUIBundle(host);\n\n window.dispatchEvent(new CustomEvent(\"horae:sdk:mount\", {\n detail: { container: options.container, passportId: options.passportId }\n }));\n}\n"],"mappings":"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","names":["axios"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { AxiosInstance } from 'axios';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @horae/passport-core — shared types
|
|
5
|
+
*/
|
|
6
|
+
type AuthStrategy = "jwt" | "shopify" | "privy" | "custom";
|
|
7
|
+
interface PassportSDKConfig {
|
|
8
|
+
/** Horae API base URL (e.g. https://v2.api.dev.horae.io/v1) */
|
|
9
|
+
apiBase: string;
|
|
10
|
+
/** Auth strategy */
|
|
11
|
+
authStrategy: AuthStrategy;
|
|
12
|
+
/** For custom strategy: async token getter */
|
|
13
|
+
getToken?: () => Promise<string | null>;
|
|
14
|
+
/** For JWT strategy: initial token (optional; can set later via setToken) */
|
|
15
|
+
token?: string | null;
|
|
16
|
+
/** Store token in localStorage under this key (omit for in-memory only) */
|
|
17
|
+
tokenStorageKey?: string | null;
|
|
18
|
+
/** UI Host base URL where the bundle is served (default: https://app.horae.io) */
|
|
19
|
+
uiHost?: string;
|
|
20
|
+
}
|
|
21
|
+
interface PassportListItem {
|
|
22
|
+
id: string;
|
|
23
|
+
tokenId?: string;
|
|
24
|
+
brandLogo: string;
|
|
25
|
+
brand?: string;
|
|
26
|
+
productImage: string;
|
|
27
|
+
name: string;
|
|
28
|
+
description: string;
|
|
29
|
+
[key: string]: unknown;
|
|
30
|
+
}
|
|
31
|
+
interface PassportDetail {
|
|
32
|
+
id: string;
|
|
33
|
+
tokenId?: string;
|
|
34
|
+
brandLogo: string;
|
|
35
|
+
brand?: string;
|
|
36
|
+
productImage: string;
|
|
37
|
+
name: string;
|
|
38
|
+
description: string;
|
|
39
|
+
[key: string]: unknown;
|
|
40
|
+
}
|
|
41
|
+
interface User {
|
|
42
|
+
id?: string;
|
|
43
|
+
email?: string;
|
|
44
|
+
walletAddress?: string;
|
|
45
|
+
[key: string]: unknown;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @horae/passport-core — event emitter for auth and passport events
|
|
50
|
+
*/
|
|
51
|
+
type SDKEvent = "auth:ready" | "auth:error" | "passport:claimed" | "passport:loaded" | "passport:list:loaded" | "token:expired";
|
|
52
|
+
type EventMap = {
|
|
53
|
+
"auth:ready": void;
|
|
54
|
+
"auth:error": {
|
|
55
|
+
message: string;
|
|
56
|
+
code?: string;
|
|
57
|
+
};
|
|
58
|
+
"passport:claimed": {
|
|
59
|
+
tokenId: string;
|
|
60
|
+
};
|
|
61
|
+
"passport:loaded": {
|
|
62
|
+
tokenId: string;
|
|
63
|
+
};
|
|
64
|
+
"passport:list:loaded": {
|
|
65
|
+
count: number;
|
|
66
|
+
};
|
|
67
|
+
"token:expired": void;
|
|
68
|
+
};
|
|
69
|
+
type Listener<T> = (payload: T) => void;
|
|
70
|
+
declare function createEventBus(): {
|
|
71
|
+
on: <E extends SDKEvent>(event: E, fn: Listener<EventMap[E]>) => () => boolean | undefined;
|
|
72
|
+
emit: <E extends SDKEvent>(event: E, payload?: EventMap[E]) => void;
|
|
73
|
+
};
|
|
74
|
+
type EventBus = ReturnType<typeof createEventBus>;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Auth strategy contracts — token-based only, no cross-domain cookies
|
|
78
|
+
*/
|
|
79
|
+
interface AuthStrategyBase {
|
|
80
|
+
/** Return current Bearer token for API calls */
|
|
81
|
+
getToken(): Promise<string | null>;
|
|
82
|
+
/** Optional: set token (e.g. after bridge call) */
|
|
83
|
+
setToken?(token: string | null): void | Promise<void>;
|
|
84
|
+
/** Optional: clear token on logout */
|
|
85
|
+
clearToken?(): void | Promise<void>;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* JWT strategy — token from config or setToken; optional localStorage persistence
|
|
90
|
+
*/
|
|
91
|
+
|
|
92
|
+
interface JwtStrategyOptions {
|
|
93
|
+
/** Initial token */
|
|
94
|
+
token?: string | null;
|
|
95
|
+
/** localStorage key for persistence (omit for in-memory only) */
|
|
96
|
+
storageKey?: string | null;
|
|
97
|
+
}
|
|
98
|
+
declare function createJwtStrategy(options?: JwtStrategyOptions): AuthStrategyBase;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Custom strategy — caller supplies getToken (e.g. from backend or Privy)
|
|
102
|
+
*/
|
|
103
|
+
|
|
104
|
+
declare function createCustomStrategy(getToken: () => Promise<string | null>): AuthStrategyBase;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Horae API client — axios instance with Bearer token from auth strategy
|
|
108
|
+
*/
|
|
109
|
+
|
|
110
|
+
interface CreateClientOptions {
|
|
111
|
+
apiBase: string;
|
|
112
|
+
getToken: () => Promise<string | null>;
|
|
113
|
+
}
|
|
114
|
+
declare function createClient(options: CreateClientOptions): AxiosInstance;
|
|
115
|
+
declare function createClientWithAuth(auth: AuthStrategyBase, apiBase: string): AxiosInstance;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Horae Passport API — list, single, claim, user, documents
|
|
119
|
+
* Mirrors passport-web shared/api/api.service.ts (token-based auth)
|
|
120
|
+
*/
|
|
121
|
+
|
|
122
|
+
declare function createPassportApi(api: AxiosInstance): {
|
|
123
|
+
/** List passports claimed by current user */
|
|
124
|
+
getMyClaimedPassports(): Promise<PassportListItem[]>;
|
|
125
|
+
/** List passports by owner wallet (e.g. Shopify) */
|
|
126
|
+
getPassportsByOwner(ownerWallet: string): Promise<PassportListItem[]>;
|
|
127
|
+
/** Single passport (authenticated) */
|
|
128
|
+
getPassport(tokenId: string, isAuthenticated?: boolean): Promise<PassportDetail>;
|
|
129
|
+
/** Claim passport */
|
|
130
|
+
claimPassport(data: {
|
|
131
|
+
tokenId: string;
|
|
132
|
+
toAddress: string;
|
|
133
|
+
claimKey: string;
|
|
134
|
+
}): Promise<void>;
|
|
135
|
+
/** Shopify claim (serialNumber + optional email) */
|
|
136
|
+
claimShopifyPassport(data: {
|
|
137
|
+
serialNumber: string;
|
|
138
|
+
email?: string;
|
|
139
|
+
}): Promise<void>;
|
|
140
|
+
/** Current user profile */
|
|
141
|
+
getMe(): Promise<User>;
|
|
142
|
+
/** Documents for a passport (owner gets file URLs) */
|
|
143
|
+
getDocuments(tokenId: string): Promise<{
|
|
144
|
+
url?: string;
|
|
145
|
+
name?: string;
|
|
146
|
+
[k: string]: unknown;
|
|
147
|
+
}[]>;
|
|
148
|
+
/** Transfer passport to user by email */
|
|
149
|
+
transferPassportToUser(data: {
|
|
150
|
+
tokenId: string;
|
|
151
|
+
recipientEmail: string;
|
|
152
|
+
}): Promise<void>;
|
|
153
|
+
/** Set stolen status */
|
|
154
|
+
setStolenStatus(data: {
|
|
155
|
+
tokenId: string;
|
|
156
|
+
isStolen: boolean;
|
|
157
|
+
}): Promise<void>;
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* UI Renderer for @horae/passport-core
|
|
162
|
+
* Dynamically injects the pre-built React SDK bundle to render the UI on the host page.
|
|
163
|
+
*/
|
|
164
|
+
interface RenderOptions {
|
|
165
|
+
/** CSS selector of the container element (e.g. "#passport-app") */
|
|
166
|
+
container: string;
|
|
167
|
+
/** UI Host base URL where the bundle is served (default: https://app.horae.io) */
|
|
168
|
+
uiHost?: string;
|
|
169
|
+
/** Initial tab/view (e.g. "passports", "claim", "settings") */
|
|
170
|
+
initialTab?: string;
|
|
171
|
+
}
|
|
172
|
+
interface RenderPassportOptions extends RenderOptions {
|
|
173
|
+
/** The ID of the passport to show */
|
|
174
|
+
passportId: string;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Render the generic SDK App
|
|
178
|
+
*/
|
|
179
|
+
declare function mountApp(options: RenderOptions, apiBase: string, token: string | null): Promise<void>;
|
|
180
|
+
/**
|
|
181
|
+
* Render specifically the Passport List (maps to "passports" tab/view)
|
|
182
|
+
*/
|
|
183
|
+
declare function renderPassportList(options: RenderOptions, apiBase: string, token: string | null): Promise<void>;
|
|
184
|
+
/**
|
|
185
|
+
* Render specifically a Single Passport (maps to a specific detail view layout)
|
|
186
|
+
*/
|
|
187
|
+
declare function renderPassport(options: RenderPassportOptions, apiBase: string, token: string | null): Promise<void>;
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* @horae/passport-core
|
|
191
|
+
* Reusable Passport SDK — auth strategies, Horae API client, events.
|
|
192
|
+
* Token-based auth only; no cross-domain cookies.
|
|
193
|
+
*/
|
|
194
|
+
|
|
195
|
+
interface PassportSDK {
|
|
196
|
+
/** Horae API client (axios) */
|
|
197
|
+
readonly api: ReturnType<typeof createPassportApi>;
|
|
198
|
+
/** Event bus for auth:ready, auth:error, passport:claimed, etc. */
|
|
199
|
+
readonly events: ReturnType<typeof createEventBus>;
|
|
200
|
+
/** Auth strategy instance (for setToken/clearToken when available) */
|
|
201
|
+
readonly auth: AuthStrategyBase;
|
|
202
|
+
/** Config */
|
|
203
|
+
readonly config: Readonly<PassportSDKConfig>;
|
|
204
|
+
/** Mount the generic SDK App in headless mode */
|
|
205
|
+
mount(options: RenderOptions): Promise<void>;
|
|
206
|
+
/** Render the Passport List view */
|
|
207
|
+
renderPassportList(options: RenderOptions): Promise<void>;
|
|
208
|
+
/** Render a Single Passport Detail view */
|
|
209
|
+
renderPassport(options: RenderPassportOptions): Promise<void>;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Create the Passport SDK instance.
|
|
213
|
+
* - authStrategy "jwt": pass token in config or call setToken on auth after getting JWT from your backend.
|
|
214
|
+
* - authStrategy "custom": pass getToken in config.
|
|
215
|
+
* - authStrategy "shopify": use @horae/passport-shopify to get JWT from bridge, then use jwt or custom.
|
|
216
|
+
*/
|
|
217
|
+
declare function createPassportSDK(config: PassportSDKConfig): PassportSDK;
|
|
218
|
+
|
|
219
|
+
export { type AuthStrategy, type AuthStrategyBase, type CreateClientOptions, type EventBus, type EventMap, type PassportDetail, type PassportListItem, type PassportSDK, type PassportSDKConfig, type RenderOptions, type RenderPassportOptions, type SDKEvent, type User, createClient, createClientWithAuth, createCustomStrategy, createEventBus, createJwtStrategy, createPassportApi, createPassportSDK, mountApp, renderPassport, renderPassportList };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { AxiosInstance } from 'axios';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @horae/passport-core — shared types
|
|
5
|
+
*/
|
|
6
|
+
type AuthStrategy = "jwt" | "shopify" | "privy" | "custom";
|
|
7
|
+
interface PassportSDKConfig {
|
|
8
|
+
/** Horae API base URL (e.g. https://v2.api.dev.horae.io/v1) */
|
|
9
|
+
apiBase: string;
|
|
10
|
+
/** Auth strategy */
|
|
11
|
+
authStrategy: AuthStrategy;
|
|
12
|
+
/** For custom strategy: async token getter */
|
|
13
|
+
getToken?: () => Promise<string | null>;
|
|
14
|
+
/** For JWT strategy: initial token (optional; can set later via setToken) */
|
|
15
|
+
token?: string | null;
|
|
16
|
+
/** Store token in localStorage under this key (omit for in-memory only) */
|
|
17
|
+
tokenStorageKey?: string | null;
|
|
18
|
+
/** UI Host base URL where the bundle is served (default: https://app.horae.io) */
|
|
19
|
+
uiHost?: string;
|
|
20
|
+
}
|
|
21
|
+
interface PassportListItem {
|
|
22
|
+
id: string;
|
|
23
|
+
tokenId?: string;
|
|
24
|
+
brandLogo: string;
|
|
25
|
+
brand?: string;
|
|
26
|
+
productImage: string;
|
|
27
|
+
name: string;
|
|
28
|
+
description: string;
|
|
29
|
+
[key: string]: unknown;
|
|
30
|
+
}
|
|
31
|
+
interface PassportDetail {
|
|
32
|
+
id: string;
|
|
33
|
+
tokenId?: string;
|
|
34
|
+
brandLogo: string;
|
|
35
|
+
brand?: string;
|
|
36
|
+
productImage: string;
|
|
37
|
+
name: string;
|
|
38
|
+
description: string;
|
|
39
|
+
[key: string]: unknown;
|
|
40
|
+
}
|
|
41
|
+
interface User {
|
|
42
|
+
id?: string;
|
|
43
|
+
email?: string;
|
|
44
|
+
walletAddress?: string;
|
|
45
|
+
[key: string]: unknown;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @horae/passport-core — event emitter for auth and passport events
|
|
50
|
+
*/
|
|
51
|
+
type SDKEvent = "auth:ready" | "auth:error" | "passport:claimed" | "passport:loaded" | "passport:list:loaded" | "token:expired";
|
|
52
|
+
type EventMap = {
|
|
53
|
+
"auth:ready": void;
|
|
54
|
+
"auth:error": {
|
|
55
|
+
message: string;
|
|
56
|
+
code?: string;
|
|
57
|
+
};
|
|
58
|
+
"passport:claimed": {
|
|
59
|
+
tokenId: string;
|
|
60
|
+
};
|
|
61
|
+
"passport:loaded": {
|
|
62
|
+
tokenId: string;
|
|
63
|
+
};
|
|
64
|
+
"passport:list:loaded": {
|
|
65
|
+
count: number;
|
|
66
|
+
};
|
|
67
|
+
"token:expired": void;
|
|
68
|
+
};
|
|
69
|
+
type Listener<T> = (payload: T) => void;
|
|
70
|
+
declare function createEventBus(): {
|
|
71
|
+
on: <E extends SDKEvent>(event: E, fn: Listener<EventMap[E]>) => () => boolean | undefined;
|
|
72
|
+
emit: <E extends SDKEvent>(event: E, payload?: EventMap[E]) => void;
|
|
73
|
+
};
|
|
74
|
+
type EventBus = ReturnType<typeof createEventBus>;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Auth strategy contracts — token-based only, no cross-domain cookies
|
|
78
|
+
*/
|
|
79
|
+
interface AuthStrategyBase {
|
|
80
|
+
/** Return current Bearer token for API calls */
|
|
81
|
+
getToken(): Promise<string | null>;
|
|
82
|
+
/** Optional: set token (e.g. after bridge call) */
|
|
83
|
+
setToken?(token: string | null): void | Promise<void>;
|
|
84
|
+
/** Optional: clear token on logout */
|
|
85
|
+
clearToken?(): void | Promise<void>;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* JWT strategy — token from config or setToken; optional localStorage persistence
|
|
90
|
+
*/
|
|
91
|
+
|
|
92
|
+
interface JwtStrategyOptions {
|
|
93
|
+
/** Initial token */
|
|
94
|
+
token?: string | null;
|
|
95
|
+
/** localStorage key for persistence (omit for in-memory only) */
|
|
96
|
+
storageKey?: string | null;
|
|
97
|
+
}
|
|
98
|
+
declare function createJwtStrategy(options?: JwtStrategyOptions): AuthStrategyBase;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Custom strategy — caller supplies getToken (e.g. from backend or Privy)
|
|
102
|
+
*/
|
|
103
|
+
|
|
104
|
+
declare function createCustomStrategy(getToken: () => Promise<string | null>): AuthStrategyBase;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Horae API client — axios instance with Bearer token from auth strategy
|
|
108
|
+
*/
|
|
109
|
+
|
|
110
|
+
interface CreateClientOptions {
|
|
111
|
+
apiBase: string;
|
|
112
|
+
getToken: () => Promise<string | null>;
|
|
113
|
+
}
|
|
114
|
+
declare function createClient(options: CreateClientOptions): AxiosInstance;
|
|
115
|
+
declare function createClientWithAuth(auth: AuthStrategyBase, apiBase: string): AxiosInstance;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Horae Passport API — list, single, claim, user, documents
|
|
119
|
+
* Mirrors passport-web shared/api/api.service.ts (token-based auth)
|
|
120
|
+
*/
|
|
121
|
+
|
|
122
|
+
declare function createPassportApi(api: AxiosInstance): {
|
|
123
|
+
/** List passports claimed by current user */
|
|
124
|
+
getMyClaimedPassports(): Promise<PassportListItem[]>;
|
|
125
|
+
/** List passports by owner wallet (e.g. Shopify) */
|
|
126
|
+
getPassportsByOwner(ownerWallet: string): Promise<PassportListItem[]>;
|
|
127
|
+
/** Single passport (authenticated) */
|
|
128
|
+
getPassport(tokenId: string, isAuthenticated?: boolean): Promise<PassportDetail>;
|
|
129
|
+
/** Claim passport */
|
|
130
|
+
claimPassport(data: {
|
|
131
|
+
tokenId: string;
|
|
132
|
+
toAddress: string;
|
|
133
|
+
claimKey: string;
|
|
134
|
+
}): Promise<void>;
|
|
135
|
+
/** Shopify claim (serialNumber + optional email) */
|
|
136
|
+
claimShopifyPassport(data: {
|
|
137
|
+
serialNumber: string;
|
|
138
|
+
email?: string;
|
|
139
|
+
}): Promise<void>;
|
|
140
|
+
/** Current user profile */
|
|
141
|
+
getMe(): Promise<User>;
|
|
142
|
+
/** Documents for a passport (owner gets file URLs) */
|
|
143
|
+
getDocuments(tokenId: string): Promise<{
|
|
144
|
+
url?: string;
|
|
145
|
+
name?: string;
|
|
146
|
+
[k: string]: unknown;
|
|
147
|
+
}[]>;
|
|
148
|
+
/** Transfer passport to user by email */
|
|
149
|
+
transferPassportToUser(data: {
|
|
150
|
+
tokenId: string;
|
|
151
|
+
recipientEmail: string;
|
|
152
|
+
}): Promise<void>;
|
|
153
|
+
/** Set stolen status */
|
|
154
|
+
setStolenStatus(data: {
|
|
155
|
+
tokenId: string;
|
|
156
|
+
isStolen: boolean;
|
|
157
|
+
}): Promise<void>;
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* UI Renderer for @horae/passport-core
|
|
162
|
+
* Dynamically injects the pre-built React SDK bundle to render the UI on the host page.
|
|
163
|
+
*/
|
|
164
|
+
interface RenderOptions {
|
|
165
|
+
/** CSS selector of the container element (e.g. "#passport-app") */
|
|
166
|
+
container: string;
|
|
167
|
+
/** UI Host base URL where the bundle is served (default: https://app.horae.io) */
|
|
168
|
+
uiHost?: string;
|
|
169
|
+
/** Initial tab/view (e.g. "passports", "claim", "settings") */
|
|
170
|
+
initialTab?: string;
|
|
171
|
+
}
|
|
172
|
+
interface RenderPassportOptions extends RenderOptions {
|
|
173
|
+
/** The ID of the passport to show */
|
|
174
|
+
passportId: string;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Render the generic SDK App
|
|
178
|
+
*/
|
|
179
|
+
declare function mountApp(options: RenderOptions, apiBase: string, token: string | null): Promise<void>;
|
|
180
|
+
/**
|
|
181
|
+
* Render specifically the Passport List (maps to "passports" tab/view)
|
|
182
|
+
*/
|
|
183
|
+
declare function renderPassportList(options: RenderOptions, apiBase: string, token: string | null): Promise<void>;
|
|
184
|
+
/**
|
|
185
|
+
* Render specifically a Single Passport (maps to a specific detail view layout)
|
|
186
|
+
*/
|
|
187
|
+
declare function renderPassport(options: RenderPassportOptions, apiBase: string, token: string | null): Promise<void>;
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* @horae/passport-core
|
|
191
|
+
* Reusable Passport SDK — auth strategies, Horae API client, events.
|
|
192
|
+
* Token-based auth only; no cross-domain cookies.
|
|
193
|
+
*/
|
|
194
|
+
|
|
195
|
+
interface PassportSDK {
|
|
196
|
+
/** Horae API client (axios) */
|
|
197
|
+
readonly api: ReturnType<typeof createPassportApi>;
|
|
198
|
+
/** Event bus for auth:ready, auth:error, passport:claimed, etc. */
|
|
199
|
+
readonly events: ReturnType<typeof createEventBus>;
|
|
200
|
+
/** Auth strategy instance (for setToken/clearToken when available) */
|
|
201
|
+
readonly auth: AuthStrategyBase;
|
|
202
|
+
/** Config */
|
|
203
|
+
readonly config: Readonly<PassportSDKConfig>;
|
|
204
|
+
/** Mount the generic SDK App in headless mode */
|
|
205
|
+
mount(options: RenderOptions): Promise<void>;
|
|
206
|
+
/** Render the Passport List view */
|
|
207
|
+
renderPassportList(options: RenderOptions): Promise<void>;
|
|
208
|
+
/** Render a Single Passport Detail view */
|
|
209
|
+
renderPassport(options: RenderPassportOptions): Promise<void>;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Create the Passport SDK instance.
|
|
213
|
+
* - authStrategy "jwt": pass token in config or call setToken on auth after getting JWT from your backend.
|
|
214
|
+
* - authStrategy "custom": pass getToken in config.
|
|
215
|
+
* - authStrategy "shopify": use @horae/passport-shopify to get JWT from bridge, then use jwt or custom.
|
|
216
|
+
*/
|
|
217
|
+
declare function createPassportSDK(config: PassportSDKConfig): PassportSDK;
|
|
218
|
+
|
|
219
|
+
export { type AuthStrategy, type AuthStrategyBase, type CreateClientOptions, type EventBus, type EventMap, type PassportDetail, type PassportListItem, type PassportSDK, type PassportSDKConfig, type RenderOptions, type RenderPassportOptions, type SDKEvent, type User, createClient, createClientWithAuth, createCustomStrategy, createEventBus, createJwtStrategy, createPassportApi, createPassportSDK, mountApp, renderPassport, renderPassportList };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
// src/events.ts
|
|
2
|
+
function createEventBus() {
|
|
3
|
+
const listeners = /* @__PURE__ */ new Map();
|
|
4
|
+
function on(event, fn) {
|
|
5
|
+
if (!listeners.has(event)) listeners.set(event, /* @__PURE__ */ new Set());
|
|
6
|
+
listeners.get(event).add(fn);
|
|
7
|
+
return () => listeners.get(event)?.delete(fn);
|
|
8
|
+
}
|
|
9
|
+
function emit(event, payload) {
|
|
10
|
+
listeners.get(event)?.forEach((fn) => fn(payload));
|
|
11
|
+
}
|
|
12
|
+
return { on, emit };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// src/auth/strategies/jwt.ts
|
|
16
|
+
function createJwtStrategy(options = {}) {
|
|
17
|
+
let token = options.token ?? null;
|
|
18
|
+
const storageKey = options.storageKey ?? null;
|
|
19
|
+
const storage = typeof window !== "undefined" ? window.localStorage : void 0;
|
|
20
|
+
if (storage && storageKey) {
|
|
21
|
+
try {
|
|
22
|
+
const stored = storage.getItem(storageKey);
|
|
23
|
+
if (stored) token = stored;
|
|
24
|
+
} catch {
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
async getToken() {
|
|
29
|
+
return token;
|
|
30
|
+
},
|
|
31
|
+
setToken(next) {
|
|
32
|
+
token = next;
|
|
33
|
+
if (storage && storageKey) {
|
|
34
|
+
try {
|
|
35
|
+
if (next) storage.setItem(storageKey, next);
|
|
36
|
+
else storage.removeItem(storageKey);
|
|
37
|
+
} catch {
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
clearToken() {
|
|
42
|
+
token = null;
|
|
43
|
+
if (storage && storageKey) {
|
|
44
|
+
try {
|
|
45
|
+
storage.removeItem(storageKey);
|
|
46
|
+
} catch {
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// src/auth/strategies/custom.ts
|
|
54
|
+
function createCustomStrategy(getToken) {
|
|
55
|
+
return { getToken };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// src/client.ts
|
|
59
|
+
import axios from "axios";
|
|
60
|
+
function createClient(options) {
|
|
61
|
+
const { apiBase, getToken } = options;
|
|
62
|
+
const client = axios.create({
|
|
63
|
+
baseURL: apiBase.replace(/\/v1\/?$/, "") + "/v1"
|
|
64
|
+
});
|
|
65
|
+
client.interceptors.request.use(async (config) => {
|
|
66
|
+
const token = await getToken();
|
|
67
|
+
if (token) {
|
|
68
|
+
config.headers.Authorization = `Bearer ${token}`;
|
|
69
|
+
}
|
|
70
|
+
config.headers["ngrok-skip-browser-warning"] = "true";
|
|
71
|
+
return config;
|
|
72
|
+
});
|
|
73
|
+
return client;
|
|
74
|
+
}
|
|
75
|
+
function createClientWithAuth(auth, apiBase) {
|
|
76
|
+
return createClient({
|
|
77
|
+
apiBase,
|
|
78
|
+
getToken: () => auth.getToken()
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// src/api.ts
|
|
83
|
+
function unwrapItems(response) {
|
|
84
|
+
const data = response.data;
|
|
85
|
+
if (Array.isArray(data)) return data;
|
|
86
|
+
const inner = data && typeof data === "object" && "data" in data ? data.data : data;
|
|
87
|
+
const items = inner && typeof inner === "object" && "items" in inner ? inner.items : Array.isArray(inner) ? inner : [];
|
|
88
|
+
return items ?? [];
|
|
89
|
+
}
|
|
90
|
+
function unwrapData(response) {
|
|
91
|
+
const data = response.data;
|
|
92
|
+
return data && typeof data === "object" && "data" in data ? data.data : data;
|
|
93
|
+
}
|
|
94
|
+
function createPassportApi(api) {
|
|
95
|
+
return {
|
|
96
|
+
/** List passports claimed by current user */
|
|
97
|
+
async getMyClaimedPassports() {
|
|
98
|
+
const res = await api.get("/api/passport/claimed");
|
|
99
|
+
return unwrapItems(res);
|
|
100
|
+
},
|
|
101
|
+
/** List passports by owner wallet (e.g. Shopify) */
|
|
102
|
+
async getPassportsByOwner(ownerWallet) {
|
|
103
|
+
const res = await api.get("/api/passport/claimed", { params: { owner: ownerWallet } });
|
|
104
|
+
return unwrapItems(res);
|
|
105
|
+
},
|
|
106
|
+
/** Single passport (authenticated) */
|
|
107
|
+
async getPassport(tokenId, isAuthenticated = true) {
|
|
108
|
+
const path = isAuthenticated ? `/api/passport/${tokenId}` : `/api/passport/public/${tokenId}`;
|
|
109
|
+
const res = await api.get(path);
|
|
110
|
+
return unwrapData(res);
|
|
111
|
+
},
|
|
112
|
+
/** Claim passport */
|
|
113
|
+
async claimPassport(data) {
|
|
114
|
+
await api.post("/api/passport/claim", data);
|
|
115
|
+
},
|
|
116
|
+
/** Shopify claim (serialNumber + optional email) */
|
|
117
|
+
async claimShopifyPassport(data) {
|
|
118
|
+
await api.post("/api/passport/shopify-claim", data);
|
|
119
|
+
},
|
|
120
|
+
/** Current user profile */
|
|
121
|
+
async getMe() {
|
|
122
|
+
const res = await api.get("/users/me");
|
|
123
|
+
const raw = unwrapData(res);
|
|
124
|
+
const doc = raw && typeof raw === "object" && "_doc" in raw ? raw._doc : raw;
|
|
125
|
+
return doc;
|
|
126
|
+
},
|
|
127
|
+
/** Documents for a passport (owner gets file URLs) */
|
|
128
|
+
async getDocuments(tokenId) {
|
|
129
|
+
const res = await api.get(`/api/passport/${tokenId}/documents`);
|
|
130
|
+
const responseData = res.data?.data ?? res.data;
|
|
131
|
+
if (responseData && typeof responseData === "object" && "data" in responseData && Array.isArray(responseData.data))
|
|
132
|
+
return responseData.data;
|
|
133
|
+
if (Array.isArray(responseData)) return responseData;
|
|
134
|
+
return [];
|
|
135
|
+
},
|
|
136
|
+
/** Transfer passport to user by email */
|
|
137
|
+
async transferPassportToUser(data) {
|
|
138
|
+
await api.post("/api/passport/transfer-to-user", data);
|
|
139
|
+
},
|
|
140
|
+
/** Set stolen status */
|
|
141
|
+
async setStolenStatus(data) {
|
|
142
|
+
await api.post("/api/passport/set-stolen", data);
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// src/ui.ts
|
|
148
|
+
function prepareContainer(options, apiBase, token) {
|
|
149
|
+
const containerEl = document.querySelector(options.container);
|
|
150
|
+
if (!containerEl) {
|
|
151
|
+
throw new Error(`PassportSDK: container element '${options.container}' not found.`);
|
|
152
|
+
}
|
|
153
|
+
if (containerEl instanceof HTMLElement) {
|
|
154
|
+
containerEl.dataset.horaeApiBase = apiBase;
|
|
155
|
+
if (options.initialTab) {
|
|
156
|
+
containerEl.dataset.initialTab = options.initialTab;
|
|
157
|
+
}
|
|
158
|
+
if (token) {
|
|
159
|
+
containerEl.dataset.horaeToken = token;
|
|
160
|
+
}
|
|
161
|
+
containerEl.classList.add("horae-sdk-root");
|
|
162
|
+
containerEl.dataset.containerSelector = options.container;
|
|
163
|
+
}
|
|
164
|
+
return containerEl;
|
|
165
|
+
}
|
|
166
|
+
function loadUIBundle(uiHost) {
|
|
167
|
+
const SCRIPT_ID = "horae-passport-sdk-ui";
|
|
168
|
+
return new Promise((resolve, reject) => {
|
|
169
|
+
if (document.getElementById(SCRIPT_ID)) {
|
|
170
|
+
resolve();
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
const script = document.createElement("script");
|
|
174
|
+
script.id = SCRIPT_ID;
|
|
175
|
+
script.type = "module";
|
|
176
|
+
script.src = `${uiHost.replace(/\/$/, "")}/sdk-embed.js`;
|
|
177
|
+
script.onload = () => resolve();
|
|
178
|
+
script.onerror = () => reject(new Error(`PassportSDK: Failed to load UI bundle from ${script.src}`));
|
|
179
|
+
document.head.appendChild(script);
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
async function mountApp(options, apiBase, token) {
|
|
183
|
+
prepareContainer(options, apiBase, token);
|
|
184
|
+
const host = options.uiHost || "https://app.horae.io";
|
|
185
|
+
await loadUIBundle(host);
|
|
186
|
+
window.dispatchEvent(new CustomEvent("horae:sdk:mount", {
|
|
187
|
+
detail: { container: options.container }
|
|
188
|
+
}));
|
|
189
|
+
}
|
|
190
|
+
async function renderPassportList(options, apiBase, token) {
|
|
191
|
+
return mountApp({ ...options, initialTab: "passports" }, apiBase, token);
|
|
192
|
+
}
|
|
193
|
+
async function renderPassport(options, apiBase, token) {
|
|
194
|
+
const containerEl = prepareContainer(options, apiBase, token);
|
|
195
|
+
if (containerEl instanceof HTMLElement) {
|
|
196
|
+
containerEl.dataset.passportId = options.passportId;
|
|
197
|
+
containerEl.dataset.initialTab = "detail";
|
|
198
|
+
}
|
|
199
|
+
const host = options.uiHost || "https://app.horae.io";
|
|
200
|
+
await loadUIBundle(host);
|
|
201
|
+
window.dispatchEvent(new CustomEvent("horae:sdk:mount", {
|
|
202
|
+
detail: { container: options.container, passportId: options.passportId }
|
|
203
|
+
}));
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// src/index.ts
|
|
207
|
+
function createPassportSDK(config) {
|
|
208
|
+
const events = createEventBus();
|
|
209
|
+
let auth;
|
|
210
|
+
switch (config.authStrategy) {
|
|
211
|
+
case "jwt": {
|
|
212
|
+
const jwt = createJwtStrategy({
|
|
213
|
+
token: config.token ?? void 0,
|
|
214
|
+
storageKey: config.tokenStorageKey ?? void 0
|
|
215
|
+
});
|
|
216
|
+
auth = jwt;
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
case "custom": {
|
|
220
|
+
if (!config.getToken) throw new Error("PassportSDK: getToken required for authStrategy 'custom'");
|
|
221
|
+
auth = createCustomStrategy(config.getToken);
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
case "shopify":
|
|
225
|
+
auth = createJwtStrategy({
|
|
226
|
+
token: config.token ?? void 0,
|
|
227
|
+
storageKey: config.tokenStorageKey ?? "shopify_access_token"
|
|
228
|
+
});
|
|
229
|
+
break;
|
|
230
|
+
case "privy":
|
|
231
|
+
if (!config.getToken) throw new Error("PassportSDK: getToken required for authStrategy 'privy' (pass Privy getAccessToken)");
|
|
232
|
+
auth = createCustomStrategy(config.getToken);
|
|
233
|
+
break;
|
|
234
|
+
default:
|
|
235
|
+
throw new Error(`PassportSDK: unknown authStrategy ${String(config.authStrategy)}`);
|
|
236
|
+
}
|
|
237
|
+
const client = createClient({ apiBase: config.apiBase, getToken: () => auth.getToken() });
|
|
238
|
+
const api = createPassportApi(client);
|
|
239
|
+
return {
|
|
240
|
+
api,
|
|
241
|
+
events,
|
|
242
|
+
auth,
|
|
243
|
+
config: Object.freeze({ ...config }),
|
|
244
|
+
mount: (options) => mountApp(options, config.apiBase, config.token || null),
|
|
245
|
+
renderPassportList: (options) => renderPassportList(options, config.apiBase, config.token || null),
|
|
246
|
+
renderPassport: (options) => renderPassport(options, config.apiBase, config.token || null)
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
export {
|
|
250
|
+
createClient,
|
|
251
|
+
createClientWithAuth,
|
|
252
|
+
createCustomStrategy,
|
|
253
|
+
createEventBus,
|
|
254
|
+
createJwtStrategy,
|
|
255
|
+
createPassportApi,
|
|
256
|
+
createPassportSDK,
|
|
257
|
+
mountApp,
|
|
258
|
+
renderPassport,
|
|
259
|
+
renderPassportList
|
|
260
|
+
};
|
|
261
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/events.ts","../src/auth/strategies/jwt.ts","../src/auth/strategies/custom.ts","../src/client.ts","../src/api.ts","../src/ui.ts","../src/index.ts"],"sourcesContent":["/**\n * @horae/passport-core — event emitter for auth and passport events\n */\n\nexport type SDKEvent =\n | \"auth:ready\"\n | \"auth:error\"\n | \"passport:claimed\"\n | \"passport:loaded\"\n | \"passport:list:loaded\"\n | \"token:expired\";\n\nexport type EventMap = {\n \"auth:ready\": void;\n \"auth:error\": { message: string; code?: string };\n \"passport:claimed\": { tokenId: string };\n \"passport:loaded\": { tokenId: string };\n \"passport:list:loaded\": { count: number };\n \"token:expired\": void;\n};\n\ntype Listener<T> = (payload: T) => void;\n\nexport function createEventBus() {\n const listeners = new Map<SDKEvent, Set<Listener<unknown>>>();\n\n function on<E extends SDKEvent>(event: E, fn: Listener<EventMap[E]>) {\n if (!listeners.has(event)) listeners.set(event, new Set());\n listeners.get(event)!.add(fn as Listener<unknown>);\n return () => listeners.get(event)?.delete(fn as Listener<unknown>);\n }\n\n function emit<E extends SDKEvent>(event: E, payload?: EventMap[E]) {\n listeners.get(event)?.forEach((fn) => (fn as Listener<EventMap[E]>)(payload as EventMap[E]));\n }\n\n return { on, emit };\n}\n\nexport type EventBus = ReturnType<typeof createEventBus>;\n","/**\n * JWT strategy — token from config or setToken; optional localStorage persistence\n */\n\nimport type { AuthStrategyBase } from \"../types.js\";\n\nexport interface JwtStrategyOptions {\n /** Initial token */\n token?: string | null;\n /** localStorage key for persistence (omit for in-memory only) */\n storageKey?: string | null;\n}\n\nexport function createJwtStrategy(options: JwtStrategyOptions = {}): AuthStrategyBase {\n let token: string | null = options.token ?? null;\n const storageKey = options.storageKey ?? null;\n\n const storage = typeof window !== \"undefined\" ? window.localStorage : undefined;\n if (storage && storageKey) {\n try {\n const stored = storage.getItem(storageKey);\n if (stored) token = stored;\n } catch {\n // ignore\n }\n }\n\n return {\n async getToken() {\n return token;\n },\n setToken(next: string | null) {\n token = next;\n if (storage && storageKey) {\n try {\n if (next) storage.setItem(storageKey, next);\n else storage.removeItem(storageKey);\n } catch {\n // ignore\n }\n }\n },\n clearToken() {\n token = null;\n if (storage && storageKey) {\n try {\n storage.removeItem(storageKey);\n } catch {\n // ignore\n }\n }\n },\n };\n}\n","/**\n * Custom strategy — caller supplies getToken (e.g. from backend or Privy)\n */\n\nimport type { AuthStrategyBase } from \"../types.js\";\n\nexport function createCustomStrategy(\n getToken: () => Promise<string | null>,\n): AuthStrategyBase {\n return { getToken };\n}\n","/**\n * Horae API client — axios instance with Bearer token from auth strategy\n */\n\nimport axios, { type AxiosInstance } from \"axios\";\nimport type { AuthStrategyBase } from \"./auth/index.js\";\n\nexport interface CreateClientOptions {\n apiBase: string;\n getToken: () => Promise<string | null>;\n}\n\nexport function createClient(options: CreateClientOptions): AxiosInstance {\n const { apiBase, getToken } = options;\n const client = axios.create({\n baseURL: apiBase.replace(/\\/v1\\/?$/, \"\") + \"/v1\",\n });\n\n client.interceptors.request.use(async (config) => {\n const token = await getToken();\n if (token) {\n config.headers.Authorization = `Bearer ${token}`;\n }\n config.headers[\"ngrok-skip-browser-warning\"] = \"true\";\n return config;\n });\n\n return client;\n}\n\nexport function createClientWithAuth(auth: AuthStrategyBase, apiBase: string): AxiosInstance {\n return createClient({\n apiBase,\n getToken: () => auth.getToken(),\n });\n}\n","/**\n * Horae Passport API — list, single, claim, user, documents\n * Mirrors passport-web shared/api/api.service.ts (token-based auth)\n */\n\nimport type { AxiosInstance } from \"axios\";\nimport type { PassportListItem, PassportDetail, User } from \"./types.js\";\n\nfunction unwrapItems<T>(response: { data?: unknown }): T[] {\n const data = response.data as { data?: { items?: T[] }; items?: T[] } | T[] | undefined;\n if (Array.isArray(data)) return data;\n const inner = data && typeof data === \"object\" && \"data\" in data ? (data as { data?: { items?: T[] } }).data : data;\n const items = (inner && typeof inner === \"object\" && \"items\" in inner\n ? (inner as { items?: T[] }).items\n : Array.isArray(inner)\n ? inner\n : []) as T[];\n return items ?? [];\n}\n\nfunction unwrapData<T>(response: { data?: unknown }): T {\n const data = response.data as { data?: T } | T;\n return (data && typeof data === \"object\" && \"data\" in data ? (data as { data: T }).data : data) as T;\n}\n\nexport function createPassportApi(api: AxiosInstance) {\n return {\n /** List passports claimed by current user */\n async getMyClaimedPassports(): Promise<PassportListItem[]> {\n const res = await api.get(\"/api/passport/claimed\");\n return unwrapItems<PassportListItem>(res) as PassportListItem[];\n },\n\n /** List passports by owner wallet (e.g. Shopify) */\n async getPassportsByOwner(ownerWallet: string): Promise<PassportListItem[]> {\n const res = await api.get(\"/api/passport/claimed\", { params: { owner: ownerWallet } });\n return unwrapItems<PassportListItem>(res) as PassportListItem[];\n },\n\n /** Single passport (authenticated) */\n async getPassport(tokenId: string, isAuthenticated = true): Promise<PassportDetail> {\n const path = isAuthenticated ? `/api/passport/${tokenId}` : `/api/passport/public/${tokenId}`;\n const res = await api.get(path);\n return unwrapData<PassportDetail>(res);\n },\n\n /** Claim passport */\n async claimPassport(data: { tokenId: string; toAddress: string; claimKey: string }) {\n await api.post(\"/api/passport/claim\", data);\n },\n\n /** Shopify claim (serialNumber + optional email) */\n async claimShopifyPassport(data: { serialNumber: string; email?: string }) {\n await api.post(\"/api/passport/shopify-claim\", data);\n },\n\n /** Current user profile */\n async getMe(): Promise<User> {\n const res = await api.get(\"/users/me\");\n const raw = unwrapData<unknown>(res);\n const doc = raw && typeof raw === \"object\" && \"_doc\" in raw ? (raw as { _doc: User })._doc : raw;\n return doc as User;\n },\n\n /** Documents for a passport (owner gets file URLs) */\n async getDocuments(tokenId: string): Promise<{ url?: string; name?: string; [k: string]: unknown }[]> {\n const res = await api.get(`/api/passport/${tokenId}/documents`);\n const responseData = (res.data as { data?: unknown })?.data ?? res.data;\n if (responseData && typeof responseData === \"object\" && \"data\" in responseData && Array.isArray((responseData as { data: unknown[] }).data))\n return (responseData as { data: unknown[] }).data as { url?: string; name?: string; [k: string]: unknown }[];\n if (Array.isArray(responseData)) return responseData as { url?: string; name?: string; [k: string]: unknown }[];\n return [];\n },\n\n /** Transfer passport to user by email */\n async transferPassportToUser(data: { tokenId: string; recipientEmail: string }) {\n await api.post(\"/api/passport/transfer-to-user\", data);\n },\n\n /** Set stolen status */\n async setStolenStatus(data: { tokenId: string; isStolen: boolean }) {\n await api.post(\"/api/passport/set-stolen\", data);\n },\n };\n}\n","/**\n * UI Renderer for @horae/passport-core\n * Dynamically injects the pre-built React SDK bundle to render the UI on the host page.\n */\n\nexport interface RenderOptions {\n /** CSS selector of the container element (e.g. \"#passport-app\") */\n container: string;\n /** UI Host base URL where the bundle is served (default: https://app.horae.io) */\n uiHost?: string;\n /** Initial tab/view (e.g. \"passports\", \"claim\", \"settings\") */\n initialTab?: string;\n}\n\nexport interface RenderPassportOptions extends RenderOptions {\n /** The ID of the passport to show */\n passportId: string;\n}\n\n/**\n * Configure the container with data attributes so the UI bundle can read them upon mounting.\n */\nfunction prepareContainer(options: RenderOptions, apiBase: string, token: string | null) {\n const containerEl = document.querySelector(options.container);\n if (!containerEl) {\n throw new Error(`PassportSDK: container element '${options.container}' not found.`);\n }\n\n // Set necessary data attributes for the UI bundle (similar to Shopify embed contract)\n if (containerEl instanceof HTMLElement) {\n containerEl.dataset.horaeApiBase = apiBase;\n if (options.initialTab) {\n containerEl.dataset.initialTab = options.initialTab;\n }\n\n // Inject the token (the UI bundle will look for this or use its own mechanism)\n if (token) {\n containerEl.dataset.horaeToken = token;\n }\n\n // Add a signature class/id to ensure the embed root is identifiable\n containerEl.classList.add(\"horae-sdk-root\");\n\n // Allow the bundle to easily find its mount point if it uses querySelector internally\n // (We also pass the container selector directly to the mount event later if needed)\n containerEl.dataset.containerSelector = options.container;\n }\n\n return containerEl;\n}\n\n/**\n * Inject the script tag for the UI bundle if it hasn't been injected yet.\n */\nfunction loadUIBundle(uiHost: string): Promise<void> {\n const SCRIPT_ID = \"horae-passport-sdk-ui\";\n\n return new Promise((resolve, reject) => {\n if (document.getElementById(SCRIPT_ID)) {\n // Script already added\n resolve();\n return;\n }\n\n const script = document.createElement(\"script\");\n script.id = SCRIPT_ID;\n script.type = \"module\";\n // Target the newly created sdk-embed entrypoint\n script.src = `${uiHost.replace(/\\/$/, '')}/sdk-embed.js`;\n\n script.onload = () => resolve();\n script.onerror = () => reject(new Error(`PassportSDK: Failed to load UI bundle from ${script.src}`));\n\n document.head.appendChild(script);\n });\n}\n\n/**\n * Render the generic SDK App\n */\nexport async function mountApp(\n options: RenderOptions,\n apiBase: string,\n token: string | null\n): Promise<void> {\n prepareContainer(options, apiBase, token);\n\n const host = options.uiHost || \"https://app.horae.io\";\n await loadUIBundle(host);\n\n // Dispatch a custom event in case the bundle is already loaded and listening for dynamic mounts\n window.dispatchEvent(new CustomEvent(\"horae:sdk:mount\", {\n detail: { container: options.container }\n }));\n}\n\n/**\n * Render specifically the Passport List (maps to \"passports\" tab/view)\n */\nexport async function renderPassportList(\n options: RenderOptions,\n apiBase: string,\n token: string | null\n): Promise<void> {\n return mountApp({ ...options, initialTab: \"passports\" }, apiBase, token);\n}\n\n/**\n * Render specifically a Single Passport (maps to a specific detail view layout)\n */\nexport async function renderPassport(\n options: RenderPassportOptions,\n apiBase: string,\n token: string | null\n): Promise<void> {\n const containerEl = prepareContainer(options, apiBase, token);\n\n if (containerEl instanceof HTMLElement) {\n containerEl.dataset.passportId = options.passportId;\n // Set initialTab to \"detail\" or a specific flag the UI recognizes\n containerEl.dataset.initialTab = \"detail\";\n }\n\n const host = options.uiHost || \"https://app.horae.io\";\n await loadUIBundle(host);\n\n window.dispatchEvent(new CustomEvent(\"horae:sdk:mount\", {\n detail: { container: options.container, passportId: options.passportId }\n }));\n}\n","/**\n * @horae/passport-core\n * Reusable Passport SDK — auth strategies, Horae API client, events.\n * Token-based auth only; no cross-domain cookies.\n */\n\nexport type { PassportSDKConfig, AuthStrategy, PassportListItem, PassportDetail, User } from \"./types.js\";\nexport { createEventBus } from \"./events.js\";\nexport type { SDKEvent, EventMap, EventBus } from \"./events.js\";\nexport { createJwtStrategy, createCustomStrategy } from \"./auth/index.js\";\nexport type { AuthStrategyBase } from \"./auth/index.js\";\nexport { createClient, createClientWithAuth } from \"./client.js\";\nexport type { CreateClientOptions } from \"./client.js\";\nexport { createPassportApi } from \"./api.js\";\nexport * from \"./ui.js\";\n\nimport type { PassportSDKConfig } from \"./types.js\";\nimport { createEventBus } from \"./events.js\";\nimport { createJwtStrategy } from \"./auth/strategies/jwt.js\";\nimport { createCustomStrategy } from \"./auth/strategies/custom.js\";\nimport { createClient } from \"./client.js\";\nimport { createPassportApi } from \"./api.js\";\nimport type { AuthStrategyBase } from \"./auth/index.js\";\nimport { mountApp, renderPassportList, renderPassport } from \"./ui.js\";\nimport type { RenderOptions, RenderPassportOptions } from \"./ui.js\";\n\nexport interface PassportSDK {\n /** Horae API client (axios) */\n readonly api: ReturnType<typeof createPassportApi>;\n /** Event bus for auth:ready, auth:error, passport:claimed, etc. */\n readonly events: ReturnType<typeof createEventBus>;\n /** Auth strategy instance (for setToken/clearToken when available) */\n readonly auth: AuthStrategyBase;\n /** Config */\n readonly config: Readonly<PassportSDKConfig>;\n /** Mount the generic SDK App in headless mode */\n mount(options: RenderOptions): Promise<void>;\n /** Render the Passport List view */\n renderPassportList(options: RenderOptions): Promise<void>;\n /** Render a Single Passport Detail view */\n renderPassport(options: RenderPassportOptions): Promise<void>;\n}\n\n/**\n * Create the Passport SDK instance.\n * - authStrategy \"jwt\": pass token in config or call setToken on auth after getting JWT from your backend.\n * - authStrategy \"custom\": pass getToken in config.\n * - authStrategy \"shopify\": use @horae/passport-shopify to get JWT from bridge, then use jwt or custom.\n */\nexport function createPassportSDK(config: PassportSDKConfig): PassportSDK {\n const events = createEventBus();\n let auth: AuthStrategyBase;\n\n switch (config.authStrategy) {\n case \"jwt\": {\n const jwt = createJwtStrategy({\n token: config.token ?? undefined,\n storageKey: config.tokenStorageKey ?? undefined,\n });\n auth = jwt;\n break;\n }\n case \"custom\": {\n if (!config.getToken) throw new Error(\"PassportSDK: getToken required for authStrategy 'custom'\");\n auth = createCustomStrategy(config.getToken);\n break;\n }\n case \"shopify\":\n // Shopify: use JWT strategy; token is set by @horae/passport-shopify bridge\n auth = createJwtStrategy({\n token: config.token ?? undefined,\n storageKey: config.tokenStorageKey ?? \"shopify_access_token\",\n });\n break;\n case \"privy\":\n // Privy: use custom strategy with getToken from host (e.g. getAccessToken from @privy-io/react-auth)\n if (!config.getToken) throw new Error(\"PassportSDK: getToken required for authStrategy 'privy' (pass Privy getAccessToken)\");\n auth = createCustomStrategy(config.getToken);\n break;\n default:\n throw new Error(`PassportSDK: unknown authStrategy ${String((config as { authStrategy?: string }).authStrategy)}`);\n }\n\n const client = createClient({ apiBase: config.apiBase, getToken: () => auth.getToken() });\n const api = createPassportApi(client);\n\n return {\n api,\n events,\n auth,\n config: Object.freeze({ ...config }),\n mount: (options) => mountApp(options, config.apiBase, config.token || null),\n renderPassportList: (options) => renderPassportList(options, config.apiBase, config.token || null),\n renderPassport: (options) => renderPassport(options, config.apiBase, config.token || null),\n };\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@horae.io/passport-core",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Horae Passport SDK — core client, auth strategies, Horae API",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsup",
|
|
21
|
+
"dev": "tsup --watch"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"axios": "^1.8.4",
|
|
25
|
+
"zod": "^3.24.2"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"tsup": "^8.3.5",
|
|
29
|
+
"typescript": "~5.7.2"
|
|
30
|
+
},
|
|
31
|
+
"peerDependencies": {}
|
|
32
|
+
}
|