@foir/sdk 0.20.0 → 0.21.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 +51 -0
- package/dist/auth.js +59 -13
- package/dist/client/platform-client.d.ts +1 -1
- package/dist/client.js +28 -1
- package/dist/codegen/index.js +1 -1
- package/dist/data/builtins.d.ts +15 -56
- package/dist/data/document.d.ts +14 -0
- package/dist/data/index.d.ts +2 -2
- package/dist/data/transport.d.ts +25 -18
- package/dist/data.js +91 -13
- package/dist/index.js +24 -0
- package/dist/next.js +44 -5
- package/dist/placement/index.d.ts +2 -19
- package/dist/placement.js +11 -16
- package/dist/push.js +52 -6
- package/dist/react.js +51 -12
- package/dist/server/events-client.d.ts +3 -3
- package/dist/server/server-client.d.ts +1 -1
- package/dist/server/verify-scoped-token.d.ts +1 -25
- package/dist/server.js +55 -103
- package/dist/transport/foir-http.d.ts +44 -5
- package/dist/transport/key-kind.d.ts +40 -0
- package/package.json +1 -1
- package/dist/server/older-claims.generated.d.ts +0 -8
package/dist/index.js
CHANGED
|
@@ -94,6 +94,27 @@ function isValidHostMessage(data) {
|
|
|
94
94
|
return true;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
// src/transport/key-kind.ts
|
|
98
|
+
var KEY_PREFIXES = [
|
|
99
|
+
["foir_pub_", "publishable"],
|
|
100
|
+
["foir_srv_", "server"],
|
|
101
|
+
["foir_adm_", "admin"],
|
|
102
|
+
["pk_", "publishable"],
|
|
103
|
+
["sk_", "older"],
|
|
104
|
+
["tc_", "older"]
|
|
105
|
+
];
|
|
106
|
+
var FOIR_CREDENTIALS_DOC = "https://docs.foir.dev/credentials";
|
|
107
|
+
function foirKeyKind(apiKey) {
|
|
108
|
+
if (typeof apiKey !== "string") return void 0;
|
|
109
|
+
for (const [prefix, kind] of KEY_PREFIXES) {
|
|
110
|
+
if (apiKey.startsWith(prefix)) return kind;
|
|
111
|
+
}
|
|
112
|
+
return void 0;
|
|
113
|
+
}
|
|
114
|
+
function adminKeyRefusal(client) {
|
|
115
|
+
return `[foir] ${client} was given an admin key. An admin key is a machine's own: nobody signs in through it, and an app never holds one. Build this client from your publishable key, or from your server key on the server that renders your pages. A machine that acts for itself uses createServerClient from @foir/sdk/server. See ${FOIR_CREDENTIALS_DOC}`;
|
|
116
|
+
}
|
|
117
|
+
|
|
97
118
|
// src/client/platform-client.ts
|
|
98
119
|
var PlatformClientError = class extends Error {
|
|
99
120
|
constructor(message, status, response) {
|
|
@@ -107,6 +128,9 @@ var PlatformClientError = class extends Error {
|
|
|
107
128
|
};
|
|
108
129
|
function createPlatformClient(options) {
|
|
109
130
|
const { apiUrl, apiKey } = options;
|
|
131
|
+
if (foirKeyKind(apiKey) === "admin") {
|
|
132
|
+
throw new PlatformClientError(adminKeyRefusal("createPlatformClient"));
|
|
133
|
+
}
|
|
110
134
|
return {
|
|
111
135
|
async query(query, variables) {
|
|
112
136
|
const res = await fetch(apiUrl, {
|
package/dist/next.js
CHANGED
|
@@ -20,6 +20,26 @@ function createFoirCookieAdapter(store, opts = {}) {
|
|
|
20
20
|
// src/graphql/foir-request.ts
|
|
21
21
|
import { print as print2 } from "graphql";
|
|
22
22
|
|
|
23
|
+
// src/transport/key-kind.ts
|
|
24
|
+
var KEY_PREFIXES = [
|
|
25
|
+
["foir_pub_", "publishable"],
|
|
26
|
+
["foir_srv_", "server"],
|
|
27
|
+
["foir_adm_", "admin"],
|
|
28
|
+
["pk_", "publishable"],
|
|
29
|
+
["sk_", "older"],
|
|
30
|
+
["tc_", "older"]
|
|
31
|
+
];
|
|
32
|
+
function foirKeyKind(apiKey) {
|
|
33
|
+
if (typeof apiKey !== "string") return void 0;
|
|
34
|
+
for (const [prefix, kind] of KEY_PREFIXES) {
|
|
35
|
+
if (apiKey.startsWith(prefix)) return kind;
|
|
36
|
+
}
|
|
37
|
+
return void 0;
|
|
38
|
+
}
|
|
39
|
+
function isPublishableFoirKey(apiKey) {
|
|
40
|
+
return foirKeyKind(apiKey) === "publishable";
|
|
41
|
+
}
|
|
42
|
+
|
|
23
43
|
// src/graphql/cdn-route.ts
|
|
24
44
|
import { print } from "graphql";
|
|
25
45
|
|
|
@@ -123,6 +143,28 @@ function refuseCredentialHeaders(extra) {
|
|
|
123
143
|
}
|
|
124
144
|
}
|
|
125
145
|
var FOIR_VISITOR_IP_HEADER = "X-Foir-Visitor-IP";
|
|
146
|
+
function sentCoordinate(value) {
|
|
147
|
+
return Math.round(value * 100) / 100;
|
|
148
|
+
}
|
|
149
|
+
async function resolveFoirVisitor(source) {
|
|
150
|
+
const value = typeof source === "function" ? await source() : source;
|
|
151
|
+
if (value === null || value === void 0) return { place: {} };
|
|
152
|
+
if (typeof value === "string") {
|
|
153
|
+
const address2 = value.trim();
|
|
154
|
+
return { address: address2 || void 0, place: {} };
|
|
155
|
+
}
|
|
156
|
+
const address = value.address?.trim();
|
|
157
|
+
const country = value.country?.trim();
|
|
158
|
+
const place = {};
|
|
159
|
+
if (country) place.country = country;
|
|
160
|
+
if (Number.isFinite(value.latitude) && Number.isFinite(value.longitude)) {
|
|
161
|
+
place.location = {
|
|
162
|
+
latitude: sentCoordinate(value.latitude),
|
|
163
|
+
longitude: sentCoordinate(value.longitude)
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
return { address: address || void 0, place };
|
|
167
|
+
}
|
|
126
168
|
function buildFoirHeaders(input) {
|
|
127
169
|
const headers = {
|
|
128
170
|
"Content-Type": "application/json"
|
|
@@ -739,10 +781,7 @@ function foirDataEndpoint(apiUrl, path) {
|
|
|
739
781
|
return foirApiEndpoint(apiUrl, path);
|
|
740
782
|
}
|
|
741
783
|
async function resolveVisitorAddress(config) {
|
|
742
|
-
|
|
743
|
-
const value = typeof source === "function" ? await source() : source;
|
|
744
|
-
const address = value?.trim();
|
|
745
|
-
return address ? address : void 0;
|
|
784
|
+
return (await resolveFoirVisitor(config.visitor)).address;
|
|
746
785
|
}
|
|
747
786
|
async function resolveBearerToken(config) {
|
|
748
787
|
const source = config.bearerToken;
|
|
@@ -783,7 +822,7 @@ async function resolveFoirAuth(config) {
|
|
|
783
822
|
};
|
|
784
823
|
}
|
|
785
824
|
function foirRoute(input) {
|
|
786
|
-
if (!input.apiKey
|
|
825
|
+
if (!isPublishableFoirKey(input.apiKey)) return { lane: "origin", reason: "key" };
|
|
787
826
|
if (input.bearerToken) return { lane: "origin", reason: "bearer" };
|
|
788
827
|
if (input.orgId || input.orgUrl) return { lane: "origin", reason: "org" };
|
|
789
828
|
if (input.mutation) return { lane: "origin", reason: "operation" };
|
|
@@ -65,26 +65,9 @@ export interface PlacementGrant {
|
|
|
65
65
|
grant: string;
|
|
66
66
|
reason: string;
|
|
67
67
|
}[];
|
|
68
|
-
/**
|
|
69
|
-
* @deprecated Read `grants`. The same list under the name it had before a
|
|
70
|
-
* placement declared grants.
|
|
71
|
-
*/
|
|
72
|
-
grantedScopes: string[];
|
|
73
|
-
/**
|
|
74
|
-
* @deprecated Read `withheldGrants`. The same list under the name it had
|
|
75
|
-
* before a placement declared grants.
|
|
76
|
-
*/
|
|
77
|
-
withheld: {
|
|
78
|
-
scope: string;
|
|
79
|
-
reason: string;
|
|
80
|
-
}[];
|
|
81
68
|
}
|
|
82
|
-
/**
|
|
83
|
-
|
|
84
|
-
* only `grantedScopes` and `withheld`, and the session reads `grants` from
|
|
85
|
-
* those.
|
|
86
|
-
*/
|
|
87
|
-
type PlacementGrantMessage = Pick<PlacementGrant, 'accessToken' | 'expiresAt' | 'orgId'> & Partial<Pick<PlacementGrant, 'grants' | 'withheldGrants' | 'grantedScopes' | 'withheld'>>;
|
|
69
|
+
/** What a console sends with a token. A list it does not send is an empty one. */
|
|
70
|
+
type PlacementGrantMessage = Pick<PlacementGrant, 'accessToken' | 'expiresAt' | 'orgId'> & Partial<Pick<PlacementGrant, 'grants' | 'withheldGrants'>>;
|
|
88
71
|
/** How the console looks, for an embedded page to match. */
|
|
89
72
|
export interface PlacementAppearance {
|
|
90
73
|
/**
|
package/dist/placement.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// src/auth/grant-table.generated.ts
|
|
2
2
|
var GRANT_TABLE = [
|
|
3
|
-
["records:read", true, ["own", "
|
|
3
|
+
["records:read", true, ["own", "all"], []],
|
|
4
4
|
["records:create", true, [], []],
|
|
5
|
-
["records:write", true, ["own", "
|
|
6
|
-
["records:delete", true, ["own", "
|
|
5
|
+
["records:write", true, ["own", "all"], []],
|
|
6
|
+
["records:delete", true, ["own", "all"], []],
|
|
7
7
|
["records:publish", true, ["own", "all"], []],
|
|
8
8
|
["records:attribute", true, [], []],
|
|
9
9
|
["search:read", true, [], []],
|
|
@@ -19,8 +19,8 @@ var GRANT_TABLE = [
|
|
|
19
19
|
["notifications:consent", false, [], []],
|
|
20
20
|
["pushcredentials:read", false, [], []],
|
|
21
21
|
["pushcredentials:write", false, [], []],
|
|
22
|
-
["notes:read", false, [], []],
|
|
23
|
-
["notes:write", false, [], []],
|
|
22
|
+
["notes:read", false, ["own", "all"], []],
|
|
23
|
+
["notes:write", false, ["own", "all"], []],
|
|
24
24
|
["notes:create", false, [], []],
|
|
25
25
|
["approvals:read", false, [], []],
|
|
26
26
|
["approvals:write", false, [], []],
|
|
@@ -77,7 +77,7 @@ var GRANT_TABLE = [
|
|
|
77
77
|
["roles:write", false, [], []],
|
|
78
78
|
["projects:read", false, [], []],
|
|
79
79
|
["projects:write", false, [], []],
|
|
80
|
-
["project:manage", false, [], ["records:read.all", "records:create", "records:write.all", "records:delete.all", "records:publish.all", "records:attribute", "search:read", "embeddings:read", "embeddings:write", "schemas:read", "drafts:read", "files:read.all", "files:write.all", "files:delete.all", "users:read.all", "identities:read", "users:create", "identities:delete", "users:write.all", "users:delete.all", "notifications:read.all", "notifications:write.all", "notifications:consent", "pushcredentials:read", "pushcredentials:write", "notes:read", "notes:write", "notes:create", "approvals:read", "approvals:write", "rooms:read", "rooms:write", "presence:read", "operations:read", "operations:execute", "operations:write", "appconfig:read", "schedules:read", "schedules:write", "hooks:read", "hooks:write", "auth:login_with_foir", "relyingparties:read", "relyingparties:write", "domains:read", "domains:write", "secrets:read", "secrets:list", "secrets:put", "secrets:delete", "appsecrets:read", "appsecrets:list", "appsecrets:put", "appsecrets:delete", "usersecrets:read", "usersecrets:list", "usersecrets:put", "usersecrets:delete", "opsecrets:read", "opsecrets:write", "settings:read", "settings:write", "designtokens:read", "designtokens:write", "models:read", "models:write", "configs:read", "configs:write", "signinproviders:read", "signinproviders:write", "apps:read", "apps:write", "events:read", "targeting:debug", "visitors:forward", "apikeys:read", "apikeys:write", "organizations:read", "organizations:create", "organizations:delete", "organizations:write", "organizations:reach", "members:read", "members:write", "roles:read", "roles:write", "projects:read", "projects:write"]],
|
|
80
|
+
["project:manage", false, [], ["records:read.all", "records:create", "records:write.all", "records:delete.all", "records:publish.all", "records:attribute", "search:read", "embeddings:read", "embeddings:write", "schemas:read", "drafts:read", "files:read.all", "files:write.all", "files:delete.all", "users:read.all", "identities:read", "users:create", "identities:delete", "users:write.all", "users:delete.all", "notifications:read.all", "notifications:write.all", "notifications:consent", "pushcredentials:read", "pushcredentials:write", "notes:read.all", "notes:write.all", "notes:create", "approvals:read", "approvals:write", "rooms:read", "rooms:write", "presence:read", "operations:read", "operations:execute", "operations:write", "appconfig:read", "schedules:read", "schedules:write", "hooks:read", "hooks:write", "auth:login_with_foir", "relyingparties:read", "relyingparties:write", "domains:read", "domains:write", "secrets:read", "secrets:list", "secrets:put", "secrets:delete", "appsecrets:read", "appsecrets:list", "appsecrets:put", "appsecrets:delete", "usersecrets:read", "usersecrets:list", "usersecrets:put", "usersecrets:delete", "opsecrets:read", "opsecrets:write", "settings:read", "settings:write", "designtokens:read", "designtokens:write", "models:read", "models:write", "configs:read", "configs:write", "signinproviders:read", "signinproviders:write", "apps:read", "apps:write", "events:read", "targeting:debug", "visitors:forward", "apikeys:read", "apikeys:write", "organizations:read", "organizations:create", "organizations:delete", "organizations:write", "organizations:reach", "members:read", "members:write", "roles:read", "roles:write", "projects:read", "projects:write", "audit:read"]],
|
|
81
81
|
["projects:create", false, [], []],
|
|
82
82
|
["projects:delete", false, [], []],
|
|
83
83
|
["tenants:read", false, [], []],
|
|
@@ -93,7 +93,7 @@ var GRANT_TABLE = [
|
|
|
93
93
|
["tenanthooks:read", false, [], []],
|
|
94
94
|
["tenanthooks:write", false, [], []],
|
|
95
95
|
["apps:install", false, [], []],
|
|
96
|
-
["tenant:manage", false, [], ["project:manage", "projects:create", "projects:delete", "tenants:read", "tenants:write", "tenants:delete", "
|
|
96
|
+
["tenant:manage", false, [], ["project:manage", "projects:create", "projects:delete", "tenants:read", "tenants:write", "tenants:delete", "export:run", "billing:read", "billing:manage", "usage:read", "controlkeys:read", "controlkeys:write", "tenanthooks:read", "tenanthooks:write", "apps:install"]],
|
|
97
97
|
["platform:read", false, [], []],
|
|
98
98
|
["platform:manage", false, [], ["tenant:manage", "platform:read", "platform:create_workspace", "users:setup"]],
|
|
99
99
|
["platform:create_workspace", false, [], []],
|
|
@@ -210,27 +210,22 @@ var PLACEMENT_MESSAGE = {
|
|
|
210
210
|
function strings(value) {
|
|
211
211
|
return Array.isArray(value) ? value.filter((v) => typeof v === "string") : null;
|
|
212
212
|
}
|
|
213
|
-
function reasons(value
|
|
213
|
+
function reasons(value) {
|
|
214
214
|
if (!Array.isArray(value)) return null;
|
|
215
215
|
const out = [];
|
|
216
216
|
for (const entry of value) {
|
|
217
217
|
const e = entry;
|
|
218
|
-
if (typeof e?.
|
|
218
|
+
if (typeof e?.grant === "string") out.push({ grant: e.grant, reason: typeof e.reason === "string" ? e.reason : "" });
|
|
219
219
|
}
|
|
220
220
|
return out;
|
|
221
221
|
}
|
|
222
222
|
function grantFrom(sent) {
|
|
223
|
-
const grants = strings(sent.grants) ?? strings(sent.grantedScopes) ?? [];
|
|
224
|
-
const withheld = reasons(sent.withheldGrants, "grant") ?? reasons(sent.withheld, "scope") ?? [];
|
|
225
|
-
const before = reasons(sent.withheld, "scope") ?? withheld;
|
|
226
223
|
return {
|
|
227
224
|
accessToken: sent.accessToken,
|
|
228
225
|
expiresAt: sent.expiresAt,
|
|
229
226
|
orgId: sent.orgId,
|
|
230
|
-
grants,
|
|
231
|
-
withheldGrants:
|
|
232
|
-
grantedScopes: strings(sent.grantedScopes) ?? grants,
|
|
233
|
-
withheld: before.map((w) => ({ scope: w.name, reason: w.reason }))
|
|
227
|
+
grants: strings(sent.grants) ?? [],
|
|
228
|
+
withheldGrants: reasons(sent.withheldGrants) ?? []
|
|
234
229
|
};
|
|
235
230
|
}
|
|
236
231
|
var FOIR_CONSOLE_ORIGINS = {
|
package/dist/push.js
CHANGED
|
@@ -1,6 +1,30 @@
|
|
|
1
1
|
// src/graphql/foir-request.ts
|
|
2
2
|
import { print as print2 } from "graphql";
|
|
3
3
|
|
|
4
|
+
// src/transport/key-kind.ts
|
|
5
|
+
var KEY_PREFIXES = [
|
|
6
|
+
["foir_pub_", "publishable"],
|
|
7
|
+
["foir_srv_", "server"],
|
|
8
|
+
["foir_adm_", "admin"],
|
|
9
|
+
["pk_", "publishable"],
|
|
10
|
+
["sk_", "older"],
|
|
11
|
+
["tc_", "older"]
|
|
12
|
+
];
|
|
13
|
+
var FOIR_CREDENTIALS_DOC = "https://docs.foir.dev/credentials";
|
|
14
|
+
function foirKeyKind(apiKey) {
|
|
15
|
+
if (typeof apiKey !== "string") return void 0;
|
|
16
|
+
for (const [prefix, kind] of KEY_PREFIXES) {
|
|
17
|
+
if (apiKey.startsWith(prefix)) return kind;
|
|
18
|
+
}
|
|
19
|
+
return void 0;
|
|
20
|
+
}
|
|
21
|
+
function adminKeyRefusal(client) {
|
|
22
|
+
return `[foir] ${client} was given an admin key. An admin key is a machine's own: nobody signs in through it, and an app never holds one. Build this client from your publishable key, or from your server key on the server that renders your pages. A machine that acts for itself uses createServerClient from @foir/sdk/server. See ${FOIR_CREDENTIALS_DOC}`;
|
|
23
|
+
}
|
|
24
|
+
function isPublishableFoirKey(apiKey) {
|
|
25
|
+
return foirKeyKind(apiKey) === "publishable";
|
|
26
|
+
}
|
|
27
|
+
|
|
4
28
|
// src/graphql/cdn-route.ts
|
|
5
29
|
import { print } from "graphql";
|
|
6
30
|
|
|
@@ -125,6 +149,28 @@ function refuseCredentialHeaders(extra) {
|
|
|
125
149
|
}
|
|
126
150
|
}
|
|
127
151
|
var FOIR_VISITOR_IP_HEADER = "X-Foir-Visitor-IP";
|
|
152
|
+
function sentCoordinate(value) {
|
|
153
|
+
return Math.round(value * 100) / 100;
|
|
154
|
+
}
|
|
155
|
+
async function resolveFoirVisitor(source) {
|
|
156
|
+
const value = typeof source === "function" ? await source() : source;
|
|
157
|
+
if (value === null || value === void 0) return { place: {} };
|
|
158
|
+
if (typeof value === "string") {
|
|
159
|
+
const address2 = value.trim();
|
|
160
|
+
return { address: address2 || void 0, place: {} };
|
|
161
|
+
}
|
|
162
|
+
const address = value.address?.trim();
|
|
163
|
+
const country = value.country?.trim();
|
|
164
|
+
const place = {};
|
|
165
|
+
if (country) place.country = country;
|
|
166
|
+
if (Number.isFinite(value.latitude) && Number.isFinite(value.longitude)) {
|
|
167
|
+
place.location = {
|
|
168
|
+
latitude: sentCoordinate(value.latitude),
|
|
169
|
+
longitude: sentCoordinate(value.longitude)
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
return { address: address || void 0, place };
|
|
173
|
+
}
|
|
128
174
|
function buildFoirHeaders(input) {
|
|
129
175
|
const headers = {
|
|
130
176
|
"Content-Type": "application/json"
|
|
@@ -277,7 +323,7 @@ function isCdnEligible(env, documentText, bearerToken, orgId) {
|
|
|
277
323
|
if (orgId) return false;
|
|
278
324
|
if (isFoirPreview(env)) return false;
|
|
279
325
|
const apiKey = resolveFoirApiKey(env);
|
|
280
|
-
if (!apiKey
|
|
326
|
+
if (!isPublishableFoirKey(apiKey)) return false;
|
|
281
327
|
return !/\bmutation\b/.test(documentText);
|
|
282
328
|
}
|
|
283
329
|
|
|
@@ -290,6 +336,9 @@ async function foirRequest(env, document, variables, options) {
|
|
|
290
336
|
"[foir] PLATFORM_API_URL or PLATFORM_API_KEY is not set \u2014 add them to .env"
|
|
291
337
|
);
|
|
292
338
|
}
|
|
339
|
+
if (foirKeyKind(apiKey) === "admin") {
|
|
340
|
+
throw new FoirRequestError(adminKeyRefusal("foirRequest"));
|
|
341
|
+
}
|
|
293
342
|
const headers = buildFoirHeaders({
|
|
294
343
|
apiKey,
|
|
295
344
|
draftChannel: isFoirPreview(env),
|
|
@@ -852,10 +901,7 @@ function foirDataEndpoint(apiUrl, path) {
|
|
|
852
901
|
return foirApiEndpoint(apiUrl, path);
|
|
853
902
|
}
|
|
854
903
|
async function resolveVisitorAddress(config) {
|
|
855
|
-
|
|
856
|
-
const value = typeof source === "function" ? await source() : source;
|
|
857
|
-
const address = value?.trim();
|
|
858
|
-
return address ? address : void 0;
|
|
904
|
+
return (await resolveFoirVisitor(config.visitor)).address;
|
|
859
905
|
}
|
|
860
906
|
async function resolveBearerToken(config) {
|
|
861
907
|
const source = config.bearerToken;
|
|
@@ -896,7 +942,7 @@ async function resolveFoirAuth(config) {
|
|
|
896
942
|
};
|
|
897
943
|
}
|
|
898
944
|
function foirRoute(input) {
|
|
899
|
-
if (!input.apiKey
|
|
945
|
+
if (!isPublishableFoirKey(input.apiKey)) return { lane: "origin", reason: "key" };
|
|
900
946
|
if (input.bearerToken) return { lane: "origin", reason: "bearer" };
|
|
901
947
|
if (input.orgId || input.orgUrl) return { lane: "origin", reason: "org" };
|
|
902
948
|
if (input.mutation) return { lane: "origin", reason: "operation" };
|
package/dist/react.js
CHANGED
|
@@ -14,6 +14,26 @@ import {
|
|
|
14
14
|
// src/graphql/foir-request.ts
|
|
15
15
|
import { print as print2 } from "graphql";
|
|
16
16
|
|
|
17
|
+
// src/transport/key-kind.ts
|
|
18
|
+
var KEY_PREFIXES = [
|
|
19
|
+
["foir_pub_", "publishable"],
|
|
20
|
+
["foir_srv_", "server"],
|
|
21
|
+
["foir_adm_", "admin"],
|
|
22
|
+
["pk_", "publishable"],
|
|
23
|
+
["sk_", "older"],
|
|
24
|
+
["tc_", "older"]
|
|
25
|
+
];
|
|
26
|
+
function foirKeyKind(apiKey) {
|
|
27
|
+
if (typeof apiKey !== "string") return void 0;
|
|
28
|
+
for (const [prefix, kind] of KEY_PREFIXES) {
|
|
29
|
+
if (apiKey.startsWith(prefix)) return kind;
|
|
30
|
+
}
|
|
31
|
+
return void 0;
|
|
32
|
+
}
|
|
33
|
+
function isPublishableFoirKey(apiKey) {
|
|
34
|
+
return foirKeyKind(apiKey) === "publishable";
|
|
35
|
+
}
|
|
36
|
+
|
|
17
37
|
// src/graphql/cdn-route.ts
|
|
18
38
|
import { print } from "graphql";
|
|
19
39
|
|
|
@@ -117,6 +137,28 @@ function refuseCredentialHeaders(extra) {
|
|
|
117
137
|
}
|
|
118
138
|
}
|
|
119
139
|
var FOIR_VISITOR_IP_HEADER = "X-Foir-Visitor-IP";
|
|
140
|
+
function sentCoordinate(value) {
|
|
141
|
+
return Math.round(value * 100) / 100;
|
|
142
|
+
}
|
|
143
|
+
async function resolveFoirVisitor(source) {
|
|
144
|
+
const value = typeof source === "function" ? await source() : source;
|
|
145
|
+
if (value === null || value === void 0) return { place: {} };
|
|
146
|
+
if (typeof value === "string") {
|
|
147
|
+
const address2 = value.trim();
|
|
148
|
+
return { address: address2 || void 0, place: {} };
|
|
149
|
+
}
|
|
150
|
+
const address = value.address?.trim();
|
|
151
|
+
const country = value.country?.trim();
|
|
152
|
+
const place = {};
|
|
153
|
+
if (country) place.country = country;
|
|
154
|
+
if (Number.isFinite(value.latitude) && Number.isFinite(value.longitude)) {
|
|
155
|
+
place.location = {
|
|
156
|
+
latitude: sentCoordinate(value.latitude),
|
|
157
|
+
longitude: sentCoordinate(value.longitude)
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
return { address: address || void 0, place };
|
|
161
|
+
}
|
|
120
162
|
function buildFoirHeaders(input) {
|
|
121
163
|
const headers = {
|
|
122
164
|
"Content-Type": "application/json"
|
|
@@ -733,10 +775,7 @@ function foirDataEndpoint(apiUrl, path) {
|
|
|
733
775
|
return foirApiEndpoint(apiUrl, path);
|
|
734
776
|
}
|
|
735
777
|
async function resolveVisitorAddress(config) {
|
|
736
|
-
|
|
737
|
-
const value = typeof source === "function" ? await source() : source;
|
|
738
|
-
const address = value?.trim();
|
|
739
|
-
return address ? address : void 0;
|
|
778
|
+
return (await resolveFoirVisitor(config.visitor)).address;
|
|
740
779
|
}
|
|
741
780
|
async function resolveBearerToken(config) {
|
|
742
781
|
const source = config.bearerToken;
|
|
@@ -777,7 +816,7 @@ async function resolveFoirAuth(config) {
|
|
|
777
816
|
};
|
|
778
817
|
}
|
|
779
818
|
function foirRoute(input) {
|
|
780
|
-
if (!input.apiKey
|
|
819
|
+
if (!isPublishableFoirKey(input.apiKey)) return { lane: "origin", reason: "key" };
|
|
781
820
|
if (input.bearerToken) return { lane: "origin", reason: "bearer" };
|
|
782
821
|
if (input.orgId || input.orgUrl) return { lane: "origin", reason: "org" };
|
|
783
822
|
if (input.mutation) return { lane: "origin", reason: "operation" };
|
|
@@ -1140,10 +1179,10 @@ async function authedOp(adapter, env, op, args) {
|
|
|
1140
1179
|
|
|
1141
1180
|
// src/auth/grant-table.generated.ts
|
|
1142
1181
|
var GRANT_TABLE = [
|
|
1143
|
-
["records:read", true, ["own", "
|
|
1182
|
+
["records:read", true, ["own", "all"], []],
|
|
1144
1183
|
["records:create", true, [], []],
|
|
1145
|
-
["records:write", true, ["own", "
|
|
1146
|
-
["records:delete", true, ["own", "
|
|
1184
|
+
["records:write", true, ["own", "all"], []],
|
|
1185
|
+
["records:delete", true, ["own", "all"], []],
|
|
1147
1186
|
["records:publish", true, ["own", "all"], []],
|
|
1148
1187
|
["records:attribute", true, [], []],
|
|
1149
1188
|
["search:read", true, [], []],
|
|
@@ -1159,8 +1198,8 @@ var GRANT_TABLE = [
|
|
|
1159
1198
|
["notifications:consent", false, [], []],
|
|
1160
1199
|
["pushcredentials:read", false, [], []],
|
|
1161
1200
|
["pushcredentials:write", false, [], []],
|
|
1162
|
-
["notes:read", false, [], []],
|
|
1163
|
-
["notes:write", false, [], []],
|
|
1201
|
+
["notes:read", false, ["own", "all"], []],
|
|
1202
|
+
["notes:write", false, ["own", "all"], []],
|
|
1164
1203
|
["notes:create", false, [], []],
|
|
1165
1204
|
["approvals:read", false, [], []],
|
|
1166
1205
|
["approvals:write", false, [], []],
|
|
@@ -1217,7 +1256,7 @@ var GRANT_TABLE = [
|
|
|
1217
1256
|
["roles:write", false, [], []],
|
|
1218
1257
|
["projects:read", false, [], []],
|
|
1219
1258
|
["projects:write", false, [], []],
|
|
1220
|
-
["project:manage", false, [], ["records:read.all", "records:create", "records:write.all", "records:delete.all", "records:publish.all", "records:attribute", "search:read", "embeddings:read", "embeddings:write", "schemas:read", "drafts:read", "files:read.all", "files:write.all", "files:delete.all", "users:read.all", "identities:read", "users:create", "identities:delete", "users:write.all", "users:delete.all", "notifications:read.all", "notifications:write.all", "notifications:consent", "pushcredentials:read", "pushcredentials:write", "notes:read", "notes:write", "notes:create", "approvals:read", "approvals:write", "rooms:read", "rooms:write", "presence:read", "operations:read", "operations:execute", "operations:write", "appconfig:read", "schedules:read", "schedules:write", "hooks:read", "hooks:write", "auth:login_with_foir", "relyingparties:read", "relyingparties:write", "domains:read", "domains:write", "secrets:read", "secrets:list", "secrets:put", "secrets:delete", "appsecrets:read", "appsecrets:list", "appsecrets:put", "appsecrets:delete", "usersecrets:read", "usersecrets:list", "usersecrets:put", "usersecrets:delete", "opsecrets:read", "opsecrets:write", "settings:read", "settings:write", "designtokens:read", "designtokens:write", "models:read", "models:write", "configs:read", "configs:write", "signinproviders:read", "signinproviders:write", "apps:read", "apps:write", "events:read", "targeting:debug", "visitors:forward", "apikeys:read", "apikeys:write", "organizations:read", "organizations:create", "organizations:delete", "organizations:write", "organizations:reach", "members:read", "members:write", "roles:read", "roles:write", "projects:read", "projects:write"]],
|
|
1259
|
+
["project:manage", false, [], ["records:read.all", "records:create", "records:write.all", "records:delete.all", "records:publish.all", "records:attribute", "search:read", "embeddings:read", "embeddings:write", "schemas:read", "drafts:read", "files:read.all", "files:write.all", "files:delete.all", "users:read.all", "identities:read", "users:create", "identities:delete", "users:write.all", "users:delete.all", "notifications:read.all", "notifications:write.all", "notifications:consent", "pushcredentials:read", "pushcredentials:write", "notes:read.all", "notes:write.all", "notes:create", "approvals:read", "approvals:write", "rooms:read", "rooms:write", "presence:read", "operations:read", "operations:execute", "operations:write", "appconfig:read", "schedules:read", "schedules:write", "hooks:read", "hooks:write", "auth:login_with_foir", "relyingparties:read", "relyingparties:write", "domains:read", "domains:write", "secrets:read", "secrets:list", "secrets:put", "secrets:delete", "appsecrets:read", "appsecrets:list", "appsecrets:put", "appsecrets:delete", "usersecrets:read", "usersecrets:list", "usersecrets:put", "usersecrets:delete", "opsecrets:read", "opsecrets:write", "settings:read", "settings:write", "designtokens:read", "designtokens:write", "models:read", "models:write", "configs:read", "configs:write", "signinproviders:read", "signinproviders:write", "apps:read", "apps:write", "events:read", "targeting:debug", "visitors:forward", "apikeys:read", "apikeys:write", "organizations:read", "organizations:create", "organizations:delete", "organizations:write", "organizations:reach", "members:read", "members:write", "roles:read", "roles:write", "projects:read", "projects:write", "audit:read"]],
|
|
1221
1260
|
["projects:create", false, [], []],
|
|
1222
1261
|
["projects:delete", false, [], []],
|
|
1223
1262
|
["tenants:read", false, [], []],
|
|
@@ -1233,7 +1272,7 @@ var GRANT_TABLE = [
|
|
|
1233
1272
|
["tenanthooks:read", false, [], []],
|
|
1234
1273
|
["tenanthooks:write", false, [], []],
|
|
1235
1274
|
["apps:install", false, [], []],
|
|
1236
|
-
["tenant:manage", false, [], ["project:manage", "projects:create", "projects:delete", "tenants:read", "tenants:write", "tenants:delete", "
|
|
1275
|
+
["tenant:manage", false, [], ["project:manage", "projects:create", "projects:delete", "tenants:read", "tenants:write", "tenants:delete", "export:run", "billing:read", "billing:manage", "usage:read", "controlkeys:read", "controlkeys:write", "tenanthooks:read", "tenanthooks:write", "apps:install"]],
|
|
1237
1276
|
["platform:read", false, [], []],
|
|
1238
1277
|
["platform:manage", false, [], ["tenant:manage", "platform:read", "platform:create_workspace", "users:setup"]],
|
|
1239
1278
|
["platform:create_workspace", false, [], []],
|
|
@@ -200,10 +200,10 @@ export interface FoirEventsClientConfig {
|
|
|
200
200
|
*/
|
|
201
201
|
apiUrl: string;
|
|
202
202
|
/**
|
|
203
|
-
* An
|
|
203
|
+
* An admin key holding `events:read`.
|
|
204
204
|
*
|
|
205
|
-
* A
|
|
206
|
-
* note at the top of this file.
|
|
205
|
+
* A publishable or a server key is refused here rather than at the server:
|
|
206
|
+
* see the operator note at the top of this file.
|
|
207
207
|
*/
|
|
208
208
|
apiKey: string;
|
|
209
209
|
/**
|
|
@@ -25,7 +25,7 @@ export declare class ServerClientError extends Error {
|
|
|
25
25
|
*
|
|
26
26
|
* const client = createServerClient({
|
|
27
27
|
* baseUrl: 'https://api.example.com',
|
|
28
|
-
* apiKey:
|
|
28
|
+
* apiKey: process.env.FOIR_ADMIN_KEY!, // foir_adm_...
|
|
29
29
|
* });
|
|
30
30
|
*
|
|
31
31
|
* const { data } = await client.query('query { models { items { key } } }');
|
|
@@ -28,8 +28,7 @@ export interface ScopedTokenClaims {
|
|
|
28
28
|
* What the token carries, as grants in the permission catalogue's names:
|
|
29
29
|
* `records:read:<model>`, `records:write:<model>`,
|
|
30
30
|
* `records:create:<model>`, `users:create` and the rest. It is the whole of
|
|
31
|
-
* what the token may do. A token
|
|
32
|
-
* read as what its older claims were worth, as the platform reads it, so
|
|
31
|
+
* what the token may do. A token that does not say its grants is refused, so
|
|
33
32
|
* this is always set.
|
|
34
33
|
*/
|
|
35
34
|
grants: Array<string>;
|
|
@@ -38,29 +37,6 @@ export interface ScopedTokenClaims {
|
|
|
38
37
|
nbf: number;
|
|
39
38
|
iat: number;
|
|
40
39
|
ctx?: Record<string, string>;
|
|
41
|
-
/**
|
|
42
|
-
* The same grants in the names a token said them with before it said its
|
|
43
|
-
* grants. They never say more than {@link ScopedTokenClaims.grants}, and
|
|
44
|
-
* can say less: a grant the older names had no word for is left out.
|
|
45
|
-
*
|
|
46
|
-
* @deprecated Read {@link ScopedTokenClaims.grants}. The platform stops
|
|
47
|
-
* sending this once no token minted before is live.
|
|
48
|
-
*/
|
|
49
|
-
cap?: Array<string>;
|
|
50
|
-
/**
|
|
51
|
-
* The models the older `records:read` marker in
|
|
52
|
-
* {@link ScopedTokenClaims.cap} was for.
|
|
53
|
-
*
|
|
54
|
-
* @deprecated Read {@link recordsReadModels}.
|
|
55
|
-
*/
|
|
56
|
-
rrm?: Array<string>;
|
|
57
|
-
/**
|
|
58
|
-
* The models the older `records:write` marker in
|
|
59
|
-
* {@link ScopedTokenClaims.cap} was for.
|
|
60
|
-
*
|
|
61
|
-
* @deprecated Read {@link recordsWriteModels}.
|
|
62
|
-
*/
|
|
63
|
-
rwm?: Array<string>;
|
|
64
40
|
/**
|
|
65
41
|
* The customer the token acts for, when it acts for one: the signed-in
|
|
66
42
|
* customer who ran the operation, or the owner of the record whose event
|