@trypattern/sdk 0.1.12 → 0.1.13

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 (80) hide show
  1. package/README.md +73 -12
  2. package/dist/access.d.ts +0 -4
  3. package/dist/access.js +0 -5
  4. package/dist/api.d.ts +3 -1
  5. package/dist/api.js +5 -23
  6. package/dist/app.d.ts +20 -5
  7. package/dist/app.js +32 -2
  8. package/dist/blocks.d.ts +18 -0
  9. package/dist/blocks.js +36 -0
  10. package/dist/bookings.d.ts +400 -0
  11. package/dist/bookings.js +300 -0
  12. package/dist/bookingsMemory.d.ts +9 -0
  13. package/dist/bookingsMemory.js +79 -0
  14. package/dist/bookingsStore.d.ts +698 -0
  15. package/dist/bookingsStore.js +129 -0
  16. package/dist/cli/app.js +143 -59
  17. package/dist/cli/publishing.d.ts +3 -1
  18. package/dist/cli/publishing.js +33 -5
  19. package/dist/cli/starter.d.ts +17 -0
  20. package/dist/cli/starter.js +140 -0
  21. package/dist/components/Calendar.d.ts +20 -0
  22. package/dist/components/Calendar.js +195 -0
  23. package/dist/components/Card.d.ts +1 -1
  24. package/dist/components/Card.js +7 -29
  25. package/dist/components/Choice.js +27 -13
  26. package/dist/components/ChoiceGroup.d.ts +3 -1
  27. package/dist/components/ChoiceGroup.js +3 -4
  28. package/dist/components/Dialog.d.ts +1 -1
  29. package/dist/components/Dialog.js +3 -1
  30. package/dist/components/ListRow.js +2 -1
  31. package/dist/components/RadioGroup.d.ts +2 -1
  32. package/dist/components/RadioGroup.js +10 -2
  33. package/dist/components/Text.d.ts +1 -1
  34. package/dist/components/Text.js +43 -2
  35. package/dist/components/calendarDates.d.ts +14 -0
  36. package/dist/components/calendarDates.js +84 -0
  37. package/dist/components/controlLook.js +11 -11
  38. package/dist/components/icons.d.ts +2 -0
  39. package/dist/components/icons.js +2 -0
  40. package/dist/components/interaction.d.ts +1 -1
  41. package/dist/flows/browser.d.ts +7 -1
  42. package/dist/flows/browser.js +25 -7
  43. package/dist/flows/flowBrowser.d.ts +2 -2
  44. package/dist/flows/flowBrowser.js +2 -2
  45. package/dist/flows/flows.js +8 -2
  46. package/dist/index.d.ts +4 -1
  47. package/dist/index.js +1 -0
  48. package/dist/media.d.ts +11 -1
  49. package/dist/patternAccess.d.ts +11 -2
  50. package/dist/patternAccess.js +8 -4
  51. package/dist/publishWire.d.ts +24 -2
  52. package/dist/publishWire.js +22 -2
  53. package/dist/react.d.ts +2 -0
  54. package/dist/react.js +1 -0
  55. package/dist/rules/checkAllFiles.js +16 -2
  56. package/dist/rules/lintPlugin.js +1 -0
  57. package/dist/schema.d.ts +3 -1
  58. package/dist/schema.js +1 -0
  59. package/dist/server/bookings.d.ts +12 -0
  60. package/dist/server/bookings.js +142 -0
  61. package/dist/server/createServer.d.ts +3 -2
  62. package/dist/server/createServer.js +13 -4
  63. package/dist/server/env.d.ts +8 -0
  64. package/dist/server/index.d.ts +3 -1
  65. package/dist/server/index.js +1 -0
  66. package/dist/server/media.d.ts +16 -3
  67. package/dist/server/media.js +72 -23
  68. package/dist/server/patternViewer.js +16 -10
  69. package/dist/server/tables.d.ts +2 -0
  70. package/dist/server/tables.js +17 -1
  71. package/dist/start.js +12 -0
  72. package/dist/table.js +9 -3
  73. package/dist/theme/scales.d.ts +1 -1
  74. package/dist/theme/scales.js +1 -0
  75. package/dist/theme/tokens.stylex.d.ts +18 -0
  76. package/dist/theme/tokens.stylex.js +19 -1
  77. package/dist/vite/localWorker.d.ts +2 -0
  78. package/dist/vite/localWorker.js +48 -3
  79. package/dist/vite/vite.js +5 -1
  80. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  import * as stylex from '@stylexjs/stylex';
2
2
  import { createElement } from 'react';
3
- import { breakpoints, fontSizes, fontWeights, fonts, lineHeights } from "../theme/tokens.stylex.js";
3
+ import { breakpoints, fontSizes, fontWeights, fonts, lineHeights, trackings } from "../theme/tokens.stylex.js";
4
4
  import { tones } from "./tones.js";
5
5
  export function Text({ children, variant = 'body', tone = 'inherit', align, as = 'span', isTruncated = false, isSelectable = false, style }) {
6
6
  return createElement(as, stylex.props(as !== 'span' && styles.block, variants[variant], tones[tone], align !== undefined && alignments[align], isTruncated && styles.truncated, isSelectable && styles.selectable, style), children);
@@ -21,11 +21,47 @@ const styles = stylex.create({
21
21
  }
22
22
  });
23
23
  const variants = stylex.create({
24
+ hero: {
25
+ fontSize: {
26
+ default: fontSizes.heading1,
27
+ [breakpoints.compact]: fontSizes.heading2
28
+ },
29
+ fontWeight: fontWeights.medium,
30
+ lineHeight: {
31
+ default: lineHeights.heading1,
32
+ [breakpoints.compact]: lineHeights.heading2
33
+ },
34
+ letterSpacing: trackings.hero
35
+ },
36
+ display: {
37
+ fontSize: {
38
+ default: fontSizes.heading2,
39
+ [breakpoints.compact]: fontSizes.heading3
40
+ },
41
+ fontWeight: fontWeights.medium,
42
+ lineHeight: {
43
+ default: lineHeights.heading2,
44
+ [breakpoints.compact]: lineHeights.heading3
45
+ },
46
+ letterSpacing: trackings.display
47
+ },
48
+ section: {
49
+ fontSize: {
50
+ default: fontSizes.heading3,
51
+ [breakpoints.compact]: fontSizes.heading5
52
+ },
53
+ fontWeight: fontWeights.medium,
54
+ lineHeight: {
55
+ default: lineHeights.heading3,
56
+ [breakpoints.compact]: lineHeights.heading5
57
+ },
58
+ letterSpacing: trackings.section
59
+ },
24
60
  heading: {
25
61
  fontSize: fontSizes.heading5,
26
62
  fontWeight: fontWeights.medium,
27
63
  lineHeight: lineHeights.heading5,
28
- letterSpacing: '-0.01em'
64
+ letterSpacing: trackings.heading
29
65
  },
30
66
  subheading: {
31
67
  fontSize: fontSizes.heading6,
@@ -42,6 +78,11 @@ const variants = stylex.create({
42
78
  fontWeight: fontWeights.normal,
43
79
  lineHeight: lineHeights.body
44
80
  },
81
+ intro: {
82
+ fontSize: fontSizes.bodyLg,
83
+ fontWeight: fontWeights.normal,
84
+ lineHeight: lineHeights.bodyLg
85
+ },
45
86
  label: {
46
87
  fontSize: {
47
88
  default: fontSizes.bodySm,
@@ -0,0 +1,14 @@
1
+ export declare function monthOf(date: string): string;
2
+ export declare function addDays(date: string, days: number): string;
3
+ export declare function addMonths(date: string, months: number): string;
4
+ export declare function daysIn(month: string): number;
5
+ export declare function weekdayIndex(date: string): number;
6
+ export declare function weeksOf(month: string): readonly (readonly (string | null)[])[];
7
+ export declare function localToday(): string;
8
+ export declare function monthTitle(month: string, locale: string | undefined): string;
9
+ export declare function dayLabel(date: string, locale: string | undefined): string;
10
+ export declare function dayNumber(date: string, locale: string | undefined): string;
11
+ export declare function weekdayNames(locale: string | undefined): readonly {
12
+ short: string;
13
+ long: string;
14
+ }[];
@@ -0,0 +1,84 @@
1
+ const DAY_MS = 24 * 60 * 60 * 1000;
2
+ function utc(date) {
3
+ return new Date(`${date}T00:00:00Z`);
4
+ }
5
+ function isoDate(time) {
6
+ return new Date(time).toISOString().slice(0, 10);
7
+ }
8
+ export function monthOf(date) {
9
+ return date.slice(0, 7);
10
+ }
11
+ export function addDays(date, days) {
12
+ return isoDate(utc(date).getTime() + days * DAY_MS);
13
+ }
14
+ export function addMonths(date, months) {
15
+ const start = utc(date);
16
+ const year = start.getUTCFullYear();
17
+ const month = start.getUTCMonth() + months;
18
+ const last = new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
19
+ return isoDate(Date.UTC(year, month, Math.min(start.getUTCDate(), last)));
20
+ }
21
+ export function daysIn(month) {
22
+ const start = utc(`${month}-01`);
23
+ return new Date(Date.UTC(start.getUTCFullYear(), start.getUTCMonth() + 1, 0)).getUTCDate();
24
+ }
25
+ export function weekdayIndex(date) {
26
+ return (utc(date).getUTCDay() + 6) % 7;
27
+ }
28
+ export function weeksOf(month) {
29
+ const first = `${month}-01`;
30
+ const cells = [
31
+ ...Array.from({ length: weekdayIndex(first) }, () => null),
32
+ ...Array.from({ length: daysIn(month) }, (_, index) => addDays(first, index))
33
+ ];
34
+ const weeks = [];
35
+ for (let index = 0; index < cells.length; index += 7) {
36
+ weeks.push(cells.slice(index, index + 7));
37
+ }
38
+ return weeks;
39
+ }
40
+ export function localToday() {
41
+ const now = new Date();
42
+ return [
43
+ String(now.getFullYear()).padStart(4, '0'),
44
+ String(now.getMonth() + 1).padStart(2, '0'),
45
+ String(now.getDate()).padStart(2, '0')
46
+ ].join('-');
47
+ }
48
+ export function monthTitle(month, locale) {
49
+ return new Intl.DateTimeFormat(locale, {
50
+ month: 'long',
51
+ year: 'numeric',
52
+ timeZone: 'UTC'
53
+ }).format(utc(`${month}-01`));
54
+ }
55
+ export function dayLabel(date, locale) {
56
+ return new Intl.DateTimeFormat(locale, {
57
+ dateStyle: 'full',
58
+ timeZone: 'UTC'
59
+ }).format(utc(date));
60
+ }
61
+ export function dayNumber(date, locale) {
62
+ return new Intl.DateTimeFormat(locale, {
63
+ day: 'numeric',
64
+ timeZone: 'UTC'
65
+ }).format(utc(date));
66
+ }
67
+ export function weekdayNames(locale) {
68
+ const monday = '2024-01-01';
69
+ const short = new Intl.DateTimeFormat(locale, {
70
+ weekday: 'short',
71
+ timeZone: 'UTC'
72
+ });
73
+ const long = new Intl.DateTimeFormat(locale, {
74
+ weekday: 'long',
75
+ timeZone: 'UTC'
76
+ });
77
+ return Array.from({ length: 7 }, (_, index) => {
78
+ const day = utc(addDays(monday, index));
79
+ return {
80
+ short: [...short.format(day)].slice(0, 2).join(''),
81
+ long: long.format(day)
82
+ };
83
+ });
84
+ }
@@ -1,44 +1,44 @@
1
1
  import * as stylex from '@stylexjs/stylex';
2
- import { borders, colors, controls, fontSizes, lineHeights, motion, radii } from "../theme/tokens.stylex.js";
2
+ import { borders, colors, controls, motion, radii } from "../theme/tokens.stylex.js";
3
3
  export const controlSizes = stylex.create({
4
4
  xs: {
5
5
  gap: controls.gapXs,
6
6
  height: controls.heightXs,
7
7
  paddingInline: controls.paddingXs,
8
- fontSize: fontSizes.captionLg,
9
- lineHeight: lineHeights.caption,
8
+ fontSize: controls.fontSizeXs,
9
+ lineHeight: controls.lineHeightXs,
10
10
  borderRadius: radii.sm
11
11
  },
12
12
  sm: {
13
13
  gap: controls.gapSm,
14
14
  height: controls.heightSm,
15
15
  paddingInline: controls.paddingSm,
16
- fontSize: fontSizes.bodySm,
17
- lineHeight: lineHeights.bodySm,
16
+ fontSize: controls.fontSizeSm,
17
+ lineHeight: controls.lineHeightSm,
18
18
  borderRadius: radii.md
19
19
  },
20
20
  md: {
21
21
  gap: controls.gapMd,
22
22
  height: controls.heightMd,
23
23
  paddingInline: controls.paddingMd,
24
- fontSize: fontSizes.bodySm,
25
- lineHeight: lineHeights.bodySm,
24
+ fontSize: controls.fontSizeMd,
25
+ lineHeight: controls.lineHeightMd,
26
26
  borderRadius: radii.md
27
27
  },
28
28
  lg: {
29
29
  gap: controls.gapLg,
30
30
  height: controls.heightLg,
31
31
  paddingInline: controls.paddingLg,
32
- fontSize: fontSizes.bodySm,
33
- lineHeight: lineHeights.bodySm,
32
+ fontSize: controls.fontSizeLg,
33
+ lineHeight: controls.lineHeightLg,
34
34
  borderRadius: radii.lg
35
35
  },
36
36
  xl: {
37
37
  gap: controls.gapXl,
38
38
  height: controls.heightXl,
39
39
  paddingInline: controls.paddingXl,
40
- fontSize: fontSizes.bodySm,
41
- lineHeight: lineHeights.bodySm,
40
+ fontSize: controls.fontSizeXl,
41
+ lineHeight: controls.lineHeightXl,
42
42
  borderRadius: radii.lg
43
43
  }
44
44
  });
@@ -33,6 +33,8 @@ declare const builtInIcons: {
33
33
  alertCircle: string[];
34
34
  chevronDown: string;
35
35
  chevronRight: string;
36
+ previous: string;
37
+ next: string;
36
38
  search: string[];
37
39
  panelLeft: string[];
38
40
  more: {
@@ -62,6 +62,8 @@ const builtInIcons = {
62
62
  ],
63
63
  chevronDown: 'M7 10l5 5 5-5',
64
64
  chevronRight: 'M10 7l5 5-5 5',
65
+ previous: 'M15 18l-6-6 6-6',
66
+ next: 'M9 18l6-6-6-6',
65
67
  search: ['M4.5 11a6.5 6.5 0 1 0 13 0a6.5 6.5 0 1 0 -13 0', 'M16 16l4 4'],
66
68
  panelLeft: [
67
69
  'M6.5 4.5h11a3 3 0 0 1 3 3v9a3 3 0 0 1 -3 3h-11a3 3 0 0 1 -3 -3v-9a3 3 0 0 1 3 -3Z',
@@ -1,6 +1,6 @@
1
1
  import type { ReactNode } from 'react';
2
2
  export type Interaction = {
3
- primitive: 'AppFrame' | 'Button' | 'Card' | 'IconButton' | 'Link' | 'ListRow' | 'Select' | 'Switch' | 'Checkbox' | 'SegmentedControl' | 'Menu' | 'ContextMenu' | 'CommandMenu' | 'TextField' | 'Form' | 'Dialog' | 'RadioGroup' | 'ChoiceGroup';
3
+ primitive: 'AppFrame' | 'Button' | 'Card' | 'IconButton' | 'Link' | 'ListRow' | 'Select' | 'Switch' | 'Checkbox' | 'SegmentedControl' | 'Menu' | 'ContextMenu' | 'CommandMenu' | 'TextField' | 'Form' | 'Dialog' | 'RadioGroup' | 'ChoiceGroup' | 'Calendar';
4
4
  event: 'click' | 'change' | 'select' | 'open' | 'close' | 'submit' | 'navigate';
5
5
  label: string;
6
6
  page: string | null;
@@ -31,4 +31,10 @@ export type AppPage = {
31
31
  captureAt: (viewport: ViewportName) => Promise<string>;
32
32
  close: () => Promise<void>;
33
33
  };
34
- export declare function openApp(browser: Browser, baseUrl: string, path: string, viewport: ViewportName, signedInAs: string | null, seed?: object, admins?: readonly string[], world?: unknown): Promise<AppPage>;
34
+ export type MemoryRun = {
35
+ seed: object;
36
+ admins: readonly string[];
37
+ title: string;
38
+ world: unknown;
39
+ };
40
+ export declare function openApp(browser: Browser, baseUrl: string, path: string, viewport: ViewportName, signedInAs: string | null, memory?: MemoryRun): Promise<AppPage>;
@@ -59,15 +59,27 @@ function watchNetwork(page) {
59
59
  }
60
60
  };
61
61
  }
62
+ function isMissingPage(response, page) {
63
+ const request = response.request();
64
+ return (request.isNavigationRequest() &&
65
+ request.frame() === page.mainFrame() &&
66
+ response.status() >= 400 &&
67
+ response.status() < 500);
68
+ }
62
69
  function recordProblems(page, problems) {
70
+ const missingPages = new Set();
63
71
  const record = (problem) => {
64
72
  if (!problems.includes(problem))
65
73
  problems.push(problem);
66
74
  };
67
75
  page.on('console', (message) => {
68
- if (message.type() === 'error') {
69
- record(`console error: ${message.text()}`);
76
+ if (message.type() !== 'error')
77
+ return;
78
+ if (missingPages.has(message.location().url) &&
79
+ message.text().startsWith('Failed to load resource')) {
80
+ return;
70
81
  }
82
+ record(`console error: ${message.text()}`);
71
83
  });
72
84
  page.on('pageerror', (error) => {
73
85
  record(`uncaught page error: ${error.message}`);
@@ -76,12 +88,16 @@ function recordProblems(page, problems) {
76
88
  record(`request failed: ${request.method()} ${request.url()} (${request.failure()?.errorText ?? 'unknown'})`);
77
89
  });
78
90
  page.on('response', (response) => {
91
+ if (isMissingPage(response, page)) {
92
+ missingPages.add(response.url());
93
+ return;
94
+ }
79
95
  if (response.status() >= 400) {
80
96
  record(`request failed: ${response.request().method()} ${response.url()} (HTTP ${response.status()})`);
81
97
  }
82
98
  });
83
99
  }
84
- export async function openApp(browser, baseUrl, path, viewport, signedInAs, seed, admins = [], world = {}) {
100
+ export async function openApp(browser, baseUrl, path, viewport, signedInAs, memory) {
85
101
  const { width, height, deviceScaleFactor } = viewports[viewport];
86
102
  const context = await browser.newContext({
87
103
  viewport: { width, height },
@@ -89,20 +105,22 @@ export async function openApp(browser, baseUrl, path, viewport, signedInAs, seed
89
105
  reducedMotion: 'reduce',
90
106
  colorScheme: 'light'
91
107
  });
92
- if (signedInAs !== null) {
108
+ if (memory === undefined && signedInAs !== null) {
93
109
  const signIn = await context.request.post(new URL('/api/auth/dev-sign-in', baseUrl).href, { data: { email: signedInAs } });
94
110
  if (!signIn.ok()) {
95
111
  throw new Error(`Dev sign-in as ${signedInAs} failed (HTTP ${signIn.status()}): ${await signIn.text()}`);
96
112
  }
97
113
  }
98
- if (seed !== undefined) {
114
+ if (memory !== undefined) {
99
115
  await context.addInitScript((value) => {
100
116
  Object.assign(globalThis, {
101
117
  patternSeed: value.seed,
102
118
  patternAdmins: value.admins,
103
- patternWorld: value.world
119
+ patternTitle: value.title,
120
+ patternWorld: value.world,
121
+ patternSignedInAs: value.signedInAs
104
122
  });
105
- }, { seed, admins, world });
123
+ }, { ...memory, signedInAs });
106
124
  }
107
125
  const page = await context.newPage();
108
126
  const problems = [];
@@ -1,4 +1,4 @@
1
- import type { ViewportName } from './browser.ts';
1
+ import type { MemoryRun, ViewportName } from './browser.ts';
2
2
  export type VisualFlowSettings = {
3
3
  url: string;
4
4
  wsEndpoint: string;
@@ -11,7 +11,7 @@ declare module 'vitest' {
11
11
  }
12
12
  }
13
13
  export declare function closeFlowBrowser(): Promise<void>;
14
- export declare function openFlowPage(settings: VisualFlowSettings, viewport: ViewportName, startAt: string, signedInAs: string | null, seed: object, admins: readonly string[], world: unknown): Promise<{
14
+ export declare function openFlowPage(settings: VisualFlowSettings, viewport: ViewportName, startAt: string, signedInAs: string | null, memory: MemoryRun): Promise<{
15
15
  send: (command: unknown) => Promise<void>;
16
16
  snapshot: () => Promise<unknown>;
17
17
  hold: (call: string) => Promise<void>;
@@ -5,9 +5,9 @@ export async function closeFlowBrowser() {
5
5
  browser = undefined;
6
6
  await (await connected)?.close();
7
7
  }
8
- export async function openFlowPage(settings, viewport, startAt, signedInAs, seed, admins, world) {
8
+ export async function openFlowPage(settings, viewport, startAt, signedInAs, memory) {
9
9
  browser ??= connectChrome(settings.wsEndpoint);
10
- const app = await openApp(await browser, settings.url, startAt, viewport, signedInAs, seed, admins, world);
10
+ const app = await openApp(await browser, settings.url, startAt, viewport, signedInAs, memory);
11
11
  return {
12
12
  send: app.dispatch,
13
13
  snapshot: app.snapshot,
@@ -59,6 +59,12 @@ async function runSteps(name, steps, run) {
59
59
  }
60
60
  }
61
61
  }
62
+ function leadingHolds(steps) {
63
+ const firstOther = steps.findIndex((step) => !('hold' in step));
64
+ return steps
65
+ .slice(0, firstOther === -1 ? steps.length : firstOther)
66
+ .flatMap((step) => ('hold' in step ? [step.hold] : []));
67
+ }
62
68
  function withTestParams(path, failing, held) {
63
69
  const params = new URLSearchParams([
64
70
  ['memory', ''],
@@ -133,8 +139,8 @@ export function defineFlow({ App }) {
133
139
  for (const viewport of visual.viewports) {
134
140
  test(`${name} [${viewport}]`, async () => {
135
141
  const { openFlowPage } = await import("./flowBrowser.js");
136
- const held = new Set();
137
- const page = await openFlowPage(visual, viewport, withTestParams(startAt, failing, held), signedInAs, seed, admins, startingWorld);
142
+ const held = new Set(leadingHolds(steps));
143
+ const page = await openFlowPage(visual, viewport, withTestParams(startAt, failing, held), signedInAs, { seed, admins, title, world: startingWorld });
138
144
  let stepFailure;
139
145
  try {
140
146
  await runSteps(name, steps, {
package/dist/index.d.ts CHANGED
@@ -1,4 +1,7 @@
1
1
  export { defineApp } from './app.ts';
2
+ export { Bookings, bookings } from './bookingsStore.ts';
3
+ export type { BookingsWorld } from './bookingsMemory.ts';
4
+ export type { AppBlock } from './blocks.ts';
2
5
  export { ActionsStore, bindActions, rankActions } from './actions.ts';
3
6
  export type { Action, ActionsBuilder } from './actions.ts';
4
7
  export { startApp } from './start.ts';
@@ -16,7 +19,7 @@ export { HttpSessionApi, MemorySessionApi } from './auth/sessionApi.ts';
16
19
  export type { EmailCodeOutcome, SessionApi, SessionCall } from './auth/sessionApi.ts';
17
20
  export { userSchema } from './auth/user.ts';
18
21
  export type { User } from './auth/user.ts';
19
- export type { AppClass, AppDefinition, AppDependencies, AppSnapshotShape, DataRouter, ModelServicesOf, NavigateCommand, PressKeyCommand, PublicStores, PlainJsonModel, RunningApp, StoreClasses, StoreInstances, StoresOf, TableStores } from './app.ts';
22
+ export type { AppClass, AppDefinition, AppDependencies, AppSnapshotShape, BlockStores, CheckedBlocks, DataRouter, ModelServicesOf, NavigateCommand, PressKeyCommand, PublicStores, PlainJsonModel, RunningApp, StoreClasses, StoreInstances, StoresOf, TableStores } from './app.ts';
20
23
  export { defineRoutes } from './routes.ts';
21
24
  export { ServiceRefused, refused } from './services.ts';
22
25
  export type { ServiceClient, ServiceMemory } from './services.ts';
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export { defineApp } from "./app.js";
2
+ export { Bookings, bookings } from "./bookingsStore.js";
2
3
  export { ActionsStore, bindActions, rankActions } from "./actions.js";
3
4
  export { startApp } from "./start.js";
4
5
  export { appearance } from "./appearance.js";
package/dist/media.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- import type { Row } from './table.ts';
2
+ import type { Fields, Row, Tables } from './table.ts';
3
3
  export declare const media: import("./table.ts").TableOf<"media", {
4
4
  readonly columns: {
5
5
  readonly key: z.ZodString;
@@ -20,6 +20,16 @@ export declare const media: import("./table.ts").TableOf<"media", {
20
20
  }>;
21
21
  export type Media = Row<typeof media>;
22
22
  export type MediaImage = Pick<Media, 'key' | 'width' | 'height' | 'placeholder' | 'alt'>;
23
+ export type StarterPhoto = {
24
+ id: string;
25
+ photo: string;
26
+ alt: string;
27
+ };
28
+ export type Starter<Group extends Tables> = {
29
+ [Key in keyof Group as Group[Key]['owned'] extends true ? never : Key]?: Group[Key] extends typeof media ? readonly StarterPhoto[] : readonly ({
30
+ id: string;
31
+ } & Fields<Group[Key]>)[];
32
+ };
23
33
  export declare const MEDIA_LIMIT_BYTES: number;
24
34
  export declare const IMAGE_WIDTHS: readonly [320, 640, 960, 1280, 1920];
25
35
  export declare function mediaPath(key: string): string;
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  export declare const TABLES_READ = "tables:read";
3
+ export declare const MEDIA_WRITE = "media:write";
3
4
  export declare const ASSERTION_SECONDS = 60;
4
5
  export declare const NEXT_PAGE_HEADER = "x-pattern-next";
5
6
  export declare const assertionKey: z.ZodObject<{
@@ -10,12 +11,20 @@ export declare const assertionKey: z.ZodObject<{
10
11
  d: z.ZodString;
11
12
  }, z.core.$strip>;
12
13
  export type AssertionKey = z.output<typeof assertionKey>;
13
- export declare function signTablesAssertion(key: AssertionKey, { sub, aud }: {
14
+ declare const assertionScope: z.ZodEnum<{
15
+ "tables:read": "tables:read";
16
+ "media:write": "media:write";
17
+ }>;
18
+ export type AssertionScope = z.output<typeof assertionScope>;
19
+ export declare function signAssertion(key: AssertionKey, { sub, aud, scope }: {
14
20
  sub: string;
15
21
  aud: string;
22
+ scope: AssertionScope;
16
23
  }, now: number): Promise<string>;
17
- export declare function verifiedTablesAssertion(token: string, { hostname, keys, now }: {
24
+ export declare function verifiedAssertion(token: string, { hostname, keys, now, scope }: {
18
25
  hostname: string;
19
26
  keys: Readonly<Record<string, string>>;
20
27
  now: number;
28
+ scope: AssertionScope;
21
29
  }): Promise<string | undefined>;
30
+ export {};
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  export const TABLES_READ = 'tables:read';
3
+ export const MEDIA_WRITE = 'media:write';
3
4
  export const ASSERTION_SECONDS = 60;
4
5
  export const NEXT_PAGE_HEADER = 'x-pattern-next';
5
6
  const CLOCK_SKEW_SECONDS = 30;
@@ -11,6 +12,7 @@ export const assertionKey = z.object({
11
12
  x: z.string(),
12
13
  d: z.string()
13
14
  });
15
+ const assertionScope = z.enum([TABLES_READ, MEDIA_WRITE]);
14
16
  const assertionHeader = z.object({
15
17
  alg: z.literal('EdDSA'),
16
18
  typ: z.literal('JWT'),
@@ -19,7 +21,7 @@ const assertionHeader = z.object({
19
21
  const assertionClaims = z.object({
20
22
  sub: z.email(),
21
23
  aud: z.string(),
22
- scope: z.literal(TABLES_READ),
24
+ scope: assertionScope,
23
25
  iat: z.int(),
24
26
  exp: z.int()
25
27
  });
@@ -53,12 +55,12 @@ function jsonOf(text) {
53
55
  function encoded(value) {
54
56
  return base64url(encoder.encode(JSON.stringify(value)));
55
57
  }
56
- export async function signTablesAssertion(key, { sub, aud }, now) {
58
+ export async function signAssertion(key, { sub, aud, scope }, now) {
57
59
  const privateKey = await crypto.subtle.importKey('jwk', { kty: key.kty, crv: key.crv, x: key.x, d: key.d }, ED25519, false, ['sign']);
58
60
  const signed = `${encoded({ alg: 'EdDSA', typ: 'JWT', kid: key.kid })}.${encoded({
59
61
  sub,
60
62
  aud,
61
- scope: TABLES_READ,
63
+ scope,
62
64
  iat: now,
63
65
  exp: now + ASSERTION_SECONDS
64
66
  })}`;
@@ -80,7 +82,7 @@ async function isSignedBy(publicKey, signed, signature) {
80
82
  return false;
81
83
  }
82
84
  }
83
- export async function verifiedTablesAssertion(token, { hostname, keys, now }) {
85
+ export async function verifiedAssertion(token, { hostname, keys, now, scope }) {
84
86
  const [header, claims, signature, ...rest] = token.split('.');
85
87
  if (header === undefined ||
86
88
  claims === undefined ||
@@ -102,6 +104,8 @@ export async function verifiedTablesAssertion(token, { hostname, keys, now }) {
102
104
  if (!parsed.success)
103
105
  return undefined;
104
106
  const { sub, aud, iat, exp } = parsed.data;
107
+ if (parsed.data.scope !== scope)
108
+ return undefined;
105
109
  const isFresh = exp > iat &&
106
110
  exp - iat <= ASSERTION_SECONDS &&
107
111
  iat <= now + CLOCK_SKEW_SECONDS &&
@@ -1,8 +1,8 @@
1
1
  import { z } from 'zod';
2
2
  import { appTitle } from './appTitle.ts';
3
3
  export { appTitle };
4
- export { ASSERTION_SECONDS, NEXT_PAGE_HEADER, TABLES_READ, assertionKey, signTablesAssertion } from './patternAccess.ts';
5
- export type { AssertionKey } from './patternAccess.ts';
4
+ export { ASSERTION_SECONDS, MEDIA_WRITE, NEXT_PAGE_HEADER, TABLES_READ, assertionKey, signAssertion } from './patternAccess.ts';
5
+ export type { AssertionKey, AssertionScope } from './patternAccess.ts';
6
6
  export declare const DASHBOARD_URL = "https://onpattern.app";
7
7
  export declare const APPS_DOMAIN = "onpattern.app";
8
8
  export declare const UPLOAD_LIMIT_BYTES: number;
@@ -21,7 +21,29 @@ export declare const UPLOAD: {
21
21
  readonly sdkVersion: "sdkVersion";
22
22
  readonly message: "message";
23
23
  readonly runWorkerFirst: "runWorkerFirst";
24
+ readonly starter: "starter";
24
25
  };
26
+ export declare const starterContent: z.ZodObject<{
27
+ photos: z.ZodArray<z.ZodObject<{
28
+ id: z.ZodString;
29
+ path: z.ZodString;
30
+ alt: z.ZodString;
31
+ }, z.core.$strip>>;
32
+ sql: z.ZodString;
33
+ }, z.core.$strip>;
34
+ export type StarterContent = z.output<typeof starterContent>;
35
+ export declare const mediaRow: z.ZodObject<{
36
+ id: z.ZodString;
37
+ key: z.ZodString;
38
+ width: z.ZodNumber;
39
+ height: z.ZodNumber;
40
+ placeholder: z.ZodNullable<z.ZodString>;
41
+ alt: z.ZodString;
42
+ type: z.ZodString;
43
+ bytes: z.ZodNumber;
44
+ createdAt: z.ZodString;
45
+ }, z.core.$strip>;
46
+ export type MediaRow = z.output<typeof mediaRow>;
25
47
  export declare function workerFirstPaths(text: string | undefined): readonly string[];
26
48
  export declare const PUBLISH_MESSAGE_LIMIT = 100;
27
49
  export declare function publishMessage(text: string): string | null;
@@ -1,7 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import { appTitle } from "./appTitle.js";
3
3
  export { appTitle };
4
- export { ASSERTION_SECONDS, NEXT_PAGE_HEADER, TABLES_READ, assertionKey, signTablesAssertion } from "./patternAccess.js";
4
+ export { ASSERTION_SECONDS, MEDIA_WRITE, NEXT_PAGE_HEADER, TABLES_READ, assertionKey, signAssertion } from "./patternAccess.js";
5
5
  import { call, defineServices } from "./services.js";
6
6
  export const DASHBOARD_URL = 'https://onpattern.app';
7
7
  export const APPS_DOMAIN = 'onpattern.app';
@@ -92,8 +92,28 @@ export const UPLOAD = {
92
92
  compatibilityDate: 'compatibilityDate',
93
93
  sdkVersion: 'sdkVersion',
94
94
  message: 'message',
95
- runWorkerFirst: 'runWorkerFirst'
95
+ runWorkerFirst: 'runWorkerFirst',
96
+ starter: 'starter'
96
97
  };
98
+ export const starterContent = z.object({
99
+ photos: z.array(z.object({
100
+ id: z.string().regex(/^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/),
101
+ path: z.string().startsWith('starter/photos/'),
102
+ alt: z.string().trim().min(1)
103
+ })),
104
+ sql: z.string()
105
+ });
106
+ export const mediaRow = z.object({
107
+ id: z.string(),
108
+ key: z.string(),
109
+ width: z.number(),
110
+ height: z.number(),
111
+ placeholder: z.string().nullable(),
112
+ alt: z.string(),
113
+ type: z.string(),
114
+ bytes: z.number(),
115
+ createdAt: z.string()
116
+ });
97
117
  const WORKER_FIRST_PATH = /^!?\/[\w.~\-/*]{0,100}$/;
98
118
  const WORKER_FIRST_LIMIT = 32;
99
119
  const ALWAYS_WORKER_FIRST = ['/api/*', '/media/*'];
package/dist/react.d.ts CHANGED
@@ -5,6 +5,8 @@ export { AppearancePicker } from './components/AppearancePicker.tsx';
5
5
  export { Avatar } from './components/Avatar.tsx';
6
6
  export { Badge } from './components/Badge.tsx';
7
7
  export { Button } from './components/Button.tsx';
8
+ export { Calendar } from './components/Calendar.tsx';
9
+ export type { CalendarDay } from './components/Calendar.tsx';
8
10
  export { Card } from './components/Card.tsx';
9
11
  export { Checkbox } from './components/Checkbox.tsx';
10
12
  export { Choice } from './components/Choice.tsx';
package/dist/react.js CHANGED
@@ -5,6 +5,7 @@ export { AppearancePicker } from "./components/AppearancePicker.js";
5
5
  export { Avatar } from "./components/Avatar.js";
6
6
  export { Badge } from "./components/Badge.js";
7
7
  export { Button } from "./components/Button.js";
8
+ export { Calendar } from "./components/Calendar.js";
8
9
  export { Card } from "./components/Card.js";
9
10
  export { Checkbox } from "./components/Checkbox.js";
10
11
  export { Choice } from "./components/Choice.js";