@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.
Files changed (2) hide show
  1. package/dist/index.js +178 -122
  2. 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.1";
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@namostack/plugin-sdk",
3
- "version": "0.1.0-rc.1",
3
+ "version": "0.1.0-rc.2",
4
4
  "description": "Build plugins for namostack: talk to the app, store data, follow its theme.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://docs.namostack.com",