@usearete/sdk 0.32.0 → 0.33.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 +26 -0
- package/dist/index.cjs +131 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +24 -3
- package/dist/index.esm.js +130 -7
- package/dist/index.esm.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,6 +25,32 @@ for await (const item of session.stacks.myStack.views.MyEntity.list.use()) {
|
|
|
25
25
|
}
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
+
## Authentication
|
|
29
|
+
|
|
30
|
+
Pick the key by where the code runs:
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
// Servers, agents and local scripts (Node, Bun, Deno, workers, SSR):
|
|
34
|
+
// an agent key (a4_ak_...) or secret key (a4_sk_...).
|
|
35
|
+
const server = await createSession(
|
|
36
|
+
{ stacks: { myStack: MY_STACK } },
|
|
37
|
+
{ auth: { secretKey: process.env.ARETE_API_KEY } },
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
// Anything shipped to a browser: an origin-bound publishable key (a4_pk_...).
|
|
41
|
+
// Create one with `a4 auth keys create-publishable --origin <scheme://host[:port]>`.
|
|
42
|
+
const browser = await createSession(
|
|
43
|
+
{ stacks: { myStack: MY_STACK } },
|
|
44
|
+
{ auth: { publishableKey: import.meta.env.VITE_ARETE_PUBLISHABLE_KEY } },
|
|
45
|
+
);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Outside a browser, when no `auth` option is set, the SDK reads `ARETE_API_KEY`
|
|
49
|
+
itself, so `createSession({ stacks })` is enough on a server with that variable
|
|
50
|
+
set. `secretKey` throws in a browser, as does a secret or agent key passed as
|
|
51
|
+
`publishableKey`; a publishable key passed as `secretKey` is refused everywhere.
|
|
52
|
+
Keys are never included in error messages or logs.
|
|
53
|
+
|
|
28
54
|
## SDK Shapes
|
|
29
55
|
|
|
30
56
|
### Mental Model
|
package/dist/index.cjs
CHANGED
|
@@ -1043,6 +1043,124 @@ const AUTH_ERROR_CODES_BY_WIRE = {
|
|
|
1043
1043
|
'stack-version-unknown': 'STACK_VERSION_UNKNOWN',
|
|
1044
1044
|
};
|
|
1045
1045
|
|
|
1046
|
+
/** Environment variable read for a secret-class key outside browsers. */
|
|
1047
|
+
const ARETE_API_KEY_ENV = 'ARETE_API_KEY';
|
|
1048
|
+
const CREATE_PUBLISHABLE_HINT = 'create one with `a4 auth keys create-publishable --origin <scheme://host[:port]>`';
|
|
1049
|
+
const PUBLISHABLE_PREFIXES = ['a4_pk_', 'hspk_'];
|
|
1050
|
+
const SECRET_PREFIXES = ['a4_sk_', 'a4_ak_', 'hsk_'];
|
|
1051
|
+
function classifyApiKey(key) {
|
|
1052
|
+
const trimmed = key.trim();
|
|
1053
|
+
if (PUBLISHABLE_PREFIXES.some((prefix) => trimmed.startsWith(prefix)))
|
|
1054
|
+
return 'publishable';
|
|
1055
|
+
if (SECRET_PREFIXES.some((prefix) => trimmed.startsWith(prefix)))
|
|
1056
|
+
return 'secret';
|
|
1057
|
+
return 'unknown';
|
|
1058
|
+
}
|
|
1059
|
+
/**
|
|
1060
|
+
* True in a browser page: both `window` and `document` exist. Web workers,
|
|
1061
|
+
* service workers, edge runtimes, SSR (Node/Bun) and Deno have no `document`
|
|
1062
|
+
* and count as server-side.
|
|
1063
|
+
*/
|
|
1064
|
+
function isBrowserEnvironment() {
|
|
1065
|
+
const scope = globalThis;
|
|
1066
|
+
return typeof scope.window === 'object'
|
|
1067
|
+
&& scope.window !== null
|
|
1068
|
+
&& typeof scope.document === 'object'
|
|
1069
|
+
&& scope.document !== null;
|
|
1070
|
+
}
|
|
1071
|
+
/**
|
|
1072
|
+
* Read an environment variable in Node, Bun or Deno without prompting for or
|
|
1073
|
+
* failing on missing permissions. Browsers have no environment.
|
|
1074
|
+
*/
|
|
1075
|
+
function readEnvironmentVariable(name) {
|
|
1076
|
+
try {
|
|
1077
|
+
const deno = globalThis.Deno;
|
|
1078
|
+
if (deno) {
|
|
1079
|
+
// Reading without --allow-env throws or prompts; ask first.
|
|
1080
|
+
const state = deno.permissions?.querySync?.({ name: 'env', variable: name })?.state;
|
|
1081
|
+
if (state !== 'granted')
|
|
1082
|
+
return undefined;
|
|
1083
|
+
const value = deno.env?.get?.(name);
|
|
1084
|
+
return typeof value === 'string' ? value : undefined;
|
|
1085
|
+
}
|
|
1086
|
+
const value = globalThis
|
|
1087
|
+
.process?.env?.[name];
|
|
1088
|
+
return typeof value === 'string' ? value : undefined;
|
|
1089
|
+
}
|
|
1090
|
+
catch {
|
|
1091
|
+
return undefined;
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
const warned$1 = new Set();
|
|
1095
|
+
function warnOnce$1(id, message) {
|
|
1096
|
+
if (warned$1.has(id))
|
|
1097
|
+
return;
|
|
1098
|
+
warned$1.add(id);
|
|
1099
|
+
console.warn(`[arete] ${message}`);
|
|
1100
|
+
}
|
|
1101
|
+
function hasExplicitAuth(auth) {
|
|
1102
|
+
return auth !== undefined && (auth.token !== undefined
|
|
1103
|
+
|| auth.getToken !== undefined
|
|
1104
|
+
|| auth.tokenEndpoint !== undefined
|
|
1105
|
+
|| auth.publishableKey !== undefined
|
|
1106
|
+
|| auth.secretKey !== undefined);
|
|
1107
|
+
}
|
|
1108
|
+
/**
|
|
1109
|
+
* Validate the configured API keys and apply the `ARETE_API_KEY` fallback.
|
|
1110
|
+
*
|
|
1111
|
+
* - `secretKey` is refused in browsers and refuses publishable keys.
|
|
1112
|
+
* - A secret-class key in `publishableKey` is refused in browsers and warned
|
|
1113
|
+
* about elsewhere (it still works server-side, as it always has).
|
|
1114
|
+
* - Outside browsers, when no auth option is set at all, `ARETE_API_KEY`
|
|
1115
|
+
* supplies `secretKey`.
|
|
1116
|
+
*
|
|
1117
|
+
* Error and warning text never includes key material.
|
|
1118
|
+
*/
|
|
1119
|
+
function resolveAuthConfig(auth) {
|
|
1120
|
+
const browser = isBrowserEnvironment();
|
|
1121
|
+
if (auth?.secretKey !== undefined) {
|
|
1122
|
+
if (browser) {
|
|
1123
|
+
throw new AreteError('auth.secretKey cannot be used in a browser: anything shipped to a browser is public. '
|
|
1124
|
+
+ 'Use auth.publishableKey with a publishable key (a4_pk_...) instead; '
|
|
1125
|
+
+ `${CREATE_PUBLISHABLE_HINT}. Keep secret and agent keys on servers and in local scripts.`, 'INVALID_CONFIG');
|
|
1126
|
+
}
|
|
1127
|
+
if (auth.secretKey.trim() === '') {
|
|
1128
|
+
throw new AreteError('auth.secretKey is empty', 'INVALID_CONFIG');
|
|
1129
|
+
}
|
|
1130
|
+
if (classifyApiKey(auth.secretKey) === 'publishable') {
|
|
1131
|
+
throw new AreteError('auth.secretKey was given a publishable key (a4_pk_...). Pass it as auth.publishableKey '
|
|
1132
|
+
+ 'instead; auth.secretKey takes an agent key (a4_ak_...) or secret key (a4_sk_...).', 'INVALID_CONFIG');
|
|
1133
|
+
}
|
|
1134
|
+
if (auth.publishableKey !== undefined) {
|
|
1135
|
+
throw new AreteError('Set either auth.secretKey (servers and scripts) or auth.publishableKey (browsers), not both.', 'INVALID_CONFIG');
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
if (auth?.publishableKey !== undefined && classifyApiKey(auth.publishableKey) === 'secret') {
|
|
1139
|
+
if (browser) {
|
|
1140
|
+
throw new AreteError('auth.publishableKey was given a secret-class key (a4_sk_... or a4_ak_...). Never ship a '
|
|
1141
|
+
+ 'secret or agent key to a browser. Use a publishable key (a4_pk_...) instead; '
|
|
1142
|
+
+ `${CREATE_PUBLISHABLE_HINT}.`, 'INVALID_CONFIG');
|
|
1143
|
+
}
|
|
1144
|
+
warnOnce$1('secret-in-publishable', 'auth.publishableKey was given a secret-class key (a4_sk_... or a4_ak_...). '
|
|
1145
|
+
+ 'Pass it as auth.secretKey instead (servers and scripts only), or set ARETE_API_KEY.');
|
|
1146
|
+
}
|
|
1147
|
+
if (browser || hasExplicitAuth(auth))
|
|
1148
|
+
return auth;
|
|
1149
|
+
const environmentKey = readEnvironmentVariable(ARETE_API_KEY_ENV)?.trim();
|
|
1150
|
+
if (!environmentKey)
|
|
1151
|
+
return auth;
|
|
1152
|
+
if (classifyApiKey(environmentKey) === 'publishable') {
|
|
1153
|
+
warnOnce$1('publishable-in-env', `${ARETE_API_KEY_ENV} holds a publishable key (a4_pk_...) and was ignored. Set it to an agent `
|
|
1154
|
+
+ 'key (a4_ak_...) or secret key (a4_sk_...), or pass the publishable key as auth.publishableKey.');
|
|
1155
|
+
return auth;
|
|
1156
|
+
}
|
|
1157
|
+
return { ...auth, secretKey: environmentKey };
|
|
1158
|
+
}
|
|
1159
|
+
/** The key sent as the token endpoint bearer credential, if any. */
|
|
1160
|
+
function tokenEndpointApiKey(auth) {
|
|
1161
|
+
return auth?.secretKey ?? auth?.publishableKey;
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1046
1164
|
const GZIP_MAGIC_0 = 0x1f;
|
|
1047
1165
|
const GZIP_MAGIC_1 = 0x8b;
|
|
1048
1166
|
const FRAME_MODES = new Set(['state', 'append', 'list']);
|
|
@@ -1850,7 +1968,7 @@ class ConnectionManager {
|
|
|
1850
1968
|
this.reconnectIntervals = config.reconnectIntervals ?? DEFAULT_CONFIG.reconnectIntervals;
|
|
1851
1969
|
this.maxReconnectAttempts =
|
|
1852
1970
|
config.maxReconnectAttempts ?? DEFAULT_CONFIG.maxReconnectAttempts;
|
|
1853
|
-
this.authConfig = config.auth;
|
|
1971
|
+
this.authConfig = resolveAuthConfig(config.auth);
|
|
1854
1972
|
this.release = normalizeStackRelease(config.release);
|
|
1855
1973
|
this.authFetch = config.fetch ?? ((input, init) => {
|
|
1856
1974
|
if (typeof globalThis.fetch !== 'function') {
|
|
@@ -2102,12 +2220,11 @@ class ConnectionManager {
|
|
|
2102
2220
|
};
|
|
2103
2221
|
}
|
|
2104
2222
|
async fetchTokenFromEndpoint(tokenEndpoint, request) {
|
|
2223
|
+
const apiKey = tokenEndpointApiKey(this.authConfig);
|
|
2105
2224
|
const response = await this.authFetch(tokenEndpoint, {
|
|
2106
2225
|
method: 'POST',
|
|
2107
2226
|
headers: {
|
|
2108
|
-
...(
|
|
2109
|
-
? { Authorization: `Bearer ${this.authConfig.publishableKey}` }
|
|
2110
|
-
: {}),
|
|
2227
|
+
...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),
|
|
2111
2228
|
...(this.authConfig?.tokenEndpointHeaders ?? {}),
|
|
2112
2229
|
'Content-Type': 'application/json',
|
|
2113
2230
|
},
|
|
@@ -5453,7 +5570,10 @@ function hasRuntimeAuthStrategy(auth) {
|
|
|
5453
5570
|
|| auth?.getToken !== undefined
|
|
5454
5571
|
|| auth?.tokenEndpoint !== undefined;
|
|
5455
5572
|
}
|
|
5456
|
-
function bindingAuthConfig(binding,
|
|
5573
|
+
function bindingAuthConfig(binding, configuredAuth) {
|
|
5574
|
+
// Resolve ARETE_API_KEY before the binding endpoint is filled in, so an
|
|
5575
|
+
// otherwise unconfigured client still authenticates.
|
|
5576
|
+
const runtimeAuth = resolveAuthConfig(configuredAuth);
|
|
5457
5577
|
if (hasRuntimeAuthStrategy(runtimeAuth))
|
|
5458
5578
|
return runtimeAuth;
|
|
5459
5579
|
if (!binding.auth.required)
|
|
@@ -7500,7 +7620,10 @@ function parseProgramReadResponse(response, path, body) {
|
|
|
7500
7620
|
throw new AreteError(`Program read '${path}' returned invalid JSON`, 'INVALID_RESPONSE', error);
|
|
7501
7621
|
}
|
|
7502
7622
|
}
|
|
7503
|
-
function hostedAuthConfig(binding,
|
|
7623
|
+
function hostedAuthConfig(binding, configuredAuth) {
|
|
7624
|
+
// Resolve ARETE_API_KEY before the binding endpoint is filled in, so an
|
|
7625
|
+
// otherwise unconfigured client still authenticates.
|
|
7626
|
+
const runtimeAuth = resolveAuthConfig(configuredAuth);
|
|
7504
7627
|
const metadata = binding.auth;
|
|
7505
7628
|
const runtimeStrategyConfigured = runtimeAuth?.token !== undefined
|
|
7506
7629
|
|| runtimeAuth?.getToken !== undefined
|
|
@@ -8975,6 +9098,7 @@ function stringifyBigints(value) {
|
|
|
8975
9098
|
const Arete = Object.assign(Arete$1, { session: createSession });
|
|
8976
9099
|
|
|
8977
9100
|
exports.API_PROBLEM_SCHEMA_VERSION = API_PROBLEM_SCHEMA_VERSION;
|
|
9101
|
+
exports.ARETE_API_KEY_ENV = ARETE_API_KEY_ENV;
|
|
8978
9102
|
exports.ASSOCIATED_TOKEN_PROGRAM_ADDRESS = ASSOCIATED_TOKEN_PROGRAM_ADDRESS;
|
|
8979
9103
|
exports.Arete = Arete;
|
|
8980
9104
|
exports.AreteError = AreteError;
|
|
@@ -9019,6 +9143,7 @@ exports.buildInstruction = buildInstruction;
|
|
|
9019
9143
|
exports.bytes = bytes;
|
|
9020
9144
|
exports.canonicalQueryKey = canonicalQueryKey;
|
|
9021
9145
|
exports.chainAccountLoader = chainAccountLoader;
|
|
9146
|
+
exports.classifyApiKey = classifyApiKey;
|
|
9022
9147
|
exports.compareProgramIdentity = compareProgramIdentity;
|
|
9023
9148
|
exports.createChainClient = createChainClient;
|
|
9024
9149
|
exports.createEntityStream = createEntityStream;
|