@trypattern/sdk 0.1.13 → 0.1.15
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/README.md +1 -1
- package/dist/cli/app.js +2 -1
- package/dist/cli/publishing.js +12 -5
- package/dist/components/RadioDescribed.d.ts +4 -0
- package/dist/components/RadioDescribed.js +20 -0
- package/dist/components/RadioGroup.d.ts +2 -0
- package/dist/components/RadioGroup.js +19 -2
- package/dist/publishWire.d.ts +11 -0
- package/dist/publishWire.js +5 -2
- package/dist/server/pages.d.ts +2 -0
- package/dist/server/pages.js +7 -2
- package/dist/vite/projectFile.d.ts +5 -0
- package/dist/vite/projectFile.js +19 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -117,7 +117,7 @@ email, phone, address, notes, status }` with `status` `requested`, `accepted`, `
|
|
|
117
117
|
shows the live photos without storing them. With
|
|
118
118
|
`pages` (`definePages(tables, { '/': head, '/items/:itemId': { head, paths } })` in
|
|
119
119
|
`server/pages.ts`) it renders the `<head>` of public pages: each `head` reads tables anyone may
|
|
120
|
-
read (`rows`, `row`, `photoUrl`) and returns `{ title, description, image, hero, jsonLd, data }`
|
|
120
|
+
read (`rows`, `row`, `photoUrl`), and `appTitle()` gives the site's name as the owner set it, and returns `{ title, description, image, hero, jsonLd, data }`
|
|
121
121
|
(or `null` for a 404). The Worker puts the title, description, canonical link, `og:*` (the image
|
|
122
122
|
at 1200×630 with `fit=cover`), `twitter:card`, JSON-LD, a preload of the `hero` with `Image`'s
|
|
123
123
|
srcset, and `data` as `<script type="application/json" id="pattern-data">` into the built
|
package/dist/cli/app.js
CHANGED
|
@@ -330,7 +330,8 @@ moves forward with any new migrations; nothing is ever deleted from it. If a ste
|
|
|
330
330
|
keeps running the previous version and the error says why. Run app check first.
|
|
331
331
|
--message '…' says what changed in a few plain words for the owner ("Pink brand colour and a welcome
|
|
332
332
|
line"); onpattern.app and app versions show it beside the version.
|
|
333
|
-
--starter (Pattern's admins only) publishes this project as the starter
|
|
333
|
+
--starter (Pattern's admins only) publishes this project as the starter new sites begin from: the one
|
|
334
|
+
for its "blueprint" in pattern.json (website or web-app), which a new business picks when it starts.
|
|
334
335
|
With a starter/ folder, its rows (starter/rows.ts) and photos (starter/photos) go with it, and a new
|
|
335
336
|
site's first publish puts them in its own database and photo bucket: the photos through the site's
|
|
336
337
|
own POST /api/media, then the rows. Later publishes never touch them; they are the owner's.`,
|
package/dist/cli/publishing.js
CHANGED
|
@@ -6,7 +6,7 @@ import { z } from 'zod';
|
|
|
6
6
|
import { DASHBOARD_URL, UPLOAD, appSummary, isPrivatePath, loginPoll, loginStarted, mediaRow, nameProblem, publishMessage, publishSummary, setupCodeExchanged, sourcePathProblem } from "../publishWire.js";
|
|
7
7
|
import { appInfo } from "../appTitle.js";
|
|
8
8
|
import { SESSION_COOKIE } from "../auth/user.js";
|
|
9
|
-
import { PROJECT_FILE, readProjectFile } from "../vite/projectFile.js";
|
|
9
|
+
import { PROJECT_FILE, blueprintOf, readProjectFile } from "../vite/projectFile.js";
|
|
10
10
|
const CONFIG_DIR = join(homedir(), '.config', 'pattern');
|
|
11
11
|
const CREDENTIALS_FILE = join(CONFIG_DIR, 'credentials.json');
|
|
12
12
|
const PENDING_FILE = join(CONFIG_DIR, 'pending-login.json');
|
|
@@ -325,7 +325,8 @@ export async function pull(io, name, cwd, setupCode) {
|
|
|
325
325
|
}
|
|
326
326
|
if (existsSync(join(target, LAUNCHER)))
|
|
327
327
|
chmodSync(join(target, LAUNCHER), 0o755);
|
|
328
|
-
|
|
328
|
+
const blueprint = blueprintOf(target);
|
|
329
|
+
writeFileSync(join(target, PROJECT_FILE), `${JSON.stringify({ name, title: result.title, ...(blueprint === undefined ? {} : { blueprint }) }, null, 2)}\n`);
|
|
329
330
|
io.say(`✓ Downloaded ${files} files of version ${result.number}`);
|
|
330
331
|
if (!(await ensurePnpm(io, target))) {
|
|
331
332
|
return {
|
|
@@ -492,12 +493,18 @@ async function followPublish(io, name, started) {
|
|
|
492
493
|
export async function publish(io, root, isStarter, message, starter) {
|
|
493
494
|
try {
|
|
494
495
|
sayHost(io);
|
|
495
|
-
const { name } = readProjectFile(root);
|
|
496
|
+
const { name, blueprint } = readProjectFile(root);
|
|
497
|
+
if (isStarter && blueprint === undefined) {
|
|
498
|
+
throw new Error(`${PROJECT_FILE} needs "blueprint" ("website" or "web-app") to publish the starter for it`);
|
|
499
|
+
}
|
|
496
500
|
const built = await io.build();
|
|
497
501
|
if (built.code !== 0)
|
|
498
502
|
return built;
|
|
499
503
|
io.say('✓ Built');
|
|
500
504
|
const { form, sources } = uploadForm(root, io, isStarter, starter);
|
|
505
|
+
if (isStarter && blueprint !== undefined) {
|
|
506
|
+
form.append(UPLOAD.blueprint, blueprint);
|
|
507
|
+
}
|
|
501
508
|
const said = publishMessage(message ?? '');
|
|
502
509
|
if (said !== null)
|
|
503
510
|
form.append(UPLOAD.message, said);
|
|
@@ -521,8 +528,8 @@ export async function publish(io, root, isStarter, message, starter) {
|
|
|
521
528
|
if (result === undefined) {
|
|
522
529
|
return {
|
|
523
530
|
code: 0,
|
|
524
|
-
data: { starter: io.sdkVersion },
|
|
525
|
-
text: `✓ Published the starter for SDK ${io.sdkVersion}`
|
|
531
|
+
data: { starter: io.sdkVersion, blueprint },
|
|
532
|
+
text: `✓ Published the ${blueprint} starter for SDK ${io.sdkVersion}`
|
|
526
533
|
};
|
|
527
534
|
}
|
|
528
535
|
return await followPublish(io, name, result);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as stylex from '@stylexjs/stylex';
|
|
3
|
+
import { space } from "../theme/tokens.stylex.js";
|
|
4
|
+
import { Text } from "./Text.js";
|
|
5
|
+
export function RadioDescribed({ option }) {
|
|
6
|
+
const tone = option.isDisabled === true ? 'tertiary' : undefined;
|
|
7
|
+
return (_jsxs("span", { ...stylex.props(styles.described), children: [_jsxs("span", { ...stylex.props(styles.labelLine), children: [_jsx(Text, { variant: "label", tone: tone ?? 'primary', children: option.label }), option.badge] }), _jsx(Text, { variant: "body", tone: tone ?? 'secondary', children: option.description })] }));
|
|
8
|
+
}
|
|
9
|
+
const styles = stylex.create({
|
|
10
|
+
described: {
|
|
11
|
+
display: 'flex',
|
|
12
|
+
flexDirection: 'column',
|
|
13
|
+
gap: space.xxs
|
|
14
|
+
},
|
|
15
|
+
labelLine: {
|
|
16
|
+
display: 'flex',
|
|
17
|
+
gap: space.sm,
|
|
18
|
+
alignItems: 'center'
|
|
19
|
+
}
|
|
20
|
+
});
|
|
@@ -2,9 +2,10 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { Radio as BaseRadio } from '@base-ui/react/radio';
|
|
3
3
|
import { RadioGroup as BaseRadioGroup } from '@base-ui/react/radio-group';
|
|
4
4
|
import * as stylex from '@stylexjs/stylex';
|
|
5
|
-
import { borders, colors, iconSizes, motion, radii, shadows, space } from "../theme/tokens.stylex.js";
|
|
5
|
+
import { borders, colors, iconSizes, lineHeights, motion, radii, shadows, space } from "../theme/tokens.stylex.js";
|
|
6
6
|
import { focusRing } from "./controlLook.js";
|
|
7
7
|
import { interact } from "./interaction.js";
|
|
8
|
+
import { RadioDescribed } from "./RadioDescribed.js";
|
|
8
9
|
import { Text } from "./Text.js";
|
|
9
10
|
export function RadioGroup({ label, value, options, onChange, size = 'md', style }) {
|
|
10
11
|
return (_jsx(BaseRadioGroup, { "aria-label": label, value: value, onValueChange: (next) => {
|
|
@@ -18,7 +19,8 @@ export function RadioGroup({ label, value, options, onChange, size = 'md', style
|
|
|
18
19
|
});
|
|
19
20
|
onChange(chosen.value);
|
|
20
21
|
}, className: stylex.props(styles.group, size === 'lg' && styles.groupLarge, style)
|
|
21
|
-
.className, children: options.map((option) => (_jsxs("label", { ...stylex.props(styles.row, size === 'lg' && styles.rowLarge, option.isDisabled === true && styles.rowDisabled), children: [_jsx(BaseRadio.Root, { value: option.value, disabled: option.isDisabled === true, "aria-label": option.label, className: (state) => stylex.props(styles.dot, state.checked ? styles.dotOn : styles.dotOff, focusRing.visible).className, children: _jsx(BaseRadio.Indicator, { className: stylex.props(styles.mark).className }) }), _jsx("span", { ...stylex.props(styles.content), children: option.content ??
|
|
22
|
+
.className, children: options.map((option) => (_jsxs("label", { ...stylex.props(styles.row, size === 'lg' && styles.rowLarge, option.description !== undefined && styles.rowDescribed, option.isDisabled === true && styles.rowDisabled), children: [_jsx("span", { ...stylex.props(styles.dotLine), children: _jsx(BaseRadio.Root, { value: option.value, disabled: option.isDisabled === true, "aria-label": option.label, className: (state) => stylex.props(styles.dot, state.checked ? styles.dotOn : styles.dotOff, option.isDisabled === true && styles.dotDisabled, focusRing.visible).className, children: _jsx(BaseRadio.Indicator, { className: stylex.props(styles.mark).className }) }) }), _jsx("span", { ...stylex.props(styles.content), children: option.content ??
|
|
23
|
+
(option.description === undefined ? (_jsx(Text, { variant: size === 'lg' ? 'lead' : 'body', tone: option.isDisabled === true ? 'tertiary' : 'primary', isTruncated: true, children: option.label })) : (_jsx(RadioDescribed, { option: option }))) }), option.trailing === undefined ? null : (_jsx("span", { ...stylex.props(styles.trailing), children: option.trailing }))] }, option.value))) }));
|
|
22
24
|
}
|
|
23
25
|
const styles = stylex.create({
|
|
24
26
|
group: {
|
|
@@ -50,9 +52,20 @@ const styles = stylex.create({
|
|
|
50
52
|
paddingBlock: space.lg,
|
|
51
53
|
paddingInline: `calc(${space.md} + ${space.xxs})`
|
|
52
54
|
},
|
|
55
|
+
rowDescribed: {
|
|
56
|
+
gap: `calc(${space.sm} + ${space.xxs})`,
|
|
57
|
+
alignItems: 'flex-start',
|
|
58
|
+
paddingBlock: `calc(${space.md} + ${space.xxs})`
|
|
59
|
+
},
|
|
53
60
|
rowDisabled: {
|
|
54
61
|
color: colors.tertiaryForeground
|
|
55
62
|
},
|
|
63
|
+
dotLine: {
|
|
64
|
+
display: 'flex',
|
|
65
|
+
flexShrink: 0,
|
|
66
|
+
alignItems: 'center',
|
|
67
|
+
height: lineHeights.bodySm
|
|
68
|
+
},
|
|
56
69
|
dot: {
|
|
57
70
|
display: 'grid',
|
|
58
71
|
flexShrink: 0,
|
|
@@ -71,6 +84,10 @@ const styles = stylex.create({
|
|
|
71
84
|
backgroundColor: colors.componentOnContainerStrong,
|
|
72
85
|
borderColor: colors.borderStrong
|
|
73
86
|
},
|
|
87
|
+
dotDisabled: {
|
|
88
|
+
backgroundColor: colors.disabled,
|
|
89
|
+
borderColor: colors.borderDefault
|
|
90
|
+
},
|
|
74
91
|
dotOn: {
|
|
75
92
|
backgroundColor: colors.invertedSolid,
|
|
76
93
|
borderColor: colors.invertedSolid
|
package/dist/publishWire.d.ts
CHANGED
|
@@ -22,7 +22,14 @@ export declare const UPLOAD: {
|
|
|
22
22
|
readonly message: "message";
|
|
23
23
|
readonly runWorkerFirst: "runWorkerFirst";
|
|
24
24
|
readonly starter: "starter";
|
|
25
|
+
readonly blueprint: "blueprint";
|
|
25
26
|
};
|
|
27
|
+
export declare const BLUEPRINTS: readonly ["website", "web-app"];
|
|
28
|
+
export declare const blueprintName: z.ZodEnum<{
|
|
29
|
+
website: "website";
|
|
30
|
+
"web-app": "web-app";
|
|
31
|
+
}>;
|
|
32
|
+
export type BlueprintName = z.output<typeof blueprintName>;
|
|
26
33
|
export declare const starterContent: z.ZodObject<{
|
|
27
34
|
photos: z.ZodArray<z.ZodObject<{
|
|
28
35
|
id: z.ZodString;
|
|
@@ -371,6 +378,10 @@ export declare const publishServices: {
|
|
|
371
378
|
readonly create: NoInfer<import("./services.ts").ServiceCall<"/apps", {
|
|
372
379
|
name: z.ZodString;
|
|
373
380
|
title: z.ZodString;
|
|
381
|
+
blueprint: z.ZodEnum<{
|
|
382
|
+
website: "website";
|
|
383
|
+
"web-app": "web-app";
|
|
384
|
+
}>;
|
|
374
385
|
}, z.ZodObject<{
|
|
375
386
|
name: z.ZodString;
|
|
376
387
|
title: z.ZodString;
|
package/dist/publishWire.js
CHANGED
|
@@ -93,8 +93,11 @@ export const UPLOAD = {
|
|
|
93
93
|
sdkVersion: 'sdkVersion',
|
|
94
94
|
message: 'message',
|
|
95
95
|
runWorkerFirst: 'runWorkerFirst',
|
|
96
|
-
starter: 'starter'
|
|
96
|
+
starter: 'starter',
|
|
97
|
+
blueprint: 'blueprint'
|
|
97
98
|
};
|
|
99
|
+
export const BLUEPRINTS = ['website', 'web-app'];
|
|
100
|
+
export const blueprintName = z.enum(BLUEPRINTS);
|
|
98
101
|
export const starterContent = z.object({
|
|
99
102
|
photos: z.array(z.object({
|
|
100
103
|
id: z.string().regex(/^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/),
|
|
@@ -259,7 +262,7 @@ export const publishServices = defineServices({
|
|
|
259
262
|
read: call({ get: '/apps/:name', reply: appSummary }),
|
|
260
263
|
create: call({
|
|
261
264
|
post: '/apps',
|
|
262
|
-
input: { name: appName, title: appTitle },
|
|
265
|
+
input: { name: appName, title: appTitle, blueprint: blueprintName },
|
|
263
266
|
reply: appSummary
|
|
264
267
|
})
|
|
265
268
|
},
|
package/dist/server/pages.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export type PageRequest<Group extends Tables> = {
|
|
|
14
14
|
rows: <Name extends ReadableName<Group>>(table: Name) => Promise<readonly Row<Group[Name]>[]>;
|
|
15
15
|
row: <Name extends ReadableName<Group>>(table: Name, id: string) => Promise<Row<Group[Name]> | undefined>;
|
|
16
16
|
photoUrl: (image: MediaImage) => string;
|
|
17
|
+
appTitle: () => Promise<string>;
|
|
17
18
|
};
|
|
18
19
|
export type PageHero = {
|
|
19
20
|
image: MediaImage;
|
|
@@ -49,6 +50,7 @@ type AnyRequest = {
|
|
|
49
50
|
rows: (table: string) => Promise<readonly unknown[]>;
|
|
50
51
|
row: (table: string, id: string) => Promise<unknown>;
|
|
51
52
|
photoUrl: (image: MediaImage) => string;
|
|
53
|
+
appTitle: () => Promise<string>;
|
|
52
54
|
};
|
|
53
55
|
type PageRoute = {
|
|
54
56
|
pattern: string;
|
package/dist/server/pages.js
CHANGED
|
@@ -71,6 +71,7 @@ function readableTable(pages, name) {
|
|
|
71
71
|
function requestFor(c, pages, params) {
|
|
72
72
|
const url = new URL(c.req.url);
|
|
73
73
|
const read = new Map();
|
|
74
|
+
let title;
|
|
74
75
|
return {
|
|
75
76
|
url,
|
|
76
77
|
params,
|
|
@@ -81,7 +82,11 @@ function requestFor(c, pages, params) {
|
|
|
81
82
|
return found;
|
|
82
83
|
},
|
|
83
84
|
row: (name, id) => readRow(c.env.DB, readableTable(pages, name), id),
|
|
84
|
-
photoUrl: (image) => `${url.origin}${mediaPath(image.key)}
|
|
85
|
+
photoUrl: (image) => `${url.origin}${mediaPath(image.key)}`,
|
|
86
|
+
appTitle: () => {
|
|
87
|
+
title ??= readTitle(c.env.DB);
|
|
88
|
+
return title;
|
|
89
|
+
}
|
|
85
90
|
};
|
|
86
91
|
}
|
|
87
92
|
function escapeHtml(text) {
|
|
@@ -217,7 +222,7 @@ export async function renderPage(c, pages, route, params) {
|
|
|
217
222
|
try {
|
|
218
223
|
head = await route.head(request);
|
|
219
224
|
if (head !== null) {
|
|
220
|
-
tags = headTags(pages, request.url, await
|
|
225
|
+
tags = headTags(pages, request.url, await request.appTitle(), head);
|
|
221
226
|
}
|
|
222
227
|
}
|
|
223
228
|
catch (error) {
|
|
@@ -3,7 +3,12 @@ export declare const PROJECT_FILE = "pattern.json";
|
|
|
3
3
|
declare const projectFile: z.ZodObject<{
|
|
4
4
|
name: z.ZodString;
|
|
5
5
|
title: z.ZodString;
|
|
6
|
+
blueprint: z.ZodOptional<z.ZodEnum<{
|
|
7
|
+
website: "website";
|
|
8
|
+
"web-app": "web-app";
|
|
9
|
+
}>>;
|
|
6
10
|
}, z.core.$strip>;
|
|
7
11
|
export type ProjectFile = z.output<typeof projectFile>;
|
|
8
12
|
export declare function readProjectFile(root: string): ProjectFile;
|
|
13
|
+
export declare function blueprintOf(root: string): "website" | "web-app" | undefined;
|
|
9
14
|
export {};
|
package/dist/vite/projectFile.js
CHANGED
|
@@ -2,8 +2,13 @@ import { existsSync, readFileSync } from 'node:fs';
|
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import { appTitle } from "../appTitle.js";
|
|
5
|
+
import { blueprintName } from "../publishWire.js";
|
|
5
6
|
export const PROJECT_FILE = 'pattern.json';
|
|
6
|
-
const projectFile = z.object({
|
|
7
|
+
const projectFile = z.object({
|
|
8
|
+
name: z.string().min(1),
|
|
9
|
+
title: appTitle,
|
|
10
|
+
blueprint: blueprintName.optional()
|
|
11
|
+
});
|
|
7
12
|
export function readProjectFile(root) {
|
|
8
13
|
const path = join(root, PROJECT_FILE);
|
|
9
14
|
if (!existsSync(path)) {
|
|
@@ -15,3 +20,16 @@ export function readProjectFile(root) {
|
|
|
15
20
|
}
|
|
16
21
|
return parsed.data;
|
|
17
22
|
}
|
|
23
|
+
export function blueprintOf(root) {
|
|
24
|
+
const path = join(root, PROJECT_FILE);
|
|
25
|
+
if (!existsSync(path))
|
|
26
|
+
return undefined;
|
|
27
|
+
try {
|
|
28
|
+
const raw = JSON.parse(readFileSync(path, 'utf8'));
|
|
29
|
+
const parsed = blueprintName.safeParse(Reflect.get(Object(raw), 'blueprint'));
|
|
30
|
+
return parsed.success ? parsed.data : undefined;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
}
|