@namostack/plugin-sdk 0.1.0-rc.1 → 0.1.0-rc.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.
- package/dist/index.js +178 -122
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -21,10 +21,37 @@ z.object({
|
|
|
21
21
|
current: PasswordSchema,
|
|
22
22
|
next: NewPasswordSchema
|
|
23
23
|
});
|
|
24
|
+
/** A one-time token from an emailed link (confirmation, password reset). */
|
|
25
|
+
const EmailTokenSchema = z.string().min(1).max(256);
|
|
26
|
+
z.object({ token: EmailTokenSchema });
|
|
27
|
+
z.object({ email: EmailSchema });
|
|
28
|
+
z.object({
|
|
29
|
+
token: EmailTokenSchema,
|
|
30
|
+
password: NewPasswordSchema
|
|
31
|
+
});
|
|
32
|
+
const DisplayNameSchema = z.string().trim().min(1).max(100);
|
|
33
|
+
z.object({ displayName: DisplayNameSchema });
|
|
34
|
+
z.object({ image: z.string().min(1).max(Math.ceil(524288 / 3) * 4).regex(/^[A-Za-z0-9+/]+={0,2}$/, "Image must be base64-encoded") });
|
|
35
|
+
z.object({
|
|
36
|
+
email: EmailSchema,
|
|
37
|
+
password: PasswordSchema
|
|
38
|
+
});
|
|
39
|
+
z.object({ token: EmailTokenSchema });
|
|
40
|
+
z.object({
|
|
41
|
+
token: EmailTokenSchema,
|
|
42
|
+
password: NewPasswordSchema
|
|
43
|
+
});
|
|
44
|
+
z.object({ password: PasswordSchema });
|
|
24
45
|
const UserSchema = z.object({
|
|
25
46
|
id: z.string(),
|
|
26
47
|
email: z.string(),
|
|
27
|
-
displayName: z.string()
|
|
48
|
+
displayName: z.string(),
|
|
49
|
+
/** Confirmed by following the link in the signup email (or a password reset). Installing and publishing need it. */
|
|
50
|
+
emailVerified: z.boolean(),
|
|
51
|
+
/** Path on the app's origin, e.g. `/avatars/usr_…/….jpg`. Unset until the user picks a photo. */
|
|
52
|
+
avatarUrl: z.string().optional(),
|
|
53
|
+
/** A new address waiting for its confirmation link; `email` stays the login until then. */
|
|
54
|
+
pendingEmail: z.string().optional()
|
|
28
55
|
});
|
|
29
56
|
z.object({ user: UserSchema });
|
|
30
57
|
const SessionSummarySchema = z.object({
|
|
@@ -37,123 +64,40 @@ const SessionSummarySchema = z.object({
|
|
|
37
64
|
current: z.boolean()
|
|
38
65
|
});
|
|
39
66
|
z.object({ sessions: z.array(SessionSummarySchema) });
|
|
67
|
+
z.object({
|
|
68
|
+
/** `usr_<uuid>`, as auth-api generates them: nothing else reaches the handlers. */
|
|
69
|
+
userId: z.string().regex(/^usr_[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$/) });
|
|
70
|
+
z.object({ developerName: DisplayNameSchema });
|
|
40
71
|
z.object({ token: z.string().min(1) });
|
|
72
|
+
/**
|
|
73
|
+
* Whether the user has confirmed their email, as of this request. Missing counts as `false`, so a
|
|
74
|
+
* token from before the claim existed (mid-deploy) can't pass the `requiresVerifiedEmail` routes.
|
|
75
|
+
*/
|
|
76
|
+
const EmailVerifiedClaimSchema = z.boolean().default(false);
|
|
77
|
+
/** The user as of this request, for responses that show it (`GET /bootstrap`). Missing in a token from before the claims existed (mid-deploy). */
|
|
78
|
+
const ProfileClaimsSchema = {
|
|
79
|
+
email: z.string().optional(),
|
|
80
|
+
displayName: z.string().optional(),
|
|
81
|
+
avatarUrl: z.string().optional(),
|
|
82
|
+
pendingEmail: z.string().optional()
|
|
83
|
+
};
|
|
41
84
|
/** A request authenticated by the session cookie (the web app). */
|
|
42
85
|
const SessionIdentitySchema = z.object({
|
|
43
86
|
kind: z.literal("session"),
|
|
44
87
|
userId: z.string(),
|
|
45
|
-
sessionId: z.string()
|
|
88
|
+
sessionId: z.string(),
|
|
89
|
+
emailVerified: EmailVerifiedClaimSchema,
|
|
90
|
+
...ProfileClaimsSchema
|
|
46
91
|
});
|
|
47
92
|
/** A request authenticated by a CLI access token (`Authorization: Bearer nst_pat_…`). */
|
|
48
93
|
const CliTokenIdentitySchema = z.object({
|
|
49
94
|
kind: z.literal("cliToken"),
|
|
50
95
|
userId: z.string(),
|
|
51
|
-
tokenId: z.string()
|
|
96
|
+
tokenId: z.string(),
|
|
97
|
+
emailVerified: EmailVerifiedClaimSchema,
|
|
98
|
+
...ProfileClaimsSchema
|
|
52
99
|
});
|
|
53
100
|
z.discriminatedUnion("kind", [SessionIdentitySchema, CliTokenIdentitySchema]);
|
|
54
|
-
const BridgeErrorCodeSchema = z.enum([
|
|
55
|
-
"permission_denied",
|
|
56
|
-
"not_found",
|
|
57
|
-
"validation_error",
|
|
58
|
-
"quota_exceeded",
|
|
59
|
-
"timeout",
|
|
60
|
-
"upgrade_required",
|
|
61
|
-
"internal"
|
|
62
|
-
]);
|
|
63
|
-
/** How a failed call crosses the bridge; `message` is safe to show. */
|
|
64
|
-
const BridgeErrorBodySchema = z.object({
|
|
65
|
-
code: BridgeErrorCodeSchema.catch("internal"),
|
|
66
|
-
message: z.string()
|
|
67
|
-
});
|
|
68
|
-
z.object({ sdkVersion: z.string().min(1) });
|
|
69
|
-
function bridgeResultSchema(value) {
|
|
70
|
-
return z.discriminatedUnion("ok", [z.object({
|
|
71
|
-
ok: z.literal(true),
|
|
72
|
-
value
|
|
73
|
-
}), z.object({
|
|
74
|
-
ok: z.literal(false),
|
|
75
|
-
error: BridgeErrorBodySchema
|
|
76
|
-
})]);
|
|
77
|
-
}
|
|
78
|
-
/** Enough for a plugin using seedui: its `ThemeProvider` builds the rest from the mode. */
|
|
79
|
-
const PluginThemeSchema = z.object({ mode: z.enum(["light", "dark"]) });
|
|
80
|
-
/** What the plugin learns about itself and the app once connected. */
|
|
81
|
-
const BridgeContextSchema = z.object({
|
|
82
|
-
/** In live testing (`env: 'dev'`), the slug: the plugin may not be published yet. */
|
|
83
|
-
pluginId: z.string(),
|
|
84
|
-
slug: z.string(),
|
|
85
|
-
/** `dev` while live testing with `namostack dev`: its data is kept apart from real data. */
|
|
86
|
-
env: z.enum(["prod", "dev"]),
|
|
87
|
-
name: z.string(),
|
|
88
|
-
version: z.string(),
|
|
89
|
-
theme: PluginThemeSchema,
|
|
90
|
-
/** The browser's language, e.g. `en-US`. */
|
|
91
|
-
locale: z.string()
|
|
92
|
-
});
|
|
93
|
-
/** `user.getProfile()`: never the email. */
|
|
94
|
-
const PluginUserProfileSchema = z.object({ displayName: z.string() });
|
|
95
|
-
const PluginNotificationSchema = z.object({
|
|
96
|
-
message: z.string().trim().min(1).max(300),
|
|
97
|
-
tone: z.enum([
|
|
98
|
-
"info",
|
|
99
|
-
"success",
|
|
100
|
-
"warning",
|
|
101
|
-
"error"
|
|
102
|
-
]).default("info")
|
|
103
|
-
});
|
|
104
|
-
/** `ui.resize(height)`, in CSS pixels. */
|
|
105
|
-
const FrameHeightSchema = z.number().int().min(0).max(1e5);
|
|
106
|
-
const DocumentIdSchema = z.string().min(1);
|
|
107
|
-
//#endregion
|
|
108
|
-
//#region ../../types/src/cli.ts
|
|
109
|
-
/** Shown in Settings → Developer; the CLI suggests its hostname, the user confirms it when approving. */
|
|
110
|
-
const TokenNameSchema = z.string().trim().min(1).max(100);
|
|
111
|
-
/** `WDJB-MJHT`: what the user checks in the browser against what the CLI printed. */
|
|
112
|
-
const UserCodeSchema = z.string().trim().toUpperCase().regex(/^[A-Z]{4}-[A-Z]{4}$/, "Invalid code");
|
|
113
|
-
z.object({ tokenName: TokenNameSchema });
|
|
114
|
-
z.object({
|
|
115
|
-
/** Secret the CLI polls with; never shown to the user. */
|
|
116
|
-
deviceCode: z.string(),
|
|
117
|
-
userCode: z.string(),
|
|
118
|
-
/** The web app's approval page for this login. */
|
|
119
|
-
verifyUrl: z.string().url(),
|
|
120
|
-
/** Seconds to wait between polls. */
|
|
121
|
-
interval: z.number().int(),
|
|
122
|
-
/** Seconds before the login request expires. */
|
|
123
|
-
expiresIn: z.number().int()
|
|
124
|
-
});
|
|
125
|
-
z.object({ deviceCode: z.string().min(1) });
|
|
126
|
-
z.discriminatedUnion("status", [
|
|
127
|
-
z.object({ status: z.literal("pending") }),
|
|
128
|
-
z.object({
|
|
129
|
-
status: z.literal("approved"),
|
|
130
|
-
/** `nst_pat_…`: the CLI stores it, the server only keeps its hash. */
|
|
131
|
-
token: z.string(),
|
|
132
|
-
expiresAt: TimestampSchema
|
|
133
|
-
}),
|
|
134
|
-
z.object({ status: z.literal("denied") }),
|
|
135
|
-
z.object({ status: z.literal("expired") })
|
|
136
|
-
]);
|
|
137
|
-
z.object({ userCode: UserCodeSchema });
|
|
138
|
-
z.object({
|
|
139
|
-
userCode: z.string(),
|
|
140
|
-
tokenName: z.string()
|
|
141
|
-
});
|
|
142
|
-
z.object({
|
|
143
|
-
userCode: UserCodeSchema,
|
|
144
|
-
tokenName: TokenNameSchema
|
|
145
|
-
});
|
|
146
|
-
/** A CLI access token as Settings → Developer lists it; never the token itself. */
|
|
147
|
-
const CliTokenSummarySchema = z.object({
|
|
148
|
-
id: z.string(),
|
|
149
|
-
name: z.string(),
|
|
150
|
-
/** Absent until the CLI first uses the token. */
|
|
151
|
-
lastUsedAt: TimestampSchema.optional(),
|
|
152
|
-
expiresAt: TimestampSchema
|
|
153
|
-
});
|
|
154
|
-
z.object({ tokens: z.array(CliTokenSummarySchema) });
|
|
155
|
-
z.object({ tokenId: z.string().min(1) });
|
|
156
|
-
z.object({ token: z.string().min(1) });
|
|
157
101
|
//#endregion
|
|
158
102
|
//#region ../../types/src/plugins.ts
|
|
159
103
|
/** `public`: in the catalog. `unlisted`: installable by direct link. `private`: owner only. */
|
|
@@ -304,13 +248,139 @@ const DevSessionSchema = z.object({
|
|
|
304
248
|
startedAt: TimestampSchema
|
|
305
249
|
});
|
|
306
250
|
z.object({ devSessions: z.array(DevSessionSchema) });
|
|
251
|
+
//#endregion
|
|
252
|
+
//#region ../../types/src/preferences.ts
|
|
253
|
+
/** `system` follows the OS setting (`prefers-color-scheme`). */
|
|
254
|
+
const ThemePreferenceSchema = z.enum([
|
|
255
|
+
"light",
|
|
256
|
+
"dark",
|
|
257
|
+
"system"
|
|
258
|
+
]);
|
|
259
|
+
/** `GET /preferences` answers it, `PUT /preferences` replaces it whole. */
|
|
260
|
+
const PreferencesSchema = z.object({
|
|
261
|
+
theme: ThemePreferenceSchema,
|
|
262
|
+
sidebarCollapsed: z.boolean(),
|
|
263
|
+
/** Applies plugin updates on its own, except those adding permissions. */
|
|
264
|
+
autoUpdatePlugins: z.boolean()
|
|
265
|
+
});
|
|
266
|
+
z.object({
|
|
267
|
+
user: UserSchema,
|
|
268
|
+
preferences: PreferencesSchema,
|
|
269
|
+
installedPlugins: z.array(InstalledPluginSchema),
|
|
270
|
+
devSessions: z.array(DevSessionSchema)
|
|
271
|
+
});
|
|
272
|
+
const BridgeErrorCodeSchema = z.enum([
|
|
273
|
+
"permission_denied",
|
|
274
|
+
"not_found",
|
|
275
|
+
"validation_error",
|
|
276
|
+
"quota_exceeded",
|
|
277
|
+
"timeout",
|
|
278
|
+
"upgrade_required",
|
|
279
|
+
"internal"
|
|
280
|
+
]);
|
|
281
|
+
/** How a failed call crosses the bridge; `message` is safe to show. */
|
|
282
|
+
const BridgeErrorBodySchema = z.object({
|
|
283
|
+
code: BridgeErrorCodeSchema.catch("internal"),
|
|
284
|
+
message: z.string()
|
|
285
|
+
});
|
|
286
|
+
z.object({ sdkVersion: z.string().min(1) });
|
|
287
|
+
function bridgeResultSchema(value) {
|
|
288
|
+
return z.discriminatedUnion("ok", [z.object({
|
|
289
|
+
ok: z.literal(true),
|
|
290
|
+
value
|
|
291
|
+
}), z.object({
|
|
292
|
+
ok: z.literal(false),
|
|
293
|
+
error: BridgeErrorBodySchema
|
|
294
|
+
})]);
|
|
295
|
+
}
|
|
296
|
+
/** Enough for a plugin using seedui: its `ThemeProvider` builds the rest from the mode. */
|
|
297
|
+
const PluginThemeSchema = z.object({ mode: z.enum(["light", "dark"]) });
|
|
298
|
+
/** What the plugin learns about itself and the app once connected. */
|
|
299
|
+
const BridgeContextSchema = z.object({
|
|
300
|
+
/** In live testing (`env: 'dev'`), the slug: the plugin may not be published yet. */
|
|
301
|
+
pluginId: z.string(),
|
|
302
|
+
slug: z.string(),
|
|
303
|
+
/** `dev` while live testing with `namostack dev`: its data is kept apart from real data. */
|
|
304
|
+
env: z.enum(["prod", "dev"]),
|
|
305
|
+
name: z.string(),
|
|
306
|
+
version: z.string(),
|
|
307
|
+
theme: PluginThemeSchema,
|
|
308
|
+
/** The browser's language, e.g. `en-US`. */
|
|
309
|
+
locale: z.string()
|
|
310
|
+
});
|
|
311
|
+
/** `user.getProfile()`: never the email. */
|
|
312
|
+
const PluginUserProfileSchema = z.object({ displayName: z.string() });
|
|
313
|
+
const PluginNotificationSchema = z.object({
|
|
314
|
+
message: z.string().trim().min(1).max(300),
|
|
315
|
+
tone: z.enum([
|
|
316
|
+
"info",
|
|
317
|
+
"success",
|
|
318
|
+
"warning",
|
|
319
|
+
"error"
|
|
320
|
+
]).default("info")
|
|
321
|
+
});
|
|
322
|
+
/** `ui.resize(height)`, in CSS pixels. */
|
|
323
|
+
const FrameHeightSchema = z.number().int().min(0).max(1e5);
|
|
324
|
+
const DocumentIdSchema = z.string().min(1);
|
|
325
|
+
//#endregion
|
|
326
|
+
//#region ../../types/src/cli.ts
|
|
327
|
+
/** Shown in Settings → Developer; the CLI suggests its hostname, the user confirms it when approving. */
|
|
328
|
+
const TokenNameSchema = z.string().trim().min(1).max(100);
|
|
329
|
+
/** `WDJB-MJHT`: what the user checks in the browser against what the CLI printed. */
|
|
330
|
+
const UserCodeSchema = z.string().trim().toUpperCase().regex(/^[A-Z]{4}-[A-Z]{4}$/, "Invalid code");
|
|
331
|
+
z.object({ tokenName: TokenNameSchema });
|
|
332
|
+
z.object({
|
|
333
|
+
/** Secret the CLI polls with; never shown to the user. */
|
|
334
|
+
deviceCode: z.string(),
|
|
335
|
+
userCode: z.string(),
|
|
336
|
+
/** The web app's approval page for this login. */
|
|
337
|
+
verifyUrl: z.string().url(),
|
|
338
|
+
/** Seconds to wait between polls. */
|
|
339
|
+
interval: z.number().int(),
|
|
340
|
+
/** Seconds before the login request expires. */
|
|
341
|
+
expiresIn: z.number().int()
|
|
342
|
+
});
|
|
343
|
+
z.object({ deviceCode: z.string().min(1) });
|
|
344
|
+
z.discriminatedUnion("status", [
|
|
345
|
+
z.object({ status: z.literal("pending") }),
|
|
346
|
+
z.object({
|
|
347
|
+
status: z.literal("approved"),
|
|
348
|
+
/** `nst_pat_…`: the CLI stores it, the server only keeps its hash. */
|
|
349
|
+
token: z.string(),
|
|
350
|
+
expiresAt: TimestampSchema
|
|
351
|
+
}),
|
|
352
|
+
z.object({ status: z.literal("denied") }),
|
|
353
|
+
z.object({ status: z.literal("expired") })
|
|
354
|
+
]);
|
|
355
|
+
z.object({ userCode: UserCodeSchema });
|
|
356
|
+
z.object({
|
|
357
|
+
userCode: z.string(),
|
|
358
|
+
tokenName: z.string()
|
|
359
|
+
});
|
|
360
|
+
z.object({
|
|
361
|
+
userCode: UserCodeSchema,
|
|
362
|
+
tokenName: TokenNameSchema
|
|
363
|
+
});
|
|
364
|
+
/** A CLI access token as Settings → Developer lists it; never the token itself. */
|
|
365
|
+
const CliTokenSummarySchema = z.object({
|
|
366
|
+
id: z.string(),
|
|
367
|
+
name: z.string(),
|
|
368
|
+
/** Absent until the CLI first uses the token. */
|
|
369
|
+
lastUsedAt: TimestampSchema.optional(),
|
|
370
|
+
expiresAt: TimestampSchema
|
|
371
|
+
});
|
|
372
|
+
z.object({ tokens: z.array(CliTokenSummarySchema) });
|
|
373
|
+
z.object({ tokenId: z.string().min(1) });
|
|
374
|
+
z.object({ token: z.string().min(1) });
|
|
307
375
|
const ErrorCodeSchema = z.enum([
|
|
308
376
|
"unauthenticated",
|
|
309
377
|
"invalid_credentials",
|
|
310
378
|
"permission_denied",
|
|
311
379
|
"not_found",
|
|
312
380
|
"email_unavailable",
|
|
381
|
+
"email_unverified",
|
|
313
382
|
"already_installed",
|
|
383
|
+
"published_plugins",
|
|
314
384
|
"slug_taken",
|
|
315
385
|
"rate_limited",
|
|
316
386
|
"quota_exceeded",
|
|
@@ -319,7 +389,7 @@ const ErrorCodeSchema = z.enum([
|
|
|
319
389
|
"internal"
|
|
320
390
|
]);
|
|
321
391
|
z.object({
|
|
322
|
-
code: ErrorCodeSchema.catch("internal"),
|
|
392
|
+
code: z.string().pipe(ErrorCodeSchema.catch("internal")),
|
|
323
393
|
message: z.string()
|
|
324
394
|
});
|
|
325
395
|
//#endregion
|
|
@@ -377,20 +447,6 @@ const FindResponseSchema = z.object({
|
|
|
377
447
|
cursor: z.string().optional()
|
|
378
448
|
});
|
|
379
449
|
//#endregion
|
|
380
|
-
//#region ../../types/src/preferences.ts
|
|
381
|
-
/** `system` follows the OS setting (`prefers-color-scheme`). */
|
|
382
|
-
const ThemePreferenceSchema = z.enum([
|
|
383
|
-
"light",
|
|
384
|
-
"dark",
|
|
385
|
-
"system"
|
|
386
|
-
]);
|
|
387
|
-
z.object({
|
|
388
|
-
theme: ThemePreferenceSchema,
|
|
389
|
-
sidebarCollapsed: z.boolean(),
|
|
390
|
-
/** Applies plugin updates on its own, except those adding permissions. */
|
|
391
|
-
autoUpdatePlugins: z.boolean()
|
|
392
|
-
});
|
|
393
|
-
//#endregion
|
|
394
450
|
//#region src/config.ts
|
|
395
451
|
const SECOND_MS = 1e3;
|
|
396
452
|
/** Every rule the SDK runs on. Durations are in milliseconds. */
|
|
@@ -420,7 +476,7 @@ var BridgeError = class extends Error {
|
|
|
420
476
|
//#endregion
|
|
421
477
|
//#region src/version.ts
|
|
422
478
|
/** Sent to the app when connecting, so it can turn away SDKs too old to work with it. */
|
|
423
|
-
const SDK_VERSION = "0.1.0-rc.
|
|
479
|
+
const SDK_VERSION = "0.1.0-rc.2";
|
|
424
480
|
//#endregion
|
|
425
481
|
//#region src/connect.ts
|
|
426
482
|
const NullSchema = z.null();
|
package/package.json
CHANGED