@mekari-officeless/sdk 0.1.0 → 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/CHANGELOG.md +40 -0
- package/README.md +253 -123
- package/dist/index.cjs +396 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +135 -0
- package/dist/index.d.ts +135 -0
- package/dist/index.js +393 -0
- package/dist/index.js.map +1 -0
- package/package.json +66 -11
- package/src/client.js +0 -84
- package/src/errors.js +0 -8
- package/src/index.js +0 -2
- package/src/table.js +0 -22
- package/src/workflow.js +0 -10
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/errors.ts
|
|
4
|
+
var OfficelessError = class _OfficelessError extends Error {
|
|
5
|
+
constructor(type, message, status = null) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = "OfficelessError";
|
|
8
|
+
this.type = type;
|
|
9
|
+
this.status = status;
|
|
10
|
+
Object.setPrototypeOf(this, _OfficelessError.prototype);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
function inferTypeFromStatus(status) {
|
|
14
|
+
if (status === 400 || status === 404) return "ValidationError";
|
|
15
|
+
if (status === 401 || status === 403 || status === 429) return "AuthorizationError";
|
|
16
|
+
if (status === 504) return "WorkflowExecutionError";
|
|
17
|
+
return "InternalServerError";
|
|
18
|
+
}
|
|
19
|
+
function toOfficelessError(envelope, status) {
|
|
20
|
+
const type = envelope?.type ?? inferTypeFromStatus(status);
|
|
21
|
+
const message = envelope?.message ?? `Request failed with status ${status}.`;
|
|
22
|
+
return new OfficelessError(type, message, status);
|
|
23
|
+
}
|
|
24
|
+
var CLIENT_MESSAGES = {
|
|
25
|
+
notSignedIn: "This function requires a signed-in user. Sign in with auth.login first.",
|
|
26
|
+
sessionExpired: "Session expired. Please log in again.",
|
|
27
|
+
invalidKeyFormat: "That SDK key is not in the expected format."
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// src/session-store.ts
|
|
31
|
+
var DB_NAME = "officeless-sdk";
|
|
32
|
+
var DB_VERSION = 1;
|
|
33
|
+
var KEY_STORE = "crypto-keys";
|
|
34
|
+
var STORAGE_PREFIX = "officeless.session.";
|
|
35
|
+
function isSessionExpired(session) {
|
|
36
|
+
const expiry = Date.parse(session.expiredAt);
|
|
37
|
+
if (Number.isNaN(expiry)) return false;
|
|
38
|
+
return expiry <= Date.now();
|
|
39
|
+
}
|
|
40
|
+
function createBrowserSessionStore(namespace) {
|
|
41
|
+
const storageKey = `${STORAGE_PREFIX}${namespace}`;
|
|
42
|
+
let memorySession = null;
|
|
43
|
+
let degraded = false;
|
|
44
|
+
function degrade() {
|
|
45
|
+
degraded = true;
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
async function getKey() {
|
|
49
|
+
if (degraded) return null;
|
|
50
|
+
if (typeof indexedDB === "undefined" || typeof crypto === "undefined" || !crypto.subtle) {
|
|
51
|
+
return degrade();
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
const db = await openDatabase();
|
|
55
|
+
try {
|
|
56
|
+
const existing = await idbGet(db, namespace);
|
|
57
|
+
if (existing) return existing;
|
|
58
|
+
const created = await crypto.subtle.generateKey(
|
|
59
|
+
{ name: "AES-GCM", length: 256 },
|
|
60
|
+
// Non-extractable: the key can encrypt and decrypt, but its bytes cannot be
|
|
61
|
+
// read back out — not even by our own code.
|
|
62
|
+
false,
|
|
63
|
+
["encrypt", "decrypt"]
|
|
64
|
+
);
|
|
65
|
+
await idbPut(db, namespace, created);
|
|
66
|
+
return created;
|
|
67
|
+
} finally {
|
|
68
|
+
db.close();
|
|
69
|
+
}
|
|
70
|
+
} catch {
|
|
71
|
+
return degrade();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async function save(session) {
|
|
75
|
+
memorySession = session;
|
|
76
|
+
const key = await getKey();
|
|
77
|
+
if (!key) return;
|
|
78
|
+
try {
|
|
79
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
80
|
+
const plaintext = new TextEncoder().encode(JSON.stringify(session));
|
|
81
|
+
const ciphertext = await crypto.subtle.encrypt({ name: "AES-GCM", iv }, key, plaintext);
|
|
82
|
+
localStorage.setItem(storageKey, `${toBase64(iv)}.${toBase64(new Uint8Array(ciphertext))}`);
|
|
83
|
+
} catch {
|
|
84
|
+
degraded = true;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async function load() {
|
|
88
|
+
if (memorySession) return memorySession;
|
|
89
|
+
const key = await getKey();
|
|
90
|
+
if (!key) return null;
|
|
91
|
+
try {
|
|
92
|
+
const raw = localStorage.getItem(storageKey);
|
|
93
|
+
if (!raw) return null;
|
|
94
|
+
const separator = raw.indexOf(".");
|
|
95
|
+
if (separator < 1) return null;
|
|
96
|
+
const iv = fromBase64(raw.slice(0, separator));
|
|
97
|
+
const ciphertext = fromBase64(raw.slice(separator + 1));
|
|
98
|
+
const plaintext = await crypto.subtle.decrypt({ name: "AES-GCM", iv }, key, ciphertext);
|
|
99
|
+
memorySession = JSON.parse(new TextDecoder().decode(plaintext));
|
|
100
|
+
return memorySession;
|
|
101
|
+
} catch {
|
|
102
|
+
await clear();
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async function clear() {
|
|
107
|
+
memorySession = null;
|
|
108
|
+
try {
|
|
109
|
+
localStorage.removeItem(storageKey);
|
|
110
|
+
} catch {
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return { save, load, clear };
|
|
114
|
+
}
|
|
115
|
+
function createSessionHandle(store) {
|
|
116
|
+
let current = null;
|
|
117
|
+
return {
|
|
118
|
+
get: () => current,
|
|
119
|
+
async set(session) {
|
|
120
|
+
current = session;
|
|
121
|
+
await store.save(session);
|
|
122
|
+
},
|
|
123
|
+
async clear() {
|
|
124
|
+
current = null;
|
|
125
|
+
await store.clear();
|
|
126
|
+
},
|
|
127
|
+
async hydrate() {
|
|
128
|
+
current = await store.load();
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function openDatabase() {
|
|
133
|
+
return new Promise((resolve, reject) => {
|
|
134
|
+
const request = indexedDB.open(DB_NAME, DB_VERSION);
|
|
135
|
+
request.onupgradeneeded = () => {
|
|
136
|
+
const db = request.result;
|
|
137
|
+
if (!db.objectStoreNames.contains(KEY_STORE)) db.createObjectStore(KEY_STORE);
|
|
138
|
+
};
|
|
139
|
+
request.onsuccess = () => resolve(request.result);
|
|
140
|
+
request.onerror = () => reject(request.error);
|
|
141
|
+
request.onblocked = () => reject(new Error("IndexedDB open blocked"));
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
function idbGet(db, key) {
|
|
145
|
+
return new Promise((resolve, reject) => {
|
|
146
|
+
const request = db.transaction(KEY_STORE, "readonly").objectStore(KEY_STORE).get(key);
|
|
147
|
+
request.onsuccess = () => resolve(request.result);
|
|
148
|
+
request.onerror = () => reject(request.error);
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
function idbPut(db, key, value) {
|
|
152
|
+
return new Promise((resolve, reject) => {
|
|
153
|
+
const transaction = db.transaction(KEY_STORE, "readwrite");
|
|
154
|
+
transaction.objectStore(KEY_STORE).put(value, key);
|
|
155
|
+
transaction.oncomplete = () => resolve();
|
|
156
|
+
transaction.onerror = () => reject(transaction.error);
|
|
157
|
+
transaction.onabort = () => reject(transaction.error);
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
function toBase64(bytes) {
|
|
161
|
+
let binary = "";
|
|
162
|
+
for (let index = 0; index < bytes.length; index += 1) {
|
|
163
|
+
binary += String.fromCharCode(bytes[index]);
|
|
164
|
+
}
|
|
165
|
+
return btoa(binary);
|
|
166
|
+
}
|
|
167
|
+
function fromBase64(value) {
|
|
168
|
+
const binary = atob(value);
|
|
169
|
+
const bytes = new Uint8Array(new ArrayBuffer(binary.length));
|
|
170
|
+
for (let index = 0; index < binary.length; index += 1) {
|
|
171
|
+
bytes[index] = binary.charCodeAt(index);
|
|
172
|
+
}
|
|
173
|
+
return bytes;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// src/action/hitSDKFunction.ts
|
|
177
|
+
async function hitSDKFunction(http, session, name, data, withAuth = true) {
|
|
178
|
+
if (withAuth) {
|
|
179
|
+
const current = session.get();
|
|
180
|
+
if (!current || isSessionExpired(current)) {
|
|
181
|
+
throw new OfficelessError("AuthorizationError", CLIENT_MESSAGES.notSignedIn, null);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
const result = await http.request({
|
|
185
|
+
method: "POST",
|
|
186
|
+
path: "/action/run-function",
|
|
187
|
+
body: { name, data, with_auth: withAuth },
|
|
188
|
+
withAuth
|
|
189
|
+
});
|
|
190
|
+
if (!result) {
|
|
191
|
+
throw new OfficelessError("InternalServerError", "The function returned no result.", null);
|
|
192
|
+
}
|
|
193
|
+
return {
|
|
194
|
+
status: result.status,
|
|
195
|
+
// Passed through untouched on purpose. `output` is whatever the automation
|
|
196
|
+
// returns — the developer's own shape, and any JSON value including null.
|
|
197
|
+
// Rewriting its keys to camelCase (as the SDK does for its own fields) would
|
|
198
|
+
// silently corrupt their data.
|
|
199
|
+
output: result.output
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// src/auth/login.ts
|
|
204
|
+
async function login(http, email) {
|
|
205
|
+
const data = await http.request({
|
|
206
|
+
method: "POST",
|
|
207
|
+
path: "/auth/login",
|
|
208
|
+
body: { email }
|
|
209
|
+
});
|
|
210
|
+
return {
|
|
211
|
+
sessionId: data?.session_id ?? "",
|
|
212
|
+
otpExpiresAt: data?.otp_expires_at ?? ""
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// src/auth/logout.ts
|
|
217
|
+
async function logout(http, session) {
|
|
218
|
+
if (!session.get()) return;
|
|
219
|
+
try {
|
|
220
|
+
await http.request({
|
|
221
|
+
method: "POST",
|
|
222
|
+
path: "/auth/logout",
|
|
223
|
+
withAuth: true,
|
|
224
|
+
treat401AsSuccess: true
|
|
225
|
+
});
|
|
226
|
+
} finally {
|
|
227
|
+
await session.clear();
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// src/auth/refresh.ts
|
|
232
|
+
async function refresh(http, session) {
|
|
233
|
+
const current = session.get();
|
|
234
|
+
if (!current) {
|
|
235
|
+
throw new OfficelessError("AuthorizationError", CLIENT_MESSAGES.sessionExpired, null);
|
|
236
|
+
}
|
|
237
|
+
let data;
|
|
238
|
+
try {
|
|
239
|
+
data = await http.request({
|
|
240
|
+
method: "POST",
|
|
241
|
+
path: "/auth/refresh",
|
|
242
|
+
body: { refresh_token: current.refreshToken }
|
|
243
|
+
});
|
|
244
|
+
} catch (error) {
|
|
245
|
+
if (error instanceof OfficelessError && error.status === 401) {
|
|
246
|
+
await session.clear();
|
|
247
|
+
}
|
|
248
|
+
throw error;
|
|
249
|
+
}
|
|
250
|
+
if (!data) {
|
|
251
|
+
throw new OfficelessError("InternalServerError", "Refresh did not return a session.", null);
|
|
252
|
+
}
|
|
253
|
+
await session.set({
|
|
254
|
+
...current,
|
|
255
|
+
accessToken: data.access_token,
|
|
256
|
+
refreshToken: data.refresh_token,
|
|
257
|
+
expiredAt: data.expired_at
|
|
258
|
+
});
|
|
259
|
+
return {
|
|
260
|
+
accessToken: data.access_token,
|
|
261
|
+
expiredAt: data.expired_at
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// src/auth/user.ts
|
|
266
|
+
async function user(session) {
|
|
267
|
+
const current = session.get();
|
|
268
|
+
if (!current || isSessionExpired(current)) {
|
|
269
|
+
throw new OfficelessError("AuthorizationError", CLIENT_MESSAGES.sessionExpired, null);
|
|
270
|
+
}
|
|
271
|
+
return current.user;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// src/auth/verify.ts
|
|
275
|
+
function toSdkUser(wire) {
|
|
276
|
+
return {
|
|
277
|
+
userId: wire.user_id,
|
|
278
|
+
email: wire.email,
|
|
279
|
+
name: wire.name,
|
|
280
|
+
companyId: wire.company_id,
|
|
281
|
+
companyName: wire.company_name
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
async function verify(http, session, email, sessionId, code) {
|
|
285
|
+
const data = await http.request({
|
|
286
|
+
method: "POST",
|
|
287
|
+
path: "/auth/verify",
|
|
288
|
+
body: { email, session_id: sessionId, code }
|
|
289
|
+
});
|
|
290
|
+
if (!data) {
|
|
291
|
+
throw new OfficelessError("InternalServerError", "Sign-in did not return a session.", null);
|
|
292
|
+
}
|
|
293
|
+
const result = {
|
|
294
|
+
accessToken: data.access_token,
|
|
295
|
+
refreshToken: data.refresh_token,
|
|
296
|
+
expiredAt: data.expired_at,
|
|
297
|
+
user: toSdkUser(data.user)
|
|
298
|
+
};
|
|
299
|
+
await session.set(result);
|
|
300
|
+
return result;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// src/http-client.ts
|
|
304
|
+
var SDK_VERSION = "1.0.0" ;
|
|
305
|
+
var DEFAULT_GATEWAY_URL = "https://officeless-gateway.mekari.com/v1";
|
|
306
|
+
var SDK_BASE_PATH = "/nocode/sdk/v1";
|
|
307
|
+
function createHttpClient({
|
|
308
|
+
sdkKey,
|
|
309
|
+
gatewayUrl,
|
|
310
|
+
getAccessToken
|
|
311
|
+
}) {
|
|
312
|
+
const origin = gatewayUrl.replace(/\/+$/, "");
|
|
313
|
+
async function request(options) {
|
|
314
|
+
const { method, path, body, withAuth = false, networkErrorMessage, treat401AsSuccess } = options;
|
|
315
|
+
const headers = {
|
|
316
|
+
"Content-Type": "application/json",
|
|
317
|
+
// The company, project and stage all derive from this server-side (§2.4).
|
|
318
|
+
"X-Officeless-Sdk-Key": sdkKey,
|
|
319
|
+
// Recorded in the app log and copied into the session token's `sdk` claim.
|
|
320
|
+
// Untrusted server-side — never used for an access decision (§3.4).
|
|
321
|
+
"X-Officeless-Sdk-Version": SDK_VERSION
|
|
322
|
+
};
|
|
323
|
+
if (withAuth) {
|
|
324
|
+
const token = getAccessToken();
|
|
325
|
+
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
326
|
+
}
|
|
327
|
+
let response;
|
|
328
|
+
try {
|
|
329
|
+
response = await fetch(`${origin}${SDK_BASE_PATH}${path}`, {
|
|
330
|
+
method,
|
|
331
|
+
headers,
|
|
332
|
+
body: body === void 0 ? void 0 : JSON.stringify(body)
|
|
333
|
+
});
|
|
334
|
+
} catch {
|
|
335
|
+
throw new OfficelessError(
|
|
336
|
+
"InternalServerError",
|
|
337
|
+
networkErrorMessage ?? "Could not reach Officeless. Check the network and try again.",
|
|
338
|
+
null
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
if (response.status === 401 && treat401AsSuccess) return null;
|
|
342
|
+
const envelope = await readEnvelope(response);
|
|
343
|
+
if (!response.ok || envelope?.error) {
|
|
344
|
+
throw toOfficelessError(envelope, response.status);
|
|
345
|
+
}
|
|
346
|
+
return envelope?.data ?? null;
|
|
347
|
+
}
|
|
348
|
+
return { request };
|
|
349
|
+
}
|
|
350
|
+
async function readEnvelope(response) {
|
|
351
|
+
try {
|
|
352
|
+
return await response.json();
|
|
353
|
+
} catch {
|
|
354
|
+
return null;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// src/init.ts
|
|
359
|
+
var SDK_KEY_PATTERN = /^ofc_sdk_[a-z]+_[0-9a-f]{32}$/;
|
|
360
|
+
async function init(options) {
|
|
361
|
+
const sdkKey = options.sdkKey?.trim() ?? "";
|
|
362
|
+
if (!SDK_KEY_PATTERN.test(sdkKey)) {
|
|
363
|
+
throw new OfficelessError("ValidationError", CLIENT_MESSAGES.invalidKeyFormat, null);
|
|
364
|
+
}
|
|
365
|
+
const gatewayUrl = options.gatewayUrl?.trim() || DEFAULT_GATEWAY_URL;
|
|
366
|
+
const session = createSessionHandle(createBrowserSessionStore(sdkKey));
|
|
367
|
+
const http = createHttpClient({
|
|
368
|
+
sdkKey,
|
|
369
|
+
gatewayUrl,
|
|
370
|
+
getAccessToken: () => {
|
|
371
|
+
const current = session.get();
|
|
372
|
+
return current ? current.accessToken : null;
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
await session.hydrate();
|
|
376
|
+
return {
|
|
377
|
+
auth: {
|
|
378
|
+
login: (email) => login(http, email),
|
|
379
|
+
verify: (email, sessionId, code) => verify(http, session, email, sessionId, code),
|
|
380
|
+
user: () => user(session),
|
|
381
|
+
refresh: () => refresh(http, session),
|
|
382
|
+
logout: () => logout(http, session)
|
|
383
|
+
},
|
|
384
|
+
action: {
|
|
385
|
+
hitSDKFunction: (name, payload, withAuth) => hitSDKFunction(http, session, name, payload, withAuth)
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// src/index.ts
|
|
391
|
+
var officeless = { init };
|
|
392
|
+
|
|
393
|
+
exports.OfficelessError = OfficelessError;
|
|
394
|
+
exports.officeless = officeless;
|
|
395
|
+
//# sourceMappingURL=index.cjs.map
|
|
396
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/session-store.ts","../src/action/hitSDKFunction.ts","../src/auth/login.ts","../src/auth/logout.ts","../src/auth/refresh.ts","../src/auth/user.ts","../src/auth/verify.ts","../src/http-client.ts","../src/init.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["import type { OfficelessErrorType, WireEnvelope } from './types'\n\n/**\n * Every SDK failure is thrown as one of these.\n *\n * Branch on `type` rather than `message`, while\n * messages are human-facing. `status` is exposed alongside so a 429 can be told\n * apart from a 401 — both are `AuthorizationError`\n */\nexport class OfficelessError extends Error {\n readonly type: OfficelessErrorType\n /** HTTP status, or `null` when the call never reached the server. */\n readonly status: number | null\n\n constructor(type: OfficelessErrorType, message: string, status: number | null = null) {\n super(message)\n this.name = 'OfficelessError'\n this.type = type\n this.status = status\n\n // Required so `instanceof` survives the ES5-targeting downlevel some consumers\n // still build with.\n Object.setPrototypeOf(this, OfficelessError.prototype)\n }\n}\n\n/**\n * Falls back to a type when the server did not send one.\n *\n * The server is expected to always include `type` on SDK errors. This only\n * covers a gateway or proxy answering before nocode-service does — an HTML 502 from\n * an edge, say — so the developer still gets a typed error rather than a parse crash.\n */\nfunction inferTypeFromStatus(status: number): OfficelessErrorType {\n if (status === 400 || status === 404) return 'ValidationError'\n if (status === 401 || status === 403 || status === 429) return 'AuthorizationError'\n if (status === 504) return 'WorkflowExecutionError'\n return 'InternalServerError'\n}\n\n/** Builds an `OfficelessError` from a parsed failure envelope and its HTTP status. */\nexport function toOfficelessError(\n envelope: Partial<WireEnvelope<unknown>> | null,\n status: number,\n): OfficelessError {\n const type = envelope?.type ?? inferTypeFromStatus(status)\n const message = envelope?.message ?? `Request failed with status ${status}.`\n return new OfficelessError(type, message, status)\n}\n\n/**\n * Messages the library produces itself, before or instead of a request.\n *\n * `invalidKeyFormat` is the only failure `init` can raise, and it never reaches\n * the wire. The other two are client-side guards, and repeat\n * the wording the server uses for the same situation, so a developer reads one\n * message either way.\n */\nexport const CLIENT_MESSAGES = {\n notSignedIn: 'This function requires a signed-in user. Sign in with auth.login first.',\n sessionExpired: 'Session expired. Please log in again.',\n invalidKeyFormat: 'That SDK key is not in the expected format.',\n} as const\n","import type { StoredSession } from './types'\n\n/**\n * Persistence contract for the signed-in session.\n *\n * Deliberately internal — not surfaced on `init()`. Keeping it an interface means a\n * future server build supplies its own implementation instead of forcing a rewrite.\n */\nexport interface SessionStore {\n save(session: StoredSession): Promise<void>\n load(): Promise<StoredSession | null>\n clear(): Promise<void>\n}\n\nconst DB_NAME = 'officeless-sdk'\nconst DB_VERSION = 1\nconst KEY_STORE = 'crypto-keys'\nconst STORAGE_PREFIX = 'officeless.session.'\n\n/** True once the local clock says the session is past its expiry. */\nexport function isSessionExpired(session: StoredSession): boolean {\n const expiry = Date.parse(session.expiredAt)\n if (Number.isNaN(expiry)) return false\n return expiry <= Date.now()\n}\n\n/**\n * Builds the browser session store.\n */\nexport function createBrowserSessionStore(namespace: string): SessionStore {\n const storageKey = `${STORAGE_PREFIX}${namespace}`\n\n // Used whenever the browser will not give us durable, encryptable storage. The\n // session then simply does not survive a reload, which is a degraded experience —\n // never a thrown error, and never a plaintext fallback.\n let memorySession: StoredSession | null = null\n let degraded = false\n\n function degrade(): null {\n degraded = true\n return null\n }\n\n async function getKey(): Promise<CryptoKey | null> {\n if (degraded) return null\n if (typeof indexedDB === 'undefined' || typeof crypto === 'undefined' || !crypto.subtle) {\n return degrade()\n }\n\n try {\n const db = await openDatabase()\n try {\n const existing = await idbGet<CryptoKey>(db, namespace)\n if (existing) return existing\n\n const created = await crypto.subtle.generateKey(\n { name: 'AES-GCM', length: 256 },\n // Non-extractable: the key can encrypt and decrypt, but its bytes cannot be\n // read back out — not even by our own code.\n false,\n ['encrypt', 'decrypt'],\n )\n await idbPut(db, namespace, created)\n return created\n } finally {\n db.close()\n }\n } catch {\n return degrade()\n }\n }\n\n async function save(session: StoredSession): Promise<void> {\n memorySession = session\n\n const key = await getKey()\n if (!key) return\n\n try {\n const iv = crypto.getRandomValues(new Uint8Array(12))\n const plaintext = new TextEncoder().encode(JSON.stringify(session))\n const ciphertext = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, plaintext)\n localStorage.setItem(storageKey, `${toBase64(iv)}.${toBase64(new Uint8Array(ciphertext))}`)\n } catch {\n // Quota exceeded, storage disabled mid-session, or a crypto failure. The\n // in-memory copy above still serves this page load.\n degraded = true\n }\n }\n\n async function load(): Promise<StoredSession | null> {\n if (memorySession) return memorySession\n\n const key = await getKey()\n if (!key) return null\n\n try {\n const raw = localStorage.getItem(storageKey)\n if (!raw) return null\n\n const separator = raw.indexOf('.')\n if (separator < 1) return null\n\n const iv = fromBase64(raw.slice(0, separator))\n const ciphertext = fromBase64(raw.slice(separator + 1))\n const plaintext = await crypto.subtle.decrypt({ name: 'AES-GCM', iv }, key, ciphertext)\n\n memorySession = JSON.parse(new TextDecoder().decode(plaintext)) as StoredSession\n return memorySession\n } catch {\n // Tampered, truncated, or written by a key we no longer hold. Drop it rather\n // than leaving a value that will fail on every future read.\n await clear()\n return null\n }\n }\n\n async function clear(): Promise<void> {\n memorySession = null\n try {\n localStorage.removeItem(storageKey)\n } catch {\n // Storage unavailable — the in-memory clear above is all that is needed.\n }\n }\n\n return { save, load, clear }\n}\n\n/**\n * The live session, as the rest of the library sees it.\n *\n * Reads are synchronous because the HTTP client needs the access token while\n * assembling headers; writes are async because persistence is. Kept here rather than\n * in a shared context object so each call module can take only what it needs.\n */\nexport interface SessionHandle {\n get(): StoredSession | null\n set(session: StoredSession): Promise<void>\n clear(): Promise<void>\n /** Loads any persisted session into memory. Called once, by init. */\n hydrate(): Promise<void>\n}\n\nexport function createSessionHandle(store: SessionStore): SessionHandle {\n let current: StoredSession | null = null\n\n return {\n get: () => current,\n async set(session) {\n current = session\n await store.save(session)\n },\n async clear() {\n current = null\n await store.clear()\n },\n async hydrate() {\n current = await store.load()\n },\n }\n}\n\n// ── IndexedDB helpers ───────────────────────────────────────────────────────────\n// Only the CryptoKey lives here. A non-extractable CryptoKey survives structured\n// clone, which is what makes this pattern work at all.\n\nfunction openDatabase(): Promise<IDBDatabase> {\n return new Promise((resolve, reject) => {\n const request = indexedDB.open(DB_NAME, DB_VERSION)\n request.onupgradeneeded = () => {\n const db = request.result\n if (!db.objectStoreNames.contains(KEY_STORE)) db.createObjectStore(KEY_STORE)\n }\n request.onsuccess = () => resolve(request.result)\n request.onerror = () => reject(request.error)\n request.onblocked = () => reject(new Error('IndexedDB open blocked'))\n })\n}\n\nfunction idbGet<T>(db: IDBDatabase, key: string): Promise<T | undefined> {\n return new Promise((resolve, reject) => {\n const request = db.transaction(KEY_STORE, 'readonly').objectStore(KEY_STORE).get(key)\n request.onsuccess = () => resolve(request.result as T | undefined)\n request.onerror = () => reject(request.error)\n })\n}\n\nfunction idbPut(db: IDBDatabase, key: string, value: CryptoKey): Promise<void> {\n return new Promise((resolve, reject) => {\n const transaction = db.transaction(KEY_STORE, 'readwrite')\n transaction.objectStore(KEY_STORE).put(value, key)\n transaction.oncomplete = () => resolve()\n transaction.onerror = () => reject(transaction.error)\n transaction.onabort = () => reject(transaction.error)\n })\n}\n\n// ── base64 ──────────────────────────────────────────────────────────────────────\n\nfunction toBase64(bytes: Uint8Array): string {\n let binary = ''\n for (let index = 0; index < bytes.length; index += 1) {\n binary += String.fromCharCode(bytes[index] as number)\n }\n return btoa(binary)\n}\n\n// Backed by an explicit ArrayBuffer so the result is a `Uint8Array<ArrayBuffer>`.\n// `new Uint8Array(length)` widens to `ArrayBufferLike`, which SubtleCrypto's\n// BufferSource parameter rejects — it will not accept a SharedArrayBuffer view.\nfunction fromBase64(value: string): Uint8Array<ArrayBuffer> {\n const binary = atob(value)\n const bytes = new Uint8Array(new ArrayBuffer(binary.length))\n for (let index = 0; index < binary.length; index += 1) {\n bytes[index] = binary.charCodeAt(index)\n }\n return bytes\n}\n","import { CLIENT_MESSAGES, OfficelessError } from '../errors'\nimport type { HttpClient } from '../http-client'\nimport { isSessionExpired, type SessionHandle } from '../session-store'\nimport type { HitFunctionResult, WireRunFunctionData } from '../types'\n\n/**\n * Runs a published automation by name and returns its output.\n *\n * `withAuth` defaults to true and only decides whether the SDK attaches the session.\n * Whether one is actually required is read server-side from the matched automation's\n * own Required Authorization setting, so `withAuth: false` against a function that\n * requires one is still rejected.\n *\n * The automation must have been created with the **SDK Function** trigger; anything\n * else is reported as not found, the same as a name that does not exist.\n */\nexport async function hitSDKFunction<TOutput = unknown>(\n http: HttpClient,\n session: SessionHandle,\n name: string,\n data: Record<string, unknown>,\n withAuth = true,\n): Promise<HitFunctionResult<TOutput>> {\n // a convenience, not the real gate. It only\n // catches the common case of forgetting to sign in; the SDK cannot know a specific\n // function's Required Authorization setting until the server answers.\n if (withAuth) {\n const current = session.get()\n if (!current || isSessionExpired(current)) {\n throw new OfficelessError('AuthorizationError', CLIENT_MESSAGES.notSignedIn, null)\n }\n }\n\n const result = await http.request<WireRunFunctionData>({\n method: 'POST',\n path: '/action/run-function',\n body: { name, data, with_auth: withAuth },\n withAuth,\n })\n\n if (!result) {\n throw new OfficelessError('InternalServerError', 'The function returned no result.', null)\n }\n\n return {\n status: result.status,\n // Passed through untouched on purpose. `output` is whatever the automation\n // returns — the developer's own shape, and any JSON value including null.\n // Rewriting its keys to camelCase (as the SDK does for its own fields) would\n // silently corrupt their data.\n output: result.output as TOutput,\n }\n}\n","import type { HttpClient } from '../http-client'\nimport type { LoginResult, WireLoginData } from '../types'\n\n/**\n * Sends a login code to the email.\n *\n * The endpoint answers 200 whether or not the address belongs to a project user, and\n * does not validate its format either — an unknown or malformed address gets a\n * throwaway `session_id` and no email is sent. The library deliberately\n * adds no client-side email validation on top: doing so would hand back exactly the\n * \"does this address exist\" signal the endpoint is designed to withhold.\n */\nexport async function login(http: HttpClient, email: string): Promise<LoginResult> {\n const data = await http.request<WireLoginData>({\n method: 'POST',\n path: '/auth/login',\n body: { email },\n })\n\n return {\n sessionId: data?.session_id ?? '',\n otpExpiresAt: data?.otp_expires_at ?? '',\n }\n}\n","import type { HttpClient } from '../http-client'\nimport type { SessionHandle } from '../session-store'\n\n/**\n * Ends the session, server-side and locally.\n *\n * A 401 here means the session was already ended or had expired. The library reports\n * that as success, so signing out twice does not throw — the caller's\n * intent, \"this session should not work any more\", is satisfied either way.\n *\n * The local copy is cleared even when the request fails, so the application is never\n * left holding a session it believes is live after asking for it to end.\n */\nexport async function logout(http: HttpClient, session: SessionHandle): Promise<void> {\n if (!session.get()) return\n\n try {\n await http.request<Record<string, never>>({\n method: 'POST',\n path: '/auth/logout',\n withAuth: true,\n treat401AsSuccess: true,\n })\n } finally {\n await session.clear()\n }\n}\n","import { CLIENT_MESSAGES, OfficelessError } from '../errors'\nimport type { HttpClient } from '../http-client'\nimport type { SessionHandle } from '../session-store'\nimport type { RefreshResult, WireRefreshData } from '../types'\n\n/**\n * Renews the session with the renewal token.\n */\nexport async function refresh(http: HttpClient, session: SessionHandle): Promise<RefreshResult> {\n const current = session.get()\n\n if (!current) {\n throw new OfficelessError('AuthorizationError', CLIENT_MESSAGES.sessionExpired, null)\n }\n\n let data: WireRefreshData | null\n try {\n data = await http.request<WireRefreshData>({\n method: 'POST',\n path: '/auth/refresh',\n body: { refresh_token: current.refreshToken },\n })\n } catch (error) {\n // An unknown, used, or expired renewal token means this session is finished.\n // Drop the stored copy so the app is not left holding one that cannot work.\n if (error instanceof OfficelessError && error.status === 401) {\n await session.clear()\n }\n throw error\n }\n\n if (!data) {\n throw new OfficelessError('InternalServerError', 'Refresh did not return a session.', null)\n }\n\n await session.set({\n ...current,\n accessToken: data.access_token,\n refreshToken: data.refresh_token,\n expiredAt: data.expired_at,\n })\n\n return {\n accessToken: data.access_token,\n expiredAt: data.expired_at,\n }\n}\n","import { CLIENT_MESSAGES, OfficelessError } from '../errors'\nimport { isSessionExpired, type SessionHandle } from '../session-store'\nimport type { SdkUser } from '../types'\n\n/**\n * Returns the signed-in person from the session the application already holds.\n */\nexport async function user(session: SessionHandle): Promise<SdkUser> {\n const current = session.get()\n\n if (!current || isSessionExpired(current)) {\n throw new OfficelessError('AuthorizationError', CLIENT_MESSAGES.sessionExpired, null)\n }\n\n return current.user\n}\n","import { OfficelessError } from '../errors'\nimport type { HttpClient } from '../http-client'\nimport type { SessionHandle } from '../session-store'\nimport type { SdkUser, VerifyResult, WireUser, WireVerifyData } from '../types'\n\n/** snake_case → camelCase for the user block. Shared with auth/user.ts. */\nexport function toSdkUser(wire: WireUser): SdkUser {\n return {\n userId: wire.user_id,\n email: wire.email,\n name: wire.name,\n companyId: wire.company_id,\n companyName: wire.company_name,\n }\n}\n\n/**\n * Confirms the code and starts the session.\n */\nexport async function verify(\n http: HttpClient,\n session: SessionHandle,\n email: string,\n sessionId: string,\n code: string,\n): Promise<VerifyResult> {\n const data = await http.request<WireVerifyData>({\n method: 'POST',\n path: '/auth/verify',\n body: { email, session_id: sessionId, code },\n })\n\n if (!data) {\n throw new OfficelessError('InternalServerError', 'Sign-in did not return a session.', null)\n }\n\n const result: VerifyResult = {\n accessToken: data.access_token,\n refreshToken: data.refresh_token,\n expiredAt: data.expired_at,\n user: toSdkUser(data.user),\n }\n\n await session.set(result)\n\n return result\n}\n","import { CLIENT_MESSAGES, OfficelessError, toOfficelessError } from './errors'\nimport type { WireEnvelope } from './types'\n\ndeclare const __SDK_VERSION__: string\n\n/** Injected by tsup at build time from package.json (see tsup.config.ts). */\nconst SDK_VERSION: string = typeof __SDK_VERSION__ === 'string' ? __SDK_VERSION__ : '0.0.0'\n\n/**\n * The shared multi-tenant gateway.\n *\n * The `/v1` is part of it: the gateway mounts every backing service under that\n * prefix and strips it before forwarding, so a request without it never reaches\n * nocode-service and comes back as the gateway's own HTML 404. It belongs here\n * rather than in SDK_BASE_PATH because it describes where this deployment lives,\n * not where the service mounts its routes — a single-tenant gateway may sit at a\n * different prefix, or none.\n */\nexport const DEFAULT_GATEWAY_URL = 'https://officeless-gateway.mekari.com/v1'\n\n/** All SDK endpoints live under this prefix */\nconst SDK_BASE_PATH = '/nocode/sdk/v1'\n\nexport interface HttpClientOptions {\n sdkKey: string\n gatewayUrl: string\n /** Returns the current access token, or null when nobody is signed in. */\n getAccessToken: () => string | null\n}\n\nexport interface RequestOptions {\n method: 'GET' | 'POST'\n path: string\n body?: unknown\n /** Attach `Authorization` when a token is available. Defaults to false. */\n withAuth?: boolean\n /** Overrides the message used when the request never reaches the server. */\n networkErrorMessage?: string\n /**\n * Treat a 401 as success and resolve with `null`.\n */\n treat401AsSuccess?: boolean\n}\n\nexport interface HttpClient {\n request<TData>(options: RequestOptions): Promise<TData | null>\n}\n\nexport function createHttpClient({\n sdkKey,\n gatewayUrl,\n getAccessToken,\n}: HttpClientOptions): HttpClient {\n const origin = gatewayUrl.replace(/\\/+$/, '')\n\n async function request<TData>(options: RequestOptions): Promise<TData | null> {\n const { method, path, body, withAuth = false, networkErrorMessage, treat401AsSuccess } = options\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n // The company, project and stage all derive from this server-side (§2.4).\n 'X-Officeless-Sdk-Key': sdkKey,\n // Recorded in the app log and copied into the session token's `sdk` claim.\n // Untrusted server-side — never used for an access decision (§3.4).\n 'X-Officeless-Sdk-Version': SDK_VERSION,\n }\n\n if (withAuth) {\n const token = getAccessToken()\n if (token) headers['Authorization'] = `Bearer ${token}`\n }\n\n let response: Response\n try {\n response = await fetch(`${origin}${SDK_BASE_PATH}${path}`, {\n method,\n headers,\n body: body === undefined ? undefined : JSON.stringify(body),\n })\n } catch {\n // Network-level failure: DNS, offline, CORS preflight rejected. No status.\n throw new OfficelessError(\n 'InternalServerError',\n networkErrorMessage ?? 'Could not reach Officeless. Check the network and try again.',\n null,\n )\n }\n\n if (response.status === 401 && treat401AsSuccess) return null\n\n const envelope = await readEnvelope<TData>(response)\n\n if (!response.ok || envelope?.error) {\n throw toOfficelessError(envelope, response.status)\n }\n\n return envelope?.data ?? null\n }\n\n return { request }\n}\n\n/**\n * Parses the envelope, tolerating a non-JSON body.\n *\n * A gateway or proxy can answer before nocode-service does — an HTML error page from\n * an edge, say. Returning null there lets the caller fall back to status-based typing\n * instead of throwing a JSON parse error the developer cannot act on.\n */\nasync function readEnvelope<TData>(response: Response): Promise<WireEnvelope<TData> | null> {\n try {\n return (await response.json()) as WireEnvelope<TData>\n } catch {\n return null\n }\n}\n\nexport { CLIENT_MESSAGES }\n","// created — MOB-9706\n// Reason: officeless.init() — instance assembly (RFC §1.7, §2.2 Flow 3).\nimport { hitSDKFunction } from './action/hitSDKFunction'\nimport { login } from './auth/login'\nimport { logout } from './auth/logout'\nimport { refresh } from './auth/refresh'\nimport { user } from './auth/user'\nimport { verify } from './auth/verify'\nimport { CLIENT_MESSAGES, OfficelessError } from './errors'\nimport { createHttpClient, DEFAULT_GATEWAY_URL } from './http-client'\nimport { createBrowserSessionStore, createSessionHandle } from './session-store'\nimport type { InitOptions, OfficelessApp } from './types'\n\n/**\n * The stage text is matched loosely on purpose. A stage added after this phase\n * must not make an already-published package reject a valid key, and the server\n * never reads the stage from that text — it takes it from the key's own `stage`\n * field. This is a typo check, not an authorization check: a well-formed value\n * that belongs to no key passes it and fails on the first real call.\n */\nconst SDK_KEY_PATTERN = /^ofc_sdk_[a-z]+_[0-9a-f]{32}$/\n\n/**\n * Sets the library up and returns an instance bound to one key.\n *\n * Sends nothing to Officeless (RFC §2.2 Flow 3). Nothing is checked against the\n * platform here — not the key, not the origin, not the release toggle — so setup\n * cannot fail on the network and adds nothing to application startup time. A\n * wrong, revoked or deleted key, a domain not on the project's list, and a\n * company without the release toggle are all reported by the first call that\n * reaches Officeless: `login`, `verify`, `refresh`, `logout`, or\n * `hitSDKFunction`.\n *\n * It also returns no project data. The key's company, project and stage are\n * never sent to the browser.\n *\n * Still async even though it waits on nothing, so a later phase can add a server\n * round trip without a breaking change to a package already published.\n *\n * Calling it again with a different key returns another, independent instance — a\n * page can hold more than one at a time (RFC §1.7), and each namespaces its\n * stored session by key so they cannot overwrite each other.\n *\n * Browser only. RFC §1.3 puts server and backend use out of scope for this phase:\n * the domain-allowlist control in §3.4 depends on `Origin` being set by a browser.\n */\nexport async function init(options: InitOptions): Promise<OfficelessApp> {\n const sdkKey = options.sdkKey?.trim() ?? ''\n\n if (!SDK_KEY_PATTERN.test(sdkKey)) {\n throw new OfficelessError('ValidationError', CLIENT_MESSAGES.invalidKeyFormat, null)\n }\n\n const gatewayUrl = options.gatewayUrl?.trim() || DEFAULT_GATEWAY_URL\n\n // Namespaced by the key itself, not by project and stage: those are no longer\n // known in the browser, and the key is what an instance is bound to anyway.\n const session = createSessionHandle(createBrowserSessionStore(sdkKey))\n\n const http = createHttpClient({\n sdkKey,\n gatewayUrl,\n getAccessToken: () => {\n const current = session.get()\n // The null arm is unreachable through the public API today: every call\n // that asks for auth — logout, and hitSDKFunction with withAuth on —\n // already refuses earlier when no one is signed in. It stays because the\n // http client's contract allows null, and dropping it would make this\n // closure lie about that.\n /* v8 ignore next */\n return current ? current.accessToken : null\n },\n })\n\n // Bring back a session from a previous page load, if there is one. This reads\n // local storage only — still no call to Officeless.\n await session.hydrate()\n\n return {\n auth: {\n login: (email) => login(http, email),\n verify: (email, sessionId, code) => verify(http, session, email, sessionId, code),\n user: () => user(session),\n refresh: () => refresh(http, session),\n logout: () => logout(http, session),\n },\n action: {\n hitSDKFunction: (name, payload, withAuth) =>\n hitSDKFunction(http, session, name, payload, withAuth),\n },\n }\n}\n","import { init } from './init'\n\n/**\n * The Officeless SDK.\n *\n * ```ts\n * import { officeless } from '@mekari-officeless/sdk'\n *\n * const app = await officeless.init({ sdkKey: 'ofc_sdk_prod_…' })\n * const { sessionId } = await app.auth.login('user@example.com')\n * await app.auth.verify('user@example.com', sessionId, '482913')\n * const result = await app.action.hitSDKFunction('calculate_payroll', { month: '2026-08' })\n * ```\n */\nexport const officeless = { init } as const\n\nexport { OfficelessError } from './errors'\n\nexport type {\n HitFunctionResult,\n InitOptions,\n LoginResult,\n OfficelessAction,\n OfficelessApp,\n OfficelessAuth,\n OfficelessErrorType,\n RefreshResult,\n SdkUser,\n VerifyResult,\n} from './types'\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
type OfficelessErrorType = 'AuthorizationError' | 'ValidationError' | 'WorkflowExecutionError' | 'InternalServerError';
|
|
2
|
+
interface InitOptions {
|
|
3
|
+
/** The SDK key created in Studio under Settings › Developer access. */
|
|
4
|
+
sdkKey: string;
|
|
5
|
+
/**
|
|
6
|
+
* Where to send calls. Defaults to the shared multi-tenant gateway.
|
|
7
|
+
*
|
|
8
|
+
* Set this once as part of your own deployment configuration. Never source it
|
|
9
|
+
* from user input or a remotely-fetched config: the SDK key and, after sign-in,
|
|
10
|
+
* live session tokens are sent to whatever host this resolves to (RFC §3.4).
|
|
11
|
+
*/
|
|
12
|
+
gatewayUrl?: string;
|
|
13
|
+
}
|
|
14
|
+
interface LoginResult {
|
|
15
|
+
sessionId: string;
|
|
16
|
+
otpExpiresAt: string;
|
|
17
|
+
}
|
|
18
|
+
interface SdkUser {
|
|
19
|
+
userId: string;
|
|
20
|
+
email: string;
|
|
21
|
+
name: string;
|
|
22
|
+
companyId: number;
|
|
23
|
+
companyName: string;
|
|
24
|
+
}
|
|
25
|
+
interface VerifyResult {
|
|
26
|
+
accessToken: string;
|
|
27
|
+
refreshToken: string;
|
|
28
|
+
expiredAt: string;
|
|
29
|
+
user: SdkUser;
|
|
30
|
+
}
|
|
31
|
+
interface RefreshResult {
|
|
32
|
+
accessToken: string;
|
|
33
|
+
expiredAt: string;
|
|
34
|
+
}
|
|
35
|
+
interface HitFunctionResult<TOutput = unknown> {
|
|
36
|
+
status: string;
|
|
37
|
+
/** Whatever the automation returns — any JSON value, including `null`. */
|
|
38
|
+
output: TOutput;
|
|
39
|
+
}
|
|
40
|
+
interface OfficelessAuth {
|
|
41
|
+
/**
|
|
42
|
+
* Sends a login code to the email and returns a `sessionId` for this attempt.
|
|
43
|
+
*
|
|
44
|
+
* Resolves the same way for a malformed address, an address with no account, and
|
|
45
|
+
* a real one.
|
|
46
|
+
*/
|
|
47
|
+
login(email: string): Promise<LoginResult>;
|
|
48
|
+
/** Confirms the code and starts the session. */
|
|
49
|
+
verify(email: string, sessionId: string, code: string): Promise<VerifyResult>;
|
|
50
|
+
/** Returns the signed-in person from the held session. Sends no request. */
|
|
51
|
+
user(): Promise<SdkUser>;
|
|
52
|
+
/** Renews the session with the renewal token. */
|
|
53
|
+
refresh(): Promise<RefreshResult>;
|
|
54
|
+
/** Ends the session. Calling it again on an already-ended session does not throw. */
|
|
55
|
+
logout(): Promise<void>;
|
|
56
|
+
}
|
|
57
|
+
interface OfficelessAction {
|
|
58
|
+
/**
|
|
59
|
+
* Runs a published automation by name and returns its output.
|
|
60
|
+
*
|
|
61
|
+
* `withAuth` defaults to `true` and only controls whether the SDK attaches the
|
|
62
|
+
* session. Whether one is actually required is decided by the workflow's own
|
|
63
|
+
* Required Authorization setting, server-side.
|
|
64
|
+
*/
|
|
65
|
+
hitSDKFunction<TOutput = unknown>(name: string, data: Record<string, unknown>, withAuth?: boolean): Promise<HitFunctionResult<TOutput>>;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* An instance bound to one key.
|
|
69
|
+
*
|
|
70
|
+
* It carries no project id, name or stage: setup asks Officeless nothing, so the
|
|
71
|
+
* browser is never told which project the key belongs to (RFC §2.2 Flow 3). The
|
|
72
|
+
* server reads all three from the key on every later call.
|
|
73
|
+
*/
|
|
74
|
+
interface OfficelessApp {
|
|
75
|
+
auth: OfficelessAuth;
|
|
76
|
+
action: OfficelessAction;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Sets the library up and returns an instance bound to one key.
|
|
81
|
+
*
|
|
82
|
+
* Sends nothing to Officeless (RFC §2.2 Flow 3). Nothing is checked against the
|
|
83
|
+
* platform here — not the key, not the origin, not the release toggle — so setup
|
|
84
|
+
* cannot fail on the network and adds nothing to application startup time. A
|
|
85
|
+
* wrong, revoked or deleted key, a domain not on the project's list, and a
|
|
86
|
+
* company without the release toggle are all reported by the first call that
|
|
87
|
+
* reaches Officeless: `login`, `verify`, `refresh`, `logout`, or
|
|
88
|
+
* `hitSDKFunction`.
|
|
89
|
+
*
|
|
90
|
+
* It also returns no project data. The key's company, project and stage are
|
|
91
|
+
* never sent to the browser.
|
|
92
|
+
*
|
|
93
|
+
* Still async even though it waits on nothing, so a later phase can add a server
|
|
94
|
+
* round trip without a breaking change to a package already published.
|
|
95
|
+
*
|
|
96
|
+
* Calling it again with a different key returns another, independent instance — a
|
|
97
|
+
* page can hold more than one at a time (RFC §1.7), and each namespaces its
|
|
98
|
+
* stored session by key so they cannot overwrite each other.
|
|
99
|
+
*
|
|
100
|
+
* Browser only. RFC §1.3 puts server and backend use out of scope for this phase:
|
|
101
|
+
* the domain-allowlist control in §3.4 depends on `Origin` being set by a browser.
|
|
102
|
+
*/
|
|
103
|
+
declare function init(options: InitOptions): Promise<OfficelessApp>;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Every SDK failure is thrown as one of these.
|
|
107
|
+
*
|
|
108
|
+
* Branch on `type` rather than `message`, while
|
|
109
|
+
* messages are human-facing. `status` is exposed alongside so a 429 can be told
|
|
110
|
+
* apart from a 401 — both are `AuthorizationError`
|
|
111
|
+
*/
|
|
112
|
+
declare class OfficelessError extends Error {
|
|
113
|
+
readonly type: OfficelessErrorType;
|
|
114
|
+
/** HTTP status, or `null` when the call never reached the server. */
|
|
115
|
+
readonly status: number | null;
|
|
116
|
+
constructor(type: OfficelessErrorType, message: string, status?: number | null);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* The Officeless SDK.
|
|
121
|
+
*
|
|
122
|
+
* ```ts
|
|
123
|
+
* import { officeless } from '@mekari-officeless/sdk'
|
|
124
|
+
*
|
|
125
|
+
* const app = await officeless.init({ sdkKey: 'ofc_sdk_prod_…' })
|
|
126
|
+
* const { sessionId } = await app.auth.login('user@example.com')
|
|
127
|
+
* await app.auth.verify('user@example.com', sessionId, '482913')
|
|
128
|
+
* const result = await app.action.hitSDKFunction('calculate_payroll', { month: '2026-08' })
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
declare const officeless: {
|
|
132
|
+
readonly init: typeof init;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
export { type HitFunctionResult, type InitOptions, type LoginResult, type OfficelessAction, type OfficelessApp, type OfficelessAuth, OfficelessError, type OfficelessErrorType, type RefreshResult, type SdkUser, type VerifyResult, officeless };
|