@astralbeam/sdk 0.9.0 → 0.11.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/README.md +74 -70
- package/dist/api-CWgQSDaI.d.ts +358 -0
- package/dist/api-DrKgAIBT.js +309 -0
- package/dist/api.d.ts +2 -0
- package/dist/api.js +2 -0
- package/dist/client.d.ts +93 -4
- package/dist/client.js +1 -1
- package/dist/constants-CgaMxV3q.js +1 -0
- package/dist/{core-Eh2vyhUP.js → core-xh4NLb1l.js} +306 -92
- package/dist/core.d.ts +1 -1
- package/dist/core.js +1 -1
- package/dist/debug-qLts7gEt.js +1 -0
- package/dist/index-BVVgDSdq.d.ts +1 -0
- package/dist/{index-BQBdZKyB.d.ts → index-C4uH8CV1.d.ts} +12 -5
- package/dist/listings-DVfsUaPv.js +21 -0
- package/dist/react.d.ts +60 -4
- package/dist/react.js +112 -19
- package/dist/server.d.ts +15 -1
- package/dist/server.js +24 -122
- package/dist/styles.generated-Cf-ab39H.js +60 -0
- package/dist/type_checks-C-WTHaWs.js +117 -0
- package/dist/widget-CrC3teP8.js +20 -0
- package/package.json +64 -38
- package/dist/debug-66ETHfXh.js +0 -1
- package/dist/widget-DLUt14KM.js +0 -79
package/dist/react.js
CHANGED
|
@@ -1,8 +1,87 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as CORE_OPTION_KEYS, r as createAstralBeamChat } from "./core-xh4NLb1l.js";
|
|
2
|
+
import { A as DEFAULT_COLOR_SCHEME } from "./api-DrKgAIBT.js";
|
|
2
3
|
import { forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { defineTool, mountAstralBeamChat, mountAstralBeamTenantList, mountAstralBeamTenantUserList } from "@astralbeam/sdk/client";
|
|
3
6
|
import { createPortal } from "react-dom";
|
|
4
|
-
|
|
5
|
-
|
|
7
|
+
//#region src/react/listings.tsx
|
|
8
|
+
const AstralBeamTenantList = forwardRef(function AstralBeamTenantList({ className, style, ...options }, ref) {
|
|
9
|
+
return /* @__PURE__ */ jsx("div", {
|
|
10
|
+
ref: useListingMount(mountAstralBeamTenantList, options, ref),
|
|
11
|
+
className,
|
|
12
|
+
style
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
const AstralBeamTenantUserList = forwardRef(function AstralBeamTenantUserList({ className, style, ...options }, ref) {
|
|
16
|
+
return /* @__PURE__ */ jsx("div", {
|
|
17
|
+
ref: useListingMount(mountAstralBeamTenantUserList, options, ref),
|
|
18
|
+
className,
|
|
19
|
+
style
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
function useListingMount(mount, options, ref) {
|
|
23
|
+
const target = useRef(null);
|
|
24
|
+
const handle = useRef(null);
|
|
25
|
+
const { apiUrl, fetchAstralBeamToken, scope, tenantId, tenantExternalId, showAdmin, pageSize, title, showHeader, customCss, colorScheme, theme, onError, onTenantChange, onTenantSelect, onTenantUserSelect } = options;
|
|
26
|
+
const live = useMemo(() => ({
|
|
27
|
+
apiUrl,
|
|
28
|
+
fetchAstralBeamToken,
|
|
29
|
+
scope,
|
|
30
|
+
tenantId,
|
|
31
|
+
tenantExternalId,
|
|
32
|
+
showAdmin,
|
|
33
|
+
pageSize,
|
|
34
|
+
title,
|
|
35
|
+
showHeader,
|
|
36
|
+
customCss,
|
|
37
|
+
colorScheme,
|
|
38
|
+
theme,
|
|
39
|
+
onError,
|
|
40
|
+
onTenantChange,
|
|
41
|
+
onTenantSelect,
|
|
42
|
+
onTenantUserSelect
|
|
43
|
+
}), [
|
|
44
|
+
apiUrl,
|
|
45
|
+
fetchAstralBeamToken,
|
|
46
|
+
scope,
|
|
47
|
+
tenantId,
|
|
48
|
+
tenantExternalId,
|
|
49
|
+
showAdmin,
|
|
50
|
+
pageSize,
|
|
51
|
+
title,
|
|
52
|
+
showHeader,
|
|
53
|
+
customCss,
|
|
54
|
+
colorScheme,
|
|
55
|
+
theme,
|
|
56
|
+
onError,
|
|
57
|
+
onTenantChange,
|
|
58
|
+
onTenantSelect,
|
|
59
|
+
onTenantUserSelect
|
|
60
|
+
]);
|
|
61
|
+
const liveRef = useRef(live);
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
liveRef.current = live;
|
|
64
|
+
});
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
const mounted = mount(target.current, liveRef.current);
|
|
67
|
+
handle.current = mounted;
|
|
68
|
+
return () => {
|
|
69
|
+
handle.current = null;
|
|
70
|
+
mounted.unmount();
|
|
71
|
+
};
|
|
72
|
+
}, [mount]);
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
handle.current?.update(live);
|
|
75
|
+
}, [live]);
|
|
76
|
+
useImperativeHandle(ref, () => ({
|
|
77
|
+
update: (next) => handle.current?.update(next),
|
|
78
|
+
refresh: () => handle.current?.refresh(),
|
|
79
|
+
reset: () => handle.current?.reset(),
|
|
80
|
+
unmount: () => handle.current?.unmount()
|
|
81
|
+
}), []);
|
|
82
|
+
return target;
|
|
83
|
+
}
|
|
84
|
+
//#endregion
|
|
6
85
|
//#region src/react/index.tsx
|
|
7
86
|
/** Declares a host widget; a Standard Schema `parameters` types (and validates) `render`'s props. */
|
|
8
87
|
function defineWidget(widget) {
|
|
@@ -15,13 +94,19 @@ function defineWidget(widget) {
|
|
|
15
94
|
* state stay live and nothing needs a remount.
|
|
16
95
|
*/
|
|
17
96
|
function useAstralBeamChat(options) {
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
const
|
|
97
|
+
const [core] = useState(() => createAstralBeamChat(options, true));
|
|
98
|
+
const watched = CORE_OPTION_KEYS.map((key) => options[key]);
|
|
99
|
+
const watchedRef = useRef(void 0);
|
|
21
100
|
useEffect(() => {
|
|
101
|
+
const previous = watchedRef.current;
|
|
102
|
+
watchedRef.current = watched;
|
|
103
|
+
if (previous?.every((value, index) => Object.is(value, watched[index]))) return;
|
|
22
104
|
core.updateOptions(options);
|
|
23
|
-
}
|
|
24
|
-
useEffect(() =>
|
|
105
|
+
});
|
|
106
|
+
useEffect(() => {
|
|
107
|
+
core.start();
|
|
108
|
+
return () => core.dispose();
|
|
109
|
+
}, [core]);
|
|
25
110
|
return {
|
|
26
111
|
...useSyncExternalStore(core.subscribe, core.getState, core.getState),
|
|
27
112
|
sendMessage: core.sendMessage,
|
|
@@ -32,7 +117,7 @@ function useAstralBeamChat(options) {
|
|
|
32
117
|
core
|
|
33
118
|
};
|
|
34
119
|
}
|
|
35
|
-
const AstralBeamChat = forwardRef(function AstralBeamChat({ agentId, title, showHeader, header, empty, composerActions, emptyTitle, emptyDescription, apiUrl, fetchAstralBeamToken, tools, widgets = {}, colorScheme = DEFAULT_COLOR_SCHEME, theme, attachments, sandboxPanel, debug }, ref) {
|
|
120
|
+
const AstralBeamChat = forwardRef(function AstralBeamChat({ agentId, title, showHeader, header, empty, composerActions, emptyTitle, emptyDescription, apiUrl, fetchAstralBeamToken, tools, widgets = {}, colorScheme = DEFAULT_COLOR_SCHEME, theme, customCss, attachments, sandboxPanel, debug }, ref) {
|
|
36
121
|
const targetRef = useRef(null);
|
|
37
122
|
const handleRef = useRef(null);
|
|
38
123
|
const [activeRenders, setActiveRenders] = useState(/* @__PURE__ */ new Map());
|
|
@@ -45,14 +130,18 @@ const AstralBeamChat = forwardRef(function AstralBeamChat({ agentId, title, show
|
|
|
45
130
|
toolsRef.current = tools;
|
|
46
131
|
});
|
|
47
132
|
const nextRenderKey = useRef(0);
|
|
48
|
-
const hostTools = useMemo(() =>
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
133
|
+
const hostTools = useMemo(() => {
|
|
134
|
+
const adapted = {};
|
|
135
|
+
for (const [name, definition] of Object.entries(tools ?? {})) adapted[name] = {
|
|
136
|
+
...definition,
|
|
137
|
+
execute: (input) => {
|
|
138
|
+
const current = toolsRef.current?.[name];
|
|
139
|
+
if (!current) throw new Error(`Tool "${name}" is no longer registered`);
|
|
140
|
+
return current.execute(input);
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
return adapted;
|
|
144
|
+
}, [tools]);
|
|
56
145
|
const hostWidgets = useMemo(() => Object.fromEntries(Object.entries(widgets).map(([name, definition]) => [name, {
|
|
57
146
|
...definition,
|
|
58
147
|
render: (props, container) => {
|
|
@@ -106,6 +195,7 @@ const AstralBeamChat = forwardRef(function AstralBeamChat({ agentId, title, show
|
|
|
106
195
|
emptyDescription,
|
|
107
196
|
colorScheme,
|
|
108
197
|
theme,
|
|
198
|
+
customCss,
|
|
109
199
|
attachments,
|
|
110
200
|
sandboxPanel,
|
|
111
201
|
debug,
|
|
@@ -122,6 +212,7 @@ const AstralBeamChat = forwardRef(function AstralBeamChat({ agentId, title, show
|
|
|
122
212
|
emptyDescription,
|
|
123
213
|
colorScheme,
|
|
124
214
|
theme,
|
|
215
|
+
customCss,
|
|
125
216
|
attachments,
|
|
126
217
|
sandboxPanel,
|
|
127
218
|
debug,
|
|
@@ -130,7 +221,9 @@ const AstralBeamChat = forwardRef(function AstralBeamChat({ agentId, title, show
|
|
|
130
221
|
chromeSlots
|
|
131
222
|
]);
|
|
132
223
|
const liveRef = useRef(live);
|
|
133
|
-
|
|
224
|
+
useEffect(() => {
|
|
225
|
+
liveRef.current = live;
|
|
226
|
+
});
|
|
134
227
|
useEffect(() => {
|
|
135
228
|
if (!targetRef.current) return;
|
|
136
229
|
const handle = mountAstralBeamChat(targetRef.current, liveRef.current);
|
|
@@ -158,4 +251,4 @@ const AstralBeamChat = forwardRef(function AstralBeamChat({ agentId, title, show
|
|
|
158
251
|
});
|
|
159
252
|
});
|
|
160
253
|
//#endregion
|
|
161
|
-
export { AstralBeamChat, defineTool, defineWidget, useAstralBeamChat };
|
|
254
|
+
export { AstralBeamChat, AstralBeamTenantList, AstralBeamTenantUserList, defineTool, defineWidget, useAstralBeamChat };
|
package/dist/server.d.ts
CHANGED
|
@@ -31,6 +31,20 @@ interface CreateAstralBeamTokenOptions<TTenantUser extends TenantUser = TenantUs
|
|
|
31
31
|
readonly tenant: TTenant;
|
|
32
32
|
readonly expiresInSeconds?: number | undefined;
|
|
33
33
|
}
|
|
34
|
+
interface CreateAstralBeamOrganizationTokenOptions {
|
|
35
|
+
readonly apiKey: string;
|
|
36
|
+
/** Host-authenticated email of an existing organization member, never browser-supplied identity. */
|
|
37
|
+
readonly email: string;
|
|
38
|
+
readonly organizationId: string;
|
|
39
|
+
readonly expiresInSeconds?: number | undefined;
|
|
40
|
+
}
|
|
41
|
+
/** Delegates a member's current database permissions. Does not restrict the API-key holder's authority. */
|
|
42
|
+
declare function createAstralBeamOrganizationToken({
|
|
43
|
+
apiKey,
|
|
44
|
+
email,
|
|
45
|
+
organizationId,
|
|
46
|
+
expiresInSeconds
|
|
47
|
+
}: CreateAstralBeamOrganizationTokenOptions): Promise<string>;
|
|
34
48
|
/** Creates the short-lived bearer token returned by an application's server auth endpoint. */
|
|
35
49
|
declare function createAstralBeamToken<TTenantUser extends TenantUser = TenantUser, TTenant extends Tenant = Tenant>({
|
|
36
50
|
apiKey,
|
|
@@ -39,4 +53,4 @@ declare function createAstralBeamToken<TTenantUser extends TenantUser = TenantUs
|
|
|
39
53
|
expiresInSeconds
|
|
40
54
|
}: CreateAstralBeamTokenOptions<TTenantUser, TTenant>): Promise<string>;
|
|
41
55
|
//#endregion
|
|
42
|
-
export { CHAT_AUTH_TOKEN_AUDIENCE, CHAT_AUTH_TOKEN_LIFETIME_SECONDS, CHAT_AUTH_TOKEN_MAX_LIFETIME_SECONDS, CHAT_AUTH_TOKEN_TYPE, CHAT_AUTH_TOKEN_VERSION, CreateAstralBeamTokenOptions, JsonMetadata, JsonValue, Tenant, TenantUser, createAstralBeamToken };
|
|
56
|
+
export { CHAT_AUTH_TOKEN_AUDIENCE, CHAT_AUTH_TOKEN_LIFETIME_SECONDS, CHAT_AUTH_TOKEN_MAX_LIFETIME_SECONDS, CHAT_AUTH_TOKEN_TYPE, CHAT_AUTH_TOKEN_VERSION, CreateAstralBeamOrganizationTokenOptions, CreateAstralBeamTokenOptions, JsonMetadata, JsonValue, Tenant, TenantUser, createAstralBeamOrganizationToken, createAstralBeamToken };
|
package/dist/server.js
CHANGED
|
@@ -1,27 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
const encoder = new TextEncoder();
|
|
3
|
-
const decoder = new TextDecoder();
|
|
4
|
-
new TextDecoder("utf-8", { fatal: true });
|
|
5
|
-
function concat(...buffers) {
|
|
6
|
-
const size = buffers.reduce((acc, { length }) => acc + length, 0);
|
|
7
|
-
const buf = new Uint8Array(size);
|
|
8
|
-
let i = 0;
|
|
9
|
-
for (const buffer of buffers) {
|
|
10
|
-
buf.set(buffer, i);
|
|
11
|
-
i += buffer.length;
|
|
12
|
-
}
|
|
13
|
-
return buf;
|
|
14
|
-
}
|
|
15
|
-
function encode$1(string) {
|
|
16
|
-
const bytes = new Uint8Array(string.length);
|
|
17
|
-
for (let i = 0; i < string.length; i++) {
|
|
18
|
-
const code = string.charCodeAt(i);
|
|
19
|
-
if (code > 127) throw new TypeError("non-ASCII string encountered in encode()");
|
|
20
|
-
bytes[i] = code;
|
|
21
|
-
}
|
|
22
|
-
return bytes;
|
|
23
|
-
}
|
|
24
|
-
//#endregion
|
|
1
|
+
import { a as isPublicJWK, c as encode, d as JWTInvalid, f as concat, i as isPrivateJWK, l as JOSENotSupported, m as encoder, n as isJWK, o as isSecretJWK, p as encode$1, r as isObject, s as decode, t as isDisjoint, u as JWSInvalid } from "./type_checks-C-WTHaWs.js";
|
|
25
2
|
//#region node_modules/.deno/jose@6.2.9/node_modules/jose/dist/webapi/lib/crypto_key.js
|
|
26
3
|
const unusable = (name, prop = "algorithm.name") => /* @__PURE__ */ new TypeError(`CryptoKey does not support this operation, its ${prop} must be ${name}`);
|
|
27
4
|
function checkUsage(key, usage) {
|
|
@@ -56,29 +33,6 @@ function message(msg, actual, ...types) {
|
|
|
56
33
|
}
|
|
57
34
|
const withAlg = (alg, actual, ...types) => message(`Key for the ${alg} algorithm must be `, actual, ...types);
|
|
58
35
|
//#endregion
|
|
59
|
-
//#region node_modules/.deno/jose@6.2.9/node_modules/jose/dist/webapi/util/errors.js
|
|
60
|
-
var JOSEError = class extends Error {
|
|
61
|
-
static code = "ERR_JOSE_GENERIC";
|
|
62
|
-
code = "ERR_JOSE_GENERIC";
|
|
63
|
-
constructor(message, options) {
|
|
64
|
-
super(message, options);
|
|
65
|
-
this.name = this.constructor.name;
|
|
66
|
-
Error.captureStackTrace?.(this, this.constructor);
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
var JOSENotSupported = class extends JOSEError {
|
|
70
|
-
static code = "ERR_JOSE_NOT_SUPPORTED";
|
|
71
|
-
code = "ERR_JOSE_NOT_SUPPORTED";
|
|
72
|
-
};
|
|
73
|
-
var JWSInvalid = class extends JOSEError {
|
|
74
|
-
static code = "ERR_JWS_INVALID";
|
|
75
|
-
code = "ERR_JWS_INVALID";
|
|
76
|
-
};
|
|
77
|
-
var JWTInvalid = class extends JOSEError {
|
|
78
|
-
static code = "ERR_JWT_INVALID";
|
|
79
|
-
code = "ERR_JWT_INVALID";
|
|
80
|
-
};
|
|
81
|
-
//#endregion
|
|
82
36
|
//#region node_modules/.deno/jose@6.2.9/node_modules/jose/dist/webapi/lib/is_key_like.js
|
|
83
37
|
const isCryptoKey = (key) => {
|
|
84
38
|
if (key?.[Symbol.toStringTag] === "CryptoKey") return true;
|
|
@@ -91,75 +45,6 @@ const isCryptoKey = (key) => {
|
|
|
91
45
|
const isKeyObject = (key) => key?.[Symbol.toStringTag] === "KeyObject";
|
|
92
46
|
const isKeyLike = (key) => isCryptoKey(key) || isKeyObject(key);
|
|
93
47
|
//#endregion
|
|
94
|
-
//#region node_modules/.deno/jose@6.2.9/node_modules/jose/dist/webapi/lib/base64.js
|
|
95
|
-
function encodeBase64(input) {
|
|
96
|
-
if (Uint8Array.prototype.toBase64) return input.toBase64();
|
|
97
|
-
const CHUNK_SIZE = 32768;
|
|
98
|
-
const arr = [];
|
|
99
|
-
for (let i = 0; i < input.length; i += CHUNK_SIZE) arr.push(String.fromCharCode.apply(null, input.subarray(i, i + CHUNK_SIZE)));
|
|
100
|
-
return btoa(arr.join(""));
|
|
101
|
-
}
|
|
102
|
-
function decodeBase64(encoded) {
|
|
103
|
-
if (Uint8Array.fromBase64) return Uint8Array.fromBase64(encoded);
|
|
104
|
-
const binary = atob(encoded);
|
|
105
|
-
const bytes = new Uint8Array(binary.length);
|
|
106
|
-
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
107
|
-
return bytes;
|
|
108
|
-
}
|
|
109
|
-
//#endregion
|
|
110
|
-
//#region node_modules/.deno/jose@6.2.9/node_modules/jose/dist/webapi/util/base64url.js
|
|
111
|
-
const invalid = "The input to be decoded is not correctly encoded.";
|
|
112
|
-
function decode(input) {
|
|
113
|
-
if (Uint8Array.fromBase64) try {
|
|
114
|
-
return Uint8Array.fromBase64(typeof input === "string" ? input : decoder.decode(input), { alphabet: "base64url" });
|
|
115
|
-
} catch (cause) {
|
|
116
|
-
throw new TypeError(invalid, { cause });
|
|
117
|
-
}
|
|
118
|
-
let encoded = input;
|
|
119
|
-
if (encoded instanceof Uint8Array) encoded = decoder.decode(encoded);
|
|
120
|
-
if (encoded.includes("+") || encoded.includes("/")) throw new TypeError(invalid);
|
|
121
|
-
encoded = encoded.replace(/-/g, "+").replace(/_/g, "/");
|
|
122
|
-
try {
|
|
123
|
-
return decodeBase64(encoded);
|
|
124
|
-
} catch {
|
|
125
|
-
throw new TypeError(invalid);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
function encode(input) {
|
|
129
|
-
let unencoded = input;
|
|
130
|
-
if (typeof unencoded === "string") unencoded = encoder.encode(unencoded);
|
|
131
|
-
if (Uint8Array.prototype.toBase64) return unencoded.toBase64({
|
|
132
|
-
alphabet: "base64url",
|
|
133
|
-
omitPadding: true
|
|
134
|
-
});
|
|
135
|
-
return encodeBase64(unencoded).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
|
|
136
|
-
}
|
|
137
|
-
//#endregion
|
|
138
|
-
//#region node_modules/.deno/jose@6.2.9/node_modules/jose/dist/webapi/lib/type_checks.js
|
|
139
|
-
function isObject(input) {
|
|
140
|
-
if (typeof input !== "object" || input === null || Object.prototype.toString.call(input) !== "[object Object]") return false;
|
|
141
|
-
const prototype = Object.getPrototypeOf(input);
|
|
142
|
-
if (prototype === null) return true;
|
|
143
|
-
let proto = prototype;
|
|
144
|
-
while (Object.getPrototypeOf(proto) !== null) proto = Object.getPrototypeOf(proto);
|
|
145
|
-
return prototype === proto;
|
|
146
|
-
}
|
|
147
|
-
function isDisjoint(...headers) {
|
|
148
|
-
const parameters = /* @__PURE__ */ new Set();
|
|
149
|
-
for (const header of headers) {
|
|
150
|
-
if (!header) continue;
|
|
151
|
-
for (const parameter of Object.keys(header)) {
|
|
152
|
-
if (parameters.has(parameter)) return false;
|
|
153
|
-
parameters.add(parameter);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
return true;
|
|
157
|
-
}
|
|
158
|
-
const isJWK = (key) => isObject(key) && typeof key.kty === "string";
|
|
159
|
-
const isPrivateJWK = (key) => key.kty !== "oct" && (key.kty === "AKP" && typeof key.priv === "string" || typeof key.d === "string");
|
|
160
|
-
const isPublicJWK = (key) => key.kty !== "oct" && key.d === void 0 && key.priv === void 0;
|
|
161
|
-
const isSecretJWK = (key) => key.kty === "oct" && typeof key.k === "string";
|
|
162
|
-
//#endregion
|
|
163
48
|
//#region node_modules/.deno/jose@6.2.9/node_modules/jose/dist/webapi/lib/helpers.js
|
|
164
49
|
function assertNotSet(value, name) {
|
|
165
50
|
if (value) throw new TypeError(`${name} can only be called once`);
|
|
@@ -645,7 +530,7 @@ const CHAT_AUTH_TOKEN_MAX_LIFETIME_SECONDS = 600;
|
|
|
645
530
|
const CHAT_AUTH_TOKEN_MAX_BYTES = 16384;
|
|
646
531
|
const IDENTITY_MAX_BYTES = 8192;
|
|
647
532
|
const EXTERNAL_ID_MAX_LENGTH = 255;
|
|
648
|
-
const API_KEY_PATTERN = /^key_[0-9a-
|
|
533
|
+
const API_KEY_PATTERN = /^key_[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}_[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}_abo_[A-Za-z]{64}$/;
|
|
649
534
|
const TENANT_FIELDS = [
|
|
650
535
|
"id",
|
|
651
536
|
"name",
|
|
@@ -659,13 +544,13 @@ const TENANT_USER_FIELDS = [
|
|
|
659
544
|
];
|
|
660
545
|
const textEncoder = new TextEncoder();
|
|
661
546
|
function parseApiKey(apiKey) {
|
|
662
|
-
if (!API_KEY_PATTERN.test(apiKey)) throw new Error("apiKey must match key_<
|
|
547
|
+
if (!API_KEY_PATTERN.test(apiKey)) throw new Error("apiKey must match key_<organizationId>_<id>_abo_<secret>");
|
|
663
548
|
const separator = apiKey.lastIndexOf("_abo_");
|
|
664
549
|
const keyId = apiKey.slice(0, separator);
|
|
665
550
|
const keySecret = apiKey.slice(separator + 1);
|
|
666
551
|
return {
|
|
667
552
|
keyId,
|
|
668
|
-
|
|
553
|
+
organizationId: keyId.slice(4, keyId.indexOf("_", 4)),
|
|
669
554
|
keySecret
|
|
670
555
|
};
|
|
671
556
|
}
|
|
@@ -704,10 +589,27 @@ async function signingKey(secret) {
|
|
|
704
589
|
const digest = await crypto.subtle.digest("SHA-256", textEncoder.encode(secret));
|
|
705
590
|
return textEncoder.encode(encode(new Uint8Array(digest)));
|
|
706
591
|
}
|
|
592
|
+
/** Delegates a member's current database permissions. Does not restrict the API-key holder's authority. */
|
|
593
|
+
async function createAstralBeamOrganizationToken({ apiKey, email, organizationId, expiresInSeconds = 300 }) {
|
|
594
|
+
if (typeof email !== "string" || email.length > 320 || email.includes("\0") || !/^[^\s@]+@[^\s@]+$/.test(email)) throw new Error("email must be an email address of at most 320 characters");
|
|
595
|
+
if (!Number.isInteger(expiresInSeconds) || expiresInSeconds < 60 || expiresInSeconds > 600) throw new Error("organization auth tokens must live for 60-600 seconds");
|
|
596
|
+
const { keyId, organizationId: keyOrganizationId, keySecret } = parseApiKey(apiKey);
|
|
597
|
+
if (organizationId !== keyOrganizationId) throw new Error("organizationId must match the API key organization");
|
|
598
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
599
|
+
return await new SignJWT({
|
|
600
|
+
ver: 1,
|
|
601
|
+
email,
|
|
602
|
+
organization_id: organizationId
|
|
603
|
+
}).setProtectedHeader({
|
|
604
|
+
alg: "HS256",
|
|
605
|
+
typ: "astralbeam-organization+jwt",
|
|
606
|
+
kid: keyId
|
|
607
|
+
}).setIssuer(organizationId).setAudience(CHAT_AUTH_TOKEN_AUDIENCE).setIssuedAt(now).setExpirationTime(now + expiresInSeconds).sign(await signingKey(keySecret));
|
|
608
|
+
}
|
|
707
609
|
/** Creates the short-lived bearer token returned by an application's server auth endpoint. */
|
|
708
610
|
async function createAstralBeamToken({ apiKey, user, tenant, expiresInSeconds = 300 }) {
|
|
709
611
|
if (!Number.isInteger(expiresInSeconds) || expiresInSeconds < 60 || expiresInSeconds > 600) throw new Error("chat auth tokens must live for 60-600 seconds");
|
|
710
|
-
const { keyId,
|
|
612
|
+
const { keyId, organizationId, keySecret } = parseApiKey(apiKey);
|
|
711
613
|
const identity = validatedIdentity(user, tenant);
|
|
712
614
|
const now = Math.floor(Date.now() / 1e3);
|
|
713
615
|
const token = await new SignJWT({
|
|
@@ -718,9 +620,9 @@ async function createAstralBeamToken({ apiKey, user, tenant, expiresInSeconds =
|
|
|
718
620
|
alg: "HS256",
|
|
719
621
|
typ: CHAT_AUTH_TOKEN_TYPE,
|
|
720
622
|
kid: keyId
|
|
721
|
-
}).setIssuer(
|
|
623
|
+
}).setIssuer(organizationId).setAudience(CHAT_AUTH_TOKEN_AUDIENCE).setIssuedAt(now).setExpirationTime(now + expiresInSeconds).sign(await signingKey(keySecret));
|
|
722
624
|
if (textEncoder.encode(token).byteLength > CHAT_AUTH_TOKEN_MAX_BYTES) throw new Error(`chat auth tokens must not exceed ${CHAT_AUTH_TOKEN_MAX_BYTES} bytes`);
|
|
723
625
|
return token;
|
|
724
626
|
}
|
|
725
627
|
//#endregion
|
|
726
|
-
export { CHAT_AUTH_TOKEN_AUDIENCE, CHAT_AUTH_TOKEN_LIFETIME_SECONDS, CHAT_AUTH_TOKEN_MAX_LIFETIME_SECONDS, CHAT_AUTH_TOKEN_TYPE, CHAT_AUTH_TOKEN_VERSION, createAstralBeamToken };
|
|
628
|
+
export { CHAT_AUTH_TOKEN_AUDIENCE, CHAT_AUTH_TOKEN_LIFETIME_SECONDS, CHAT_AUTH_TOKEN_MAX_LIFETIME_SECONDS, CHAT_AUTH_TOKEN_TYPE, CHAT_AUTH_TOKEN_VERSION, createAstralBeamOrganizationToken, createAstralBeamToken };
|