@trypattern/sdk 0.1.0 → 0.1.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 (93) hide show
  1. package/dist/api.d.ts +17 -7
  2. package/dist/api.js +34 -8
  3. package/dist/app.d.ts +12 -3
  4. package/dist/app.js +49 -7
  5. package/dist/appInfo.d.ts +35 -0
  6. package/dist/appInfo.js +67 -0
  7. package/dist/appTitle.d.ts +6 -0
  8. package/dist/appTitle.js +3 -0
  9. package/dist/auth/sessionApi.js +2 -1
  10. package/dist/builtins/AppSettings.d.ts +12 -0
  11. package/dist/builtins/AppSettings.js +31 -0
  12. package/dist/builtins/Toasts.d.ts +1 -1
  13. package/dist/builtins/Toasts.js +2 -1
  14. package/dist/cli/app.js +91 -14
  15. package/dist/cli/publishing.d.ts +21 -0
  16. package/dist/cli/publishing.js +502 -0
  17. package/dist/clock.d.ts +6 -0
  18. package/dist/clock.js +31 -0
  19. package/dist/components/AppFrame.js +3 -3
  20. package/dist/components/Button.d.ts +2 -1
  21. package/dist/components/Button.js +6 -3
  22. package/dist/components/CopyButton.d.ts +11 -0
  23. package/dist/components/CopyButton.js +16 -0
  24. package/dist/components/Divider.d.ts +2 -1
  25. package/dist/components/Divider.js +19 -4
  26. package/dist/components/IconButton.js +2 -2
  27. package/dist/components/Link.d.ts +8 -3
  28. package/dist/components/Link.js +11 -2
  29. package/dist/components/ListRow.js +2 -2
  30. package/dist/components/Menu.js +2 -2
  31. package/dist/components/RadioGroup.d.ts +18 -0
  32. package/dist/components/RadioGroup.js +85 -0
  33. package/dist/components/SegmentedControl.js +2 -2
  34. package/dist/components/Sidebar.js +2 -3
  35. package/dist/components/SignInScreen.js +3 -3
  36. package/dist/components/Switch.js +2 -1
  37. package/dist/components/controlLook.d.ts +10 -0
  38. package/dist/components/controlLook.js +14 -1
  39. package/dist/components/icons.d.ts +4 -3
  40. package/dist/components/icons.js +9 -3
  41. package/dist/components/interaction.d.ts +1 -1
  42. package/dist/flows/accessFlows.js +19 -3
  43. package/dist/flows/browser.d.ts +3 -1
  44. package/dist/flows/browser.js +6 -3
  45. package/dist/flows/flowBrowser.d.ts +3 -1
  46. package/dist/flows/flowBrowser.js +4 -2
  47. package/dist/flows/flows.d.ts +6 -0
  48. package/dist/flows/flows.js +32 -5
  49. package/dist/hooks/appContext.d.ts +2 -1
  50. package/dist/index.d.ts +6 -2
  51. package/dist/index.js +4 -2
  52. package/dist/publishWire.d.ts +271 -0
  53. package/dist/publishWire.js +168 -0
  54. package/dist/react.d.ts +3 -0
  55. package/dist/react.js +2 -0
  56. package/dist/registered.d.ts +6 -0
  57. package/dist/registered.js +2 -0
  58. package/dist/rules/checkAllFiles.js +10 -2
  59. package/dist/rules/lintPlugin.js +2 -0
  60. package/dist/rules/messages.d.ts +2 -2
  61. package/dist/rules/messages.js +2 -2
  62. package/dist/schema.d.ts +2 -0
  63. package/dist/schema.js +1 -0
  64. package/dist/server/auth.js +8 -2
  65. package/dist/server/createServer.d.ts +48 -3
  66. package/dist/server/createServer.js +25 -5
  67. package/dist/server/env.d.ts +2 -2
  68. package/dist/server/index.d.ts +6 -1
  69. package/dist/server/index.js +5 -1
  70. package/dist/server/services.d.ts +9 -0
  71. package/dist/server/services.js +47 -0
  72. package/dist/server/session.d.ts +1 -0
  73. package/dist/server/session.js +11 -0
  74. package/dist/server/settings.d.ts +49 -0
  75. package/dist/server/settings.js +25 -0
  76. package/dist/server/signInWire.d.ts +1 -0
  77. package/dist/server/signInWire.js +2 -1
  78. package/dist/serviceClients.d.ts +5 -0
  79. package/dist/serviceClients.js +70 -0
  80. package/dist/services.d.ts +53 -0
  81. package/dist/services.js +30 -0
  82. package/dist/start.js +30 -6
  83. package/dist/store.d.ts +5 -3
  84. package/dist/store.js +13 -3
  85. package/dist/table.d.ts +1 -1
  86. package/dist/vite/localWorker.d.ts +1 -1
  87. package/dist/vite/localWorker.js +5 -4
  88. package/dist/vite/projectFile.d.ts +9 -0
  89. package/dist/vite/projectFile.js +17 -0
  90. package/dist/vite/vite.d.ts +2 -1
  91. package/dist/vite/vite.js +10 -3
  92. package/package.json +5 -1
  93. package/presets/register.d.ts +2 -0
package/dist/cli/app.js CHANGED
@@ -14,6 +14,7 @@ import { delimiter, dirname, extname, join, relative, resolve } from 'node:path'
14
14
  import { pathToFileURL } from 'node:url';
15
15
  import { createHash } from 'node:crypto';
16
16
  import { resetLocalDatabase } from "../vite/localWorker.js";
17
+ import { login, publish, pull, restore, versions as listVersions } from "./publishing.js";
17
18
  import { toolBin } from "./tools.js";
18
19
  function dependsOnPattern(directory) {
19
20
  try {
@@ -36,15 +37,14 @@ function findRoot() {
36
37
  let directory = process.cwd();
37
38
  while (!dependsOnPattern(directory)) {
38
39
  const parent = dirname(directory);
39
- if (parent === directory) {
40
- process.stderr.write('app: no Pattern project here. Run app inside a project that depends on @trypattern/sdk.\n');
41
- process.exit(2);
42
- }
40
+ if (parent === directory)
41
+ return undefined;
43
42
  directory = parent;
44
43
  }
45
44
  return directory;
46
45
  }
47
- const ROOT = findRoot();
46
+ const FOUND_ROOT = findRoot();
47
+ const ROOT = FOUND_ROOT ?? process.cwd();
48
48
  const SDK_SOURCE = resolve(import.meta.dirname, '..');
49
49
  const SDK_EXTENSION = extname(import.meta.filename);
50
50
  const FORMATTED_PATHS = [
@@ -182,7 +182,7 @@ Works while the dev server runs; reload the page to see the new rows.`,
182
182
  migration: {
183
183
  summary: 'Write a database migration for the tables in schema.ts',
184
184
  details: `app migration add_due_dates
185
- Compares the tables in schema.ts (plus Pattern's users and sessions tables) with server/migrations and writes the
185
+ Compares the tables in schema.ts (plus Pattern's users, sessions and settings tables) with server/migrations and writes the
186
186
  SQL that moves the database from one to the other, named <number>_<name>.sql. app start applies new
187
187
  migrations to the local database. Prints "No schema changes" when the migrations already match.`,
188
188
  run: migration,
@@ -252,9 +252,52 @@ dev server (app check proves the production build has none). Sign out with app s
252
252
  })),
253
253
  takesArgument: true
254
254
  },
255
+ login: {
256
+ summary: 'Let this computer publish: approve it once in the browser',
257
+ details: `Opens onpattern.app in the browser with a short code (like FOX-7421) and prints the same
258
+ code. The person checks the codes match and clicks Allow; run app login again and it finishes.
259
+ The session lasts 30 days and lives in ~/.config/pattern/credentials.json. pull, publish, versions
260
+ and restore sign in the same way when they need to, so this is rarely run on its own.`,
261
+ run: () => login(publishingIo()),
262
+ runsAnywhere: true
263
+ },
264
+ pull: {
265
+ summary: 'Download a site from onpattern.app and install it, ready for app start',
266
+ details: `app pull sundayparty
267
+ Downloads the source of the site's latest live version into this folder when it is empty, or into
268
+ ./<name> otherwise, writes pattern.json, installs, and runs app setup. Never needs git; when the
269
+ Command Line Tools are installed it also makes a first commit. Signs in first if needed (see app login).`,
270
+ run: (argument) => pull(publishingIo(), argument, process.cwd()),
271
+ takesArgument: true,
272
+ runsAnywhere: true
273
+ },
274
+ publish: {
275
+ summary: 'Build this app and put it live at <name>.onpattern.app',
276
+ details: `Builds (vite build), then uploads the build, server/migrations and the source (every file
277
+ git would keep, never .dev.vars, .env, node_modules, dist or .dev) to onpattern.app, and waits
278
+ until the new version answers at https://<name>.onpattern.app, printing each step. The database
279
+ moves forward with any new migrations; nothing is ever deleted from it. If a step fails, the site
280
+ keeps running the previous version and the error says why. Run app check first.
281
+ --starter (Pattern's admins only) publishes this project as the starter every new site begins from.`,
282
+ run: (_argument, options) => publish(publishingIo(), ROOT, options.has('--starter')),
283
+ flags: ['--starter']
284
+ },
285
+ versions: {
286
+ summary: 'List the versions that went live, newest first',
287
+ details: 'Each version with its number, whether it went live, when, and who published it. app restore <number> puts one back.',
288
+ run: () => listVersions(publishingIo(), ROOT)
289
+ },
290
+ restore: {
291
+ summary: 'Put an earlier version back live (app restore <number>)',
292
+ details: `app restore 3
293
+ Publishes version 3's build again as a new version. The database is not rolled back: it keeps
294
+ every row and column added since, which version 3's code ignores.`,
295
+ run: (argument) => restore(publishingIo(), ROOT, argument),
296
+ takesArgument: true
297
+ },
255
298
  setup: {
256
- summary: 'Verify Node/pnpm match the pins, pnpm install, install the global `app` shim',
257
- details: `Idempotent. Fails if Node != .nvmrc or pnpm != package.json "packageManager".
299
+ summary: 'Verify Node/pnpm major versions, pnpm install, install the global `app` shim',
300
+ details: `Idempotent. Fails unless Node and pnpm have the major versions in .nvmrc and package.json "packageManager".
258
301
  Then runs \`pnpm install\` and installs ${SHIM_PATH}, a tiny shim that walks up from the
259
302
  current directory to the nearest project launcher (an executable ./app containing the line
260
303
  ${LAUNCHER_MARKER_LINE}) and runs it, so each project's CLI lives in its repo and the shim
@@ -270,6 +313,29 @@ existing ${SHIM_PATH} that was not installed by Pattern.`,
270
313
  };
271
314
  let jsonMode = false;
272
315
  let openBrowser = false;
316
+ function sdkVersion() {
317
+ const manifest = JSON.parse(readText(resolve(SDK_SOURCE, '..', 'package.json')));
318
+ return String(Reflect.get(Object(manifest), 'version') ?? '');
319
+ }
320
+ function publishingIo() {
321
+ return {
322
+ say,
323
+ openUrl,
324
+ build,
325
+ sdkVersion: sdkVersion(),
326
+ run: (bin, args, cwd) => new Promise((done) => {
327
+ let output = '';
328
+ const child = spawn(bin, args, {
329
+ cwd,
330
+ stdio: ['ignore', 'pipe', 'pipe']
331
+ });
332
+ child.stdout.on('data', (chunk) => (output += chunk.toString()));
333
+ child.stderr.on('data', (chunk) => (output += chunk.toString()));
334
+ child.on('error', (err) => done({ code: 127, output: `failed to run ${bin}: ${err.message}` }));
335
+ child.on('close', (code) => done({ code: code ?? 1, output }));
336
+ })
337
+ };
338
+ }
273
339
  function progressStream() {
274
340
  return jsonMode ? process.stderr : process.stdout;
275
341
  }
@@ -1059,10 +1125,11 @@ async function writeDatabaseSchema() {
1059
1125
  writeFileSync(file, [
1060
1126
  `import { sqlTable } from ${JSON.stringify(join(server, `tables${SDK_EXTENSION}`))}`,
1061
1127
  `import { sessions } from ${JSON.stringify(join(server, `sessions${SDK_EXTENSION}`))}`,
1128
+ `import { settings } from ${JSON.stringify(join(server, `settings${SDK_EXTENSION}`))}`,
1062
1129
  `import { users } from ${JSON.stringify(join(server, `users${SDK_EXTENSION}`))}`,
1063
1130
  `import { tables } from ${JSON.stringify(join(ROOT, 'schema.ts'))}`,
1064
1131
  '',
1065
- 'export { sessions, users }',
1132
+ 'export { sessions, settings, users }',
1066
1133
  ...keys.map((key) => `export const ${key} = sqlTable(tables[${JSON.stringify(key)}])`),
1067
1134
  ''
1068
1135
  ].join('\n'));
@@ -1147,6 +1214,9 @@ async function build() {
1147
1214
  : `✗ Build failed\n\n${indent(output.trimEnd())}`
1148
1215
  };
1149
1216
  }
1217
+ function majorOf(version) {
1218
+ return version.split('.')[0] ?? '';
1219
+ }
1150
1220
  function readPins() {
1151
1221
  const node = readText(join(ROOT, '.nvmrc')).trim().replace(/^v/, '');
1152
1222
  const pkg = JSON.parse(readText(join(ROOT, 'package.json')));
@@ -1186,11 +1256,11 @@ async function setup() {
1186
1256
  });
1187
1257
  const havePnpm = (pnpmVersion.stdout ?? '').trim();
1188
1258
  const problems = [];
1189
- if (haveNode !== pins.node) {
1190
- problems.push(`Node ${haveNode} does not match .nvmrc (${pins.node})\n fix nvm install && nvm use`);
1259
+ if (majorOf(haveNode) !== majorOf(pins.node)) {
1260
+ problems.push(`Node ${haveNode} is not Node ${majorOf(pins.node)} (.nvmrc says ${pins.node})\n fix pnpm env use --global ${majorOf(pins.node)}`);
1191
1261
  }
1192
- if (havePnpm !== pins.pnpm) {
1193
- problems.push(`pnpm ${havePnpm || '(not found)'} does not match package.json packageManager (${pins.pnpm})\n fix corepack enable pnpm`);
1262
+ if (majorOf(havePnpm) !== majorOf(pins.pnpm)) {
1263
+ problems.push(`pnpm ${havePnpm || '(not found)'} is not pnpm ${majorOf(pins.pnpm)} (package.json packageManager says ${pins.pnpm})\n fix curl -fsSL https://get.pnpm.io/install.sh | sh -`);
1194
1264
  }
1195
1265
  const versions = { node: haveNode, pnpm: havePnpm, expected: pins };
1196
1266
  if (problems.length > 0) {
@@ -1200,7 +1270,7 @@ async function setup() {
1200
1270
  text: problems.map((p) => `✗ ${p}`).join('\n')
1201
1271
  };
1202
1272
  }
1203
- say(`✓ Node ${haveNode} and pnpm ${havePnpm} match the pins`);
1273
+ say(`✓ Node ${haveNode} and pnpm ${havePnpm} match the pinned major versions`);
1204
1274
  const install = await capture('pnpm', ['install']);
1205
1275
  if (install.code !== 0) {
1206
1276
  return {
@@ -1301,6 +1371,13 @@ async function main(argv) {
1301
1371
  text: `app: ${problem}\n${usage.text}`
1302
1372
  };
1303
1373
  }
1374
+ else if (FOUND_ROOT === undefined && command.runsAnywhere !== true) {
1375
+ outcome = {
1376
+ code: 2,
1377
+ data: { error: 'no Pattern project here' },
1378
+ text: 'app: no Pattern project here. Run app inside a project that depends on @trypattern/sdk, or app pull <name> to download one.'
1379
+ };
1380
+ }
1304
1381
  else if (wantsHelp) {
1305
1382
  outcome = {
1306
1383
  code: 0,
@@ -0,0 +1,21 @@
1
+ type Outcome = {
2
+ code: number;
3
+ data: Record<string, unknown>;
4
+ text: string;
5
+ };
6
+ export type PublishingIo = {
7
+ say: (line: string) => void;
8
+ openUrl: (url: string) => void;
9
+ run: (bin: string, args: readonly string[], cwd: string) => Promise<{
10
+ code: number;
11
+ output: string;
12
+ }>;
13
+ build: () => Promise<Outcome>;
14
+ sdkVersion: string;
15
+ };
16
+ export declare function login(io: PublishingIo): Promise<Outcome>;
17
+ export declare function pull(io: PublishingIo, name: string | undefined, cwd: string): Promise<Outcome>;
18
+ export declare function publish(io: PublishingIo, root: string, isStarter: boolean): Promise<Outcome>;
19
+ export declare function versions(io: PublishingIo, root: string): Promise<Outcome>;
20
+ export declare function restore(io: PublishingIo, root: string, number: string | undefined): Promise<Outcome>;
21
+ export {};