@picked/app-kit 0.0.0-stage → 0.0.117-next.2

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.
@@ -0,0 +1,279 @@
1
+ import { APP_ASSERTION_HEADER as e, PICKED_CONFIG_PATH as t, PICKED_KEYS_PATH as n, SIGNATURE_HEADER as r, createAppAssertion as i, remoteKeySet as a, signerFor as o, trimTrailing as s, verifyBody as c, verifyMcpRequest as l, verifySessionToken as u } from "@picked/app-protocol";
2
+ import { AppInstallationLabelsDocument as d, AppInstallationTokenCreateDocument as f, LabelsDeleteDocument as p, LabelsSetDocument as m, createAdminApiClient as h } from "@picked/admin-api-client";
3
+ export * from "@picked/app-protocol";
4
+ //#region src/link.ts
5
+ var g = /^app_[a-z0-9]+$/;
6
+ function _(e) {
7
+ return g.test(e);
8
+ }
9
+ var v = /^[a-z0-9_-]{1,64}$/;
10
+ function y(e) {
11
+ if (!_(e.appId)) throw TypeError(`${e.appId} is not an app ID`);
12
+ if (!v.test(e.namespace) || !v.test(e.key)) throw TypeError(`${e.namespace}.${e.key} is not a label namespace and key`);
13
+ let t = new URL(`/apps/${e.appId}/open`, new URL(e.dashboardOrigin).origin);
14
+ return t.searchParams.set("label", `${e.namespace}.${e.key}`), t.searchParams.set("value", e.value), t.toString();
15
+ }
16
+ //#endregion
17
+ //#region src/client.ts
18
+ var b = 6e4, x = class extends Error {
19
+ userErrors;
20
+ constructor(e) {
21
+ super(e.map((e) => `${e.field.join(".")}: ${e.message}`).join("; ")), this.name = "LabelWriteError", this.userErrors = e;
22
+ }
23
+ }, S = {
24
+ TEXT: "text",
25
+ NUMBER: "number",
26
+ BOOLEAN: "boolean",
27
+ JSON: "json"
28
+ }, C = {
29
+ text: "TEXT",
30
+ number: "NUMBER",
31
+ boolean: "BOOLEAN",
32
+ json: "JSON"
33
+ };
34
+ function w(e) {
35
+ return {
36
+ ...e,
37
+ type: S[e.type]
38
+ };
39
+ }
40
+ function T(e) {
41
+ if (e.length !== 0) throw new x(e.map((e) => ({
42
+ field: e.field ?? [],
43
+ message: e.message,
44
+ code: e.code
45
+ })));
46
+ }
47
+ function E(t) {
48
+ let n = s(t.apiUrl, "/"), r = h({
49
+ apiUrl: n,
50
+ ...t.fetch === void 0 ? {} : { fetch: t.fetch }
51
+ }), { installationId: a } = t, c = t.token === void 0 || t.tokenExpiresAt === void 0 ? void 0 : {
52
+ token: t.token,
53
+ expiresAt: Date.parse(t.tokenExpiresAt)
54
+ }, l = async () => {
55
+ if (c !== void 0 && c.expiresAt - b > Date.now()) return c.token;
56
+ let s = await i(await o(t.privateKey), {
57
+ installationId: a,
58
+ audience: n
59
+ }), { appInstallationTokenCreate: l } = await r.request(f, { input: {
60
+ installationId: a,
61
+ ...t.sessionToken === void 0 ? {} : { sessionToken: t.sessionToken }
62
+ } }, { headers: { [e]: s } });
63
+ return c = {
64
+ token: l.token,
65
+ expiresAt: Date.parse(l.expiresAt)
66
+ }, l.token;
67
+ }, u = async (e, t, n = {}) => r.request(e, t, {
68
+ ...n,
69
+ headers: {
70
+ ...n.headers,
71
+ "x-api-key": await l()
72
+ }
73
+ }), g = {
74
+ ownerType: "APP_INSTALLATION",
75
+ ownerId: a
76
+ }, _ = {
77
+ async list() {
78
+ return ((await u(d, { id: a })).appInstallation?.labels ?? []).map(w);
79
+ },
80
+ async get(e, t) {
81
+ return (await _.list()).find((n) => n.namespace === e && n.key === t);
82
+ },
83
+ async set(e, t = {}) {
84
+ let { labelsSet: n } = await u(m, { input: {
85
+ ...g,
86
+ labels: e.map((e) => ({
87
+ ...e,
88
+ type: C[e.type]
89
+ })),
90
+ atomic: t.atomic ?? !1
91
+ } });
92
+ return T(n.userErrors), {
93
+ labels: n.labels.map(w),
94
+ skipped: n.skipped.map(w)
95
+ };
96
+ },
97
+ async delete(e) {
98
+ let { labelsDelete: t } = await u(p, { input: {
99
+ ...g,
100
+ labels: [...e]
101
+ } });
102
+ return T(t.userErrors), t.deletedLabelIds;
103
+ }
104
+ };
105
+ return {
106
+ labels: _,
107
+ token: l,
108
+ request: u
109
+ };
110
+ }
111
+ function D(e, t) {
112
+ return E({
113
+ apiUrl: e.apiUrl,
114
+ installationId: e.installationId,
115
+ privateKey: t.privateKey,
116
+ ...e.token === void 0 || e.tokenExpiresAt === void 0 ? {} : {
117
+ token: e.token,
118
+ tokenExpiresAt: e.tokenExpiresAt
119
+ },
120
+ ...t.sessionToken == null ? {} : { sessionToken: t.sessionToken },
121
+ ...t.fetch === void 0 ? {} : { fetch: t.fetch }
122
+ });
123
+ }
124
+ //#endregion
125
+ //#region src/server.ts
126
+ function O(e, t = 200) {
127
+ return new Response(JSON.stringify(e), {
128
+ status: t,
129
+ headers: { "content-type": "application/json" }
130
+ });
131
+ }
132
+ function k(e, t) {
133
+ let n = t.frameAncestors.length > 0 ? t.frameAncestors.join(" ") : "'none'";
134
+ return new Response(e, { headers: {
135
+ "content-type": "text/html; charset=utf-8",
136
+ "content-security-policy": `frame-ancestors ${n}`,
137
+ "referrer-policy": "strict-origin",
138
+ "x-content-type-options": "nosniff"
139
+ } });
140
+ }
141
+ function A(e, t) {
142
+ return typeof e == "object" && !!e && "type" in e && e.type === t;
143
+ }
144
+ function j(e) {
145
+ return A(e, "workflow_tool.call");
146
+ }
147
+ function M(e) {
148
+ return A(e, "installation.created");
149
+ }
150
+ function N(e) {
151
+ return A(e, "installation.upgraded");
152
+ }
153
+ function P(e) {
154
+ return A(e, "workspace.published");
155
+ }
156
+ function F(e) {
157
+ return A(e, "installation.deleted");
158
+ }
159
+ async function I(e, t) {
160
+ try {
161
+ return O({ output: await e(t) });
162
+ } catch (e) {
163
+ return O({ error: { message: e instanceof Error ? e.message : String(e) } }, 422);
164
+ }
165
+ }
166
+ function L(e, t, n, r) {
167
+ return r !== void 0 && t === r ? { kind: "mcp" } : e === "POST" ? n === "/lifecycle" ? { kind: "lifecycle" } : n === "/events" ? { kind: "event" } : n.startsWith("/tools/") ? {
168
+ kind: "tool",
169
+ name: n.slice(7)
170
+ } : null : null;
171
+ }
172
+ async function R(e, t, n) {
173
+ let r = n === "" ? null : JSON.parse(n);
174
+ if (t.kind === "lifecycle") return M(r) ? await e.onInstall?.(r) : N(r) ? await e.onUpgrade?.(r) : F(r) && await e.onUninstall?.(r), O({ ok: !0 });
175
+ if (t.kind === "event") return P(r) && await e.onEvent?.(r), O({ ok: !0 });
176
+ let i = t.kind === "tool" ? t.name : "", a = e.tools?.[i];
177
+ return a === void 0 || !j(r) ? O({ error: { message: `Unknown tool ${i}` } }, 404) : I(a, r);
178
+ }
179
+ function z(e) {
180
+ let t = s(e.basePath ?? "", "/");
181
+ return async (n) => {
182
+ let i = new URL(n.url).pathname, a = t !== "" && i.startsWith(t) ? i.slice(t.length) : i, o = L(n.method, i, a, e.mcp?.path);
183
+ if (o === null && e.manifest !== void 0 && a === "/manifest.json") return O(e.manifest);
184
+ if (o === null) return e.fallback === void 0 ? O({ error: "not_found" }, 404) : e.fallback(n);
185
+ let s = n.method === "GET" ? "" : await n.text();
186
+ if (o.kind !== "mcp" || e.mcp === void 0) return await c(e.pickedKeys, s, n.headers.get(r)) ? R(e, o, s) : O({ error: "invalid_signature" }, 401);
187
+ let u = await l(e.pickedKeys, n.headers, s);
188
+ return u === null ? O({ error: "invalid_signature" }, 401) : e.mcp.handle(new Request(n.url, {
189
+ method: n.method,
190
+ headers: n.headers,
191
+ ...n.method === "GET" ? {} : { body: s }
192
+ }), u);
193
+ };
194
+ }
195
+ function B(e) {
196
+ let t = e.headers.get("authorization") ?? "";
197
+ return t.startsWith("Bearer ") && t.length > 7 ? t.slice(7) : null;
198
+ }
199
+ async function V(e, t) {
200
+ let n = B(e);
201
+ return n === null ? null : u(t, n);
202
+ }
203
+ function H(e, t) {
204
+ let n = e?.trim() ?? "";
205
+ if (n === "") throw Error(t);
206
+ return n;
207
+ }
208
+ var U = "picked-api";
209
+ function W(e, t) {
210
+ if (typeof e != "object" || !e) return;
211
+ let n = Reflect.get(e, t);
212
+ return typeof n == "string" && n !== "" ? n : void 0;
213
+ }
214
+ function G(e, t) {
215
+ let n;
216
+ try {
217
+ n = JSON.parse(e.VERCEL_RELATED_PROJECTS ?? "[]");
218
+ } catch {
219
+ return;
220
+ }
221
+ if (!Array.isArray(n)) return;
222
+ let r = n.find((e) => W(Reflect.get(Object(e), "project"), "name") === t);
223
+ if (r === void 0) return;
224
+ let i = Reflect.get(Object(r), "preview"), a = Reflect.get(Object(r), "production"), o;
225
+ return e.VERCEL_ENV === "preview" ? o = W(i, "customEnvironment") ?? W(i, "branch") : e.VERCEL_ENV === "production" && (o = W(a, "alias") ?? W(a, "url")), o === void 0 ? void 0 : `https://${o}`;
226
+ }
227
+ function K(e) {
228
+ if (typeof e != "object" || !e) return;
229
+ let t = Reflect.get(e, "dashboardOrigins");
230
+ if (Array.isArray(t)) return t.filter((e) => typeof e == "string");
231
+ }
232
+ function q(e) {
233
+ let t = e.fetch ?? fetch, n = e.refreshAfterMs ?? 3e5, r = e.timeoutMs ?? 5e3, i = [], a, o, s = async () => {
234
+ let n = await t(e.url, { signal: AbortSignal.timeout(r) });
235
+ if (!n.ok) return;
236
+ let o = K(await n.json());
237
+ o !== void 0 && (i = o, a = Date.now());
238
+ };
239
+ return async () => (a !== void 0 && Date.now() - a < n || (o ??= s().finally(() => {
240
+ o = void 0;
241
+ }), await o.catch(() => void 0)), [...i]);
242
+ }
243
+ function J(e, r, i = {}) {
244
+ let o = H(e.PICKED_APP_PRIVATE_KEY, "Set PICKED_APP_PRIVATE_KEY to the app's private key, from `picked app keys generate`"), c = e.API_PORT === void 0 ? void 0 : `http://localhost:${e.API_PORT}`, l = s(H(e.PICKED_API_URL ?? G(e, U) ?? c, "Set PICKED_API_URL to the Picked API the app trusts"), "/"), u = (e.PICKED_DASHBOARD_ORIGINS ?? "").split(",").map((e) => e.trim()).filter((e) => e !== ""), d = e.DASHBOARD_PORT, f = u;
245
+ f.length === 0 && d !== void 0 && (f = [`http://localhost:${d}`, `http://127.0.0.1:${d}`]);
246
+ let p = e.VERCEL_ENV === "production" ? e.VERCEL_PROJECT_PRODUCTION_URL : e.VERCEL_BRANCH_URL, m = p === void 0 || p === "" ? void 0 : `https://${p}`, h = e.PICKED_APP_ID?.trim() ?? "", g = i.fetch === void 0 ? {} : { fetch: i.fetch };
247
+ return {
248
+ privateKey: o,
249
+ pickedUrl: l,
250
+ pickedKeys: a({
251
+ url: `${l}${n}`,
252
+ ...g
253
+ }),
254
+ publicUrl: s(e.APP_PUBLIC_URL?.trim() || m || `http://localhost:${r}`, "/"),
255
+ frameAncestors: [...f],
256
+ dashboardOrigins: f.length > 0 ? () => Promise.resolve([...f]) : q({
257
+ url: `${l}${t}`,
258
+ ...g,
259
+ ...i.refreshAfterMs === void 0 ? {} : { refreshAfterMs: i.refreshAfterMs },
260
+ ...i.timeoutMs === void 0 ? {} : { timeoutMs: i.timeoutMs }
261
+ }),
262
+ appId: _(h) ? h : null
263
+ };
264
+ }
265
+ function Y(e) {
266
+ return async (t) => {
267
+ let n = new URL(t.url), r = n.searchParams.get("__path");
268
+ if (r === null) return e(t);
269
+ n.searchParams.delete("__path"), n.pathname = r.startsWith("/") ? r : `/${r}`;
270
+ let i = t.method === "GET" || t.method === "HEAD" ? null : await t.text();
271
+ return e(new Request(n, {
272
+ method: t.method,
273
+ headers: t.headers,
274
+ body: i
275
+ }));
276
+ };
277
+ }
278
+ //#endregion
279
+ export { x as LabelWriteError, z as createAppHandler, E as createInstallationClient, D as installationClientFor, y as installationLinkUrl, _ as isAppId, O as json, k as page, J as readAppEnvironment, V as readSession, q as remoteDashboardOrigins, B as sessionTokenOf, Y as withRewrittenPath };
@@ -0,0 +1,197 @@
1
+ import { APP_ASSERTION_HEADER as e, PICKED_KEYS_PATH as t, SIGNATURE_HEADER as n, createSessionToken as r, createSigner as i, generateKeyPair as a, signBody as o, signMcpRequest as s, staticKeySet as c, toJwks as l, verifyAppAssertion as u, verifySessionToken as d } from "@picked/app-protocol";
2
+ //#region src/testing.ts
3
+ var f = /^[a-z0-9_-]{1,64}$/;
4
+ function p(e) {
5
+ return typeof e == "object" && !!e && !Array.isArray(e);
6
+ }
7
+ function m(e) {
8
+ return e === "TEXT" ? "text" : e === "NUMBER" ? "number" : e === "BOOLEAN" ? "boolean" : e === "JSON" ? "json" : null;
9
+ }
10
+ function h(e) {
11
+ if (!p(e)) return null;
12
+ let { namespace: t, key: n, value: r, increment: i, expiresIn: a, onlyIfAbsent: o, secret: s } = e, c = m(e.type);
13
+ return typeof t != "string" || typeof n != "string" || c === null ? null : {
14
+ namespace: t,
15
+ key: n,
16
+ type: c,
17
+ ...typeof r == "string" ? { value: r } : {},
18
+ ...typeof i == "number" ? { increment: i } : {},
19
+ ...typeof a == "number" ? { expiresIn: a } : {},
20
+ ...o === !0 ? { onlyIfAbsent: o } : {},
21
+ ...s === !0 ? { secret: s } : {}
22
+ };
23
+ }
24
+ function g(e, t) {
25
+ let n = `${t.namespace}.${t.key}`, r = e.get(n);
26
+ if (t.onlyIfAbsent === !0 && r !== void 0) return {
27
+ label: r,
28
+ skipped: r.value !== t.value
29
+ };
30
+ let i = t.increment === void 0 ? t.value ?? "" : String(Number(r?.value ?? "0") + t.increment), a = {
31
+ namespace: t.namespace,
32
+ key: t.key,
33
+ type: t.type,
34
+ value: i,
35
+ secret: t.secret === !0,
36
+ expiresAt: t.expiresIn === void 0 ? null : new Date(Date.now() + t.expiresIn * 1e3).toISOString(),
37
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
38
+ };
39
+ return e.set(n, a), {
40
+ label: a,
41
+ skipped: !1
42
+ };
43
+ }
44
+ function _(e) {
45
+ return {
46
+ ...e,
47
+ type: e.type.toUpperCase()
48
+ };
49
+ }
50
+ function v(e, t) {
51
+ return Response.json({
52
+ data: null,
53
+ errors: [{
54
+ message: e,
55
+ extensions: { code: t }
56
+ }]
57
+ });
58
+ }
59
+ function y(e, t, n) {
60
+ let r = Array.isArray(n.labels) ? n.labels : [];
61
+ if (t === "AppInstallationLabelsDelete") {
62
+ let t = r.flatMap((e) => p(e) ? [`${String(e.namespace)}.${String(e.key)}`] : []).filter((t) => e.delete(t));
63
+ return Response.json({ data: { labelsDelete: {
64
+ deletedLabelIds: t,
65
+ userErrors: []
66
+ } } });
67
+ }
68
+ let i = r.map(h), a = i.findIndex((e) => e === null || !f.test(e.namespace) || !f.test(e.key));
69
+ if (a !== -1) {
70
+ let e = [{
71
+ field: [
72
+ "labels",
73
+ String(a),
74
+ "key"
75
+ ],
76
+ message: "Key is invalid.",
77
+ code: "KEY_INVALID"
78
+ }];
79
+ return Response.json({ data: { labelsSet: {
80
+ labels: [],
81
+ skipped: [],
82
+ userErrors: e
83
+ } } });
84
+ }
85
+ let o = i.flatMap((t) => t === null ? [] : [g(e, t)]);
86
+ return Response.json({ data: { labelsSet: {
87
+ labels: o.filter((e) => !e.skipped).map((e) => _(e.label)),
88
+ skipped: o.filter((e) => e.skipped).map((e) => _(e.label)),
89
+ userErrors: []
90
+ } } });
91
+ }
92
+ async function b(f) {
93
+ let m = f.apiUrl ?? "https://api.picked.test", h = c([await i(f.appPrivateKey)]), g = await i((await a()).privateKey), b = /* @__PURE__ */ new Map(), x = /* @__PURE__ */ new Map(), S = [], C = (e) => {
94
+ let t = `appt_fake_${String(x.size + 1)}`;
95
+ return x.set(t, e), Promise.resolve({
96
+ token: t,
97
+ expiresAt: new Date(Date.now() + 36e5).toISOString()
98
+ });
99
+ }, w = async (t, n) => {
100
+ let r = t.get(e) ?? "", i = await u(h, r, { audience: m });
101
+ if (i === null || i.installationId !== n.installationId) return v("The app assertion does not verify", "UNAUTHENTICATED");
102
+ let a = typeof n.sessionToken == "string" ? await d(c([g]), n.sessionToken) : null;
103
+ return f.offlineAccess !== !0 && a?.installationId !== i.installationId ? v("The installation has no offline access; use the token Picked sends with each request", "FORBIDDEN") : Response.json({ data: { appInstallationTokenCreate: await C(i.installationId) } });
104
+ }, T = async (e, t) => {
105
+ let n = JSON.parse(t), r = p(n) && typeof n.operationName == "string" ? n.operationName : "", i = p(n) && p(n.variables) ? n.variables : {};
106
+ S.push({
107
+ operationName: r,
108
+ variables: i
109
+ });
110
+ let a = p(i.input) ? i.input : {};
111
+ if (r === "AppInstallationTokenCreate") return w(e, a);
112
+ let o = x.get(e.get("x-api-key") ?? "");
113
+ if (o === void 0) return v("Authentication failed: Invalid API key", "UNAUTHENTICATED");
114
+ let s = b.get(o) ?? /* @__PURE__ */ new Map();
115
+ if (b.set(o, s), r === "AppInstallationLabels") {
116
+ let e = i.id === o;
117
+ return Response.json({ data: { appInstallation: e ? { labels: [...s.values()].map(_) } : null } });
118
+ }
119
+ return a.ownerType !== "APP_INSTALLATION" || a.ownerId !== o ? v("Forbidden", "FORBIDDEN") : y(s, r, a);
120
+ };
121
+ return {
122
+ apiUrl: m,
123
+ fetch: async (e, n) => {
124
+ let r = new URL(e instanceof Request ? e.url : e);
125
+ return r.pathname === t ? Response.json(l([g])) : r.pathname === "/api/graphql" ? T(new Headers(n?.headers), typeof n?.body == "string" ? n.body : "{}") : Response.json({ error: { message: "Not found" } }, { status: 404 });
126
+ },
127
+ pickedKeys: c([g]),
128
+ labels: b,
129
+ received: S,
130
+ tokenFor: C,
131
+ async installationRef(e, t = {}) {
132
+ let { token: n, expiresAt: r } = await C(e);
133
+ return {
134
+ installationId: e,
135
+ organizationId: "org",
136
+ projectId: "proj_1",
137
+ apiUrl: m,
138
+ ...t,
139
+ token: n,
140
+ tokenExpiresAt: r
141
+ };
142
+ },
143
+ async signedRequest(e, t) {
144
+ let r = JSON.stringify(t);
145
+ return new Request(e, {
146
+ method: "POST",
147
+ body: r,
148
+ headers: { [n]: await o(g, r) }
149
+ });
150
+ },
151
+ async mcpRequest(e, t, n) {
152
+ let r = JSON.stringify(n);
153
+ return new Request(e, {
154
+ method: "POST",
155
+ body: r,
156
+ headers: {
157
+ "content-type": "application/json",
158
+ ...await s(g, t, r)
159
+ }
160
+ });
161
+ },
162
+ async sessionRequest(e) {
163
+ let t = await r(g, {
164
+ ...e.ref,
165
+ userId: "user-test",
166
+ exp: Math.floor(Date.now() / 1e3) + 60
167
+ });
168
+ return new Request(e.url, {
169
+ method: e.method ?? "GET",
170
+ headers: {
171
+ authorization: `Bearer ${t}`,
172
+ "content-type": "application/json"
173
+ },
174
+ ...e.body === void 0 ? {} : { body: JSON.stringify(e.body) }
175
+ });
176
+ }
177
+ };
178
+ }
179
+ function x(e, t, n, r = !1) {
180
+ return {
181
+ type: "workflow_tool.call",
182
+ ...e,
183
+ tool: t,
184
+ arguments: n,
185
+ idempotencyKey: `wrun_test:${t}`,
186
+ run: {
187
+ runId: r ? "test_wrun" : "wrun_test",
188
+ nodeId: "n1",
189
+ test: r
190
+ }
191
+ };
192
+ }
193
+ function S(e) {
194
+ return (e.headers.get("set-cookie") ?? "").split(";")[0] ?? "";
195
+ }
196
+ //#endregion
197
+ export { b as createFakePicked, S as oauthCookie, x as toolCall };
@@ -0,0 +1,145 @@
1
+ import type { AdminApiRequestOptions, TadaDocumentNode } from '@picked/admin-api-client';
2
+ import { type InstallationRef } from '@picked/app-protocol';
3
+ /** How to read a label's `value`. */
4
+ export type AppLabelType = 'text' | 'number' | 'boolean' | 'json';
5
+ /** A label the app keeps for an installation, as Picked's `Label` type describes it. */
6
+ export interface AppLabel {
7
+ namespace: string;
8
+ key: string;
9
+ type: AppLabelType;
10
+ /** The value, as a string to read through `type`; a secret label's value included. */
11
+ value: string;
12
+ /** Whether the value is kept in Vault. */
13
+ secret: boolean;
14
+ /** When the label expires, or null. */
15
+ expiresAt: string | null;
16
+ updatedAt: string;
17
+ }
18
+ /**
19
+ * A label to set, as Picked's `LabelInput` takes it. A namespace and a key are each 1-64
20
+ * lowercase letters, digits, underscores or hyphens; a value holds at most 64 KiB, and an
21
+ * installation holds at most 100 labels.
22
+ */
23
+ export interface AppLabelInput {
24
+ namespace: string;
25
+ key: string;
26
+ type: AppLabelType;
27
+ /** The value, as a string that parses as `type`. */
28
+ value?: string;
29
+ /** What to add to a `number` label, in place of `value`. */
30
+ increment?: number;
31
+ /** Seconds until the label expires, from 1 to 31536000. */
32
+ expiresIn?: number;
33
+ /** Writes only when no live label has this namespace and key. */
34
+ onlyIfAbsent?: boolean;
35
+ /** Keeps the value in Vault: a `text` or `json` value, with no `increment` or `onlyIfAbsent`. */
36
+ secret?: boolean;
37
+ }
38
+ /** A label to remove, as Picked's `LabelIdentifierInput` takes it. */
39
+ export interface AppLabelIdentifier {
40
+ namespace: string;
41
+ key: string;
42
+ /** Removes the label only while it holds this value. */
43
+ ifValue?: string;
44
+ }
45
+ /** A refused label write, as Picked's `LabelUserError` describes it. */
46
+ export interface AppLabelUserError {
47
+ field: string[];
48
+ message: string;
49
+ code: string;
50
+ }
51
+ /** A label write Picked refused, carrying its user errors. */
52
+ export declare class LabelWriteError extends Error {
53
+ readonly userErrors: readonly AppLabelUserError[];
54
+ /**
55
+ * Builds the error from Picked's user errors.
56
+ *
57
+ * @param {readonly AppLabelUserError[]} userErrors - Why Picked refused the write.
58
+ * @example new LabelWriteError([{ field: ['labels', '0', 'key'], message: 'Key is invalid.', code: 'KEY_INVALID' }])
59
+ */
60
+ constructor(userErrors: readonly AppLabelUserError[]);
61
+ }
62
+ /** The labels an app keeps for one installation in Picked, which the installation owns. */
63
+ export interface InstallationLabels {
64
+ /** Every live label, secret values included. */
65
+ list: () => Promise<AppLabel[]>;
66
+ /** One live label, or undefined when none has the namespace and key. */
67
+ get: (namespace: string, key: string) => Promise<AppLabel | undefined>;
68
+ /**
69
+ * Creates or replaces labels, as `labelsSet` does, throwing {@link LabelWriteError} when
70
+ * Picked refuses one. `skipped` holds the live labels an `onlyIfAbsent` label left in place.
71
+ */
72
+ set: (labels: readonly AppLabelInput[], options?: {
73
+ atomic?: boolean;
74
+ }) => Promise<{
75
+ labels: AppLabel[];
76
+ skipped: AppLabel[];
77
+ }>;
78
+ /** Removes labels, as `labelsDelete` does, returning the ids removed. */
79
+ delete: (labels: readonly AppLabelIdentifier[]) => Promise<string[]>;
80
+ }
81
+ /** Calls Picked as one installation. */
82
+ export interface InstallationClient {
83
+ /** The installation's labels. */
84
+ labels: InstallationLabels;
85
+ /**
86
+ * A short-lived token carrying the installation's scopes: the one Picked sent with the
87
+ * request being answered, or one minted with an app assertion, which Picked grants an app with
88
+ * offline access, or one answering a dashboard user's session. Renewed before it expires.
89
+ */
90
+ token: () => Promise<string>;
91
+ /**
92
+ * Runs a typed document with the installation's token and returns its `data`. Build the
93
+ * document with `graphql` from `@picked/admin-api-client`.
94
+ *
95
+ * @throws {Error} An `AdminApiResponseError` when Picked answers with GraphQL errors, and a
96
+ * plain `Error` on a network failure, a timeout, or a response without GraphQL errors.
97
+ */
98
+ request: <Result, Variables extends Record<string, unknown>>(document: TadaDocumentNode<Result, Variables>, variables: Variables, options?: AdminApiRequestOptions) => Promise<Result>;
99
+ }
100
+ /** Where and as whom an installation client calls Picked. */
101
+ export interface InstallationClientOptions {
102
+ /** The Picked API origin, as the request from Picked names it in `apiUrl`. */
103
+ apiUrl: string;
104
+ /** The installation. */
105
+ installationId: string;
106
+ /** The app's Ed25519 private key, as the base64 of its PKCS#8 DER or a PKCS#8 PEM. */
107
+ privateKey: string;
108
+ /** A token Picked sent with the request being answered, used until it nears expiry. */
109
+ token?: string;
110
+ /** When `token` expires. */
111
+ tokenExpiresAt?: string;
112
+ /**
113
+ * The session token of the dashboard user a page request comes from, which lets an app
114
+ * without offline access mint a token while it answers that user.
115
+ */
116
+ sessionToken?: string;
117
+ /** The fetch to send with; the global one by default. */
118
+ fetch?: typeof fetch;
119
+ }
120
+ /**
121
+ * Builds a client that calls Picked as one installation.
122
+ *
123
+ * @param {InstallationClientOptions} options - Where and as whom.
124
+ * @returns {InstallationClient} The client.
125
+ * @example createInstallationClient({ ...event, privateKey })
126
+ */
127
+ export declare function createInstallationClient(options: InstallationClientOptions): InstallationClient;
128
+ /**
129
+ * Builds the client for the installation a request names: with the token Picked sent, and for a
130
+ * page request, the session token the frame forwarded.
131
+ *
132
+ * @param {Pick<InstallationRef, 'apiUrl' | 'installationId' | 'token' | 'tokenExpiresAt'>} ref - The installation, as the request names it.
133
+ * @param {object} options - As whom and how.
134
+ * @param {string} options.privateKey - The app's private key.
135
+ * @param {string | null} [options.sessionToken] - The session token of a page request.
136
+ * @param {typeof fetch} [options.fetch] - The fetch to send with.
137
+ * @returns {InstallationClient} The client.
138
+ * @example installationClientFor(event, { privateKey: environment.privateKey })
139
+ */
140
+ export declare function installationClientFor(ref: Pick<InstallationRef, 'apiUrl' | 'installationId' | 'token' | 'tokenExpiresAt'>, options: {
141
+ privateKey: string;
142
+ sessionToken?: string | null;
143
+ fetch?: typeof fetch;
144
+ }): InstallationClient;
145
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"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"}