@trypattern/sdk 0.1.32 → 0.1.34
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/app.d.ts +6 -2
- package/dist/app.js +6 -1
- package/dist/blocks.js +11 -3
- package/dist/bookings.d.ts +78 -0
- package/dist/bookings.js +7 -1
- package/dist/bookingsStore.d.ts +78 -0
- package/dist/cli/app.js +17 -2
- package/dist/cli/requests.d.ts +5 -0
- package/dist/cli/requests.js +115 -0
- package/dist/components/AppFrame.js +12 -2
- package/dist/components/FilePreview.d.ts +10 -0
- package/dist/components/FilePreview.js +36 -0
- package/dist/components/Image.d.ts +2 -1
- package/dist/components/Image.js +5 -2
- package/dist/components/Menu.js +2 -2
- package/dist/components/MenuItems.d.ts +1 -0
- package/dist/components/MenuItems.js +13 -2
- package/dist/components/SegmentedControl.d.ts +1 -0
- package/dist/components/SegmentedControl.js +10 -3
- package/dist/components/Sidebar.d.ts +1 -0
- package/dist/components/Sidebar.js +18 -2
- package/dist/components/TableRow.d.ts +2 -1
- package/dist/components/TableRow.js +5 -2
- package/dist/components/icons.js +1 -2
- package/dist/flows/browser.js +7 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/inquiriesStore.d.ts +134 -0
- package/dist/inquiriesStore.js +64 -0
- package/dist/media.d.ts +2 -1
- package/dist/media.js +4 -4
- package/dist/patternAccess.d.ts +8 -2
- package/dist/patternAccess.js +17 -5
- package/dist/publishWire.d.ts +866 -0
- package/dist/publishWire.js +78 -0
- package/dist/react.d.ts +1 -0
- package/dist/react.js +1 -0
- package/dist/requests.d.ts +361 -0
- package/dist/requests.js +180 -0
- package/dist/rules/lintPlugin.js +1 -0
- package/dist/schema.d.ts +2 -0
- package/dist/schema.js +1 -0
- package/dist/server/authService.d.ts +1 -0
- package/dist/server/bookings.d.ts +2 -9
- package/dist/server/bookings.js +149 -31
- package/dist/server/createServer.js +4 -0
- package/dist/server/env.d.ts +3 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +1 -0
- package/dist/server/inquiries.d.ts +9 -0
- package/dist/server/inquiries.js +150 -0
- package/dist/server/patternViewer.js +7 -1
- package/dist/server/requests.d.ts +76 -0
- package/dist/server/requests.js +328 -0
- package/dist/server/services.js +9 -1
- package/dist/server/tables.js +8 -1
- package/package.json +1 -1
|
@@ -2,15 +2,15 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { Radio } from '@base-ui/react/radio';
|
|
3
3
|
import { RadioGroup } from '@base-ui/react/radio-group';
|
|
4
4
|
import * as stylex from '@stylexjs/stylex';
|
|
5
|
-
import { borders, colors, fontWeights, motion, radii } from "../theme/tokens.stylex.js";
|
|
5
|
+
import { borders, colors, fontSizes, fontWeights, lineHeights, motion, radii, space } from "../theme/tokens.stylex.js";
|
|
6
6
|
import { interact } from "./interaction.js";
|
|
7
|
-
import { controlSizes, focusRing } from "./controlLook.js";
|
|
7
|
+
import { buttonCorners, controlSizes, focusRing } from "./controlLook.js";
|
|
8
8
|
export function SegmentedControl({ label, value, options, size = 'md', disabled = false, onChange, style }) {
|
|
9
9
|
const index = Math.max(0, options.findIndex((option) => option.value === value));
|
|
10
10
|
return (_jsxs(RadioGroup, { "aria-label": label, value: value, disabled: disabled, onValueChange: (next) => {
|
|
11
11
|
interact({ primitive: 'SegmentedControl', event: 'change', label });
|
|
12
12
|
onChange(next);
|
|
13
|
-
}, className: stylex.props(styles.group, style).className, children: [_jsx("span", { ...stylex.props(styles.indicator(index, options.length)) }), options.map((option) => (
|
|
13
|
+
}, className: stylex.props(styles.group, style).className, children: [_jsx("span", { ...stylex.props(styles.indicator(index, options.length)) }), options.map((option) => (_jsxs(Radio.Root, { value: option.value, className: (state) => stylex.props(styles.option, controlSizes[size], buttonCorners[size], !disabled && styles.hoverable, state.checked && styles.optionChecked, disabled && styles.disabled, focusRing.visible).className, children: [option.label, option.count === undefined ? null : (_jsx("span", { ...stylex.props(styles.count), children: option.count }))] }, option.value)))] }));
|
|
14
14
|
}
|
|
15
15
|
const styles = stylex.create({
|
|
16
16
|
group: {
|
|
@@ -44,6 +44,7 @@ const styles = stylex.create({
|
|
|
44
44
|
position: 'relative',
|
|
45
45
|
zIndex: 1,
|
|
46
46
|
display: 'inline-flex',
|
|
47
|
+
gap: `calc(${space.xs} + ${space.xxs})`,
|
|
47
48
|
alignItems: 'center',
|
|
48
49
|
justifyContent: 'center',
|
|
49
50
|
color: colors.secondaryForeground,
|
|
@@ -64,5 +65,11 @@ const styles = stylex.create({
|
|
|
64
65
|
},
|
|
65
66
|
disabled: {
|
|
66
67
|
color: colors.disabledForeground
|
|
68
|
+
},
|
|
69
|
+
count: {
|
|
70
|
+
fontSize: fontSizes.captionLg,
|
|
71
|
+
fontWeight: fontWeights.normal,
|
|
72
|
+
lineHeight: lineHeights.caption,
|
|
73
|
+
color: colors.tertiaryForeground
|
|
67
74
|
}
|
|
68
75
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import * as stylex from '@stylexjs/stylex';
|
|
3
3
|
import { NavLink } from 'react-router';
|
|
4
|
-
import { breakpoints, colors, controls, iconSizes, layers, motion, radii, shadows, space, widths } from "../theme/tokens.stylex.js";
|
|
4
|
+
import { breakpoints, colors, controls, fontSizes, fontWeights, iconSizes, layers, lineHeights, motion, radii, shadows, space, widths } from "../theme/tokens.stylex.js";
|
|
5
5
|
import { Badge } from "./Badge.js";
|
|
6
6
|
import { focusRing } from "./controlLook.js";
|
|
7
7
|
import { Button } from "./Button.js";
|
|
@@ -16,7 +16,8 @@ export function Sidebar({ workspace, workspaceMenu, links, isToggled, isAnimatin
|
|
|
16
16
|
onFollowLink();
|
|
17
17
|
};
|
|
18
18
|
return (_jsxs("nav", { ...stylex.props(styles.sidebar, isToggled && styles.sidebarToggled, isAnimating && styles.sidebarAnimating), children: [_jsx("div", { ...stylex.props(styles.header), children: _jsx(Menu, { trigger: _jsxs(_Fragment, { children: [_jsx("span", { ...stylex.props(styles.logo), children: _jsx(Text, { variant: "caption", children: workspace.charAt(0) }) }), _jsx(Text, { variant: "label", isTruncated: true, children: workspace }), _jsx(Icon, { name: "chevronDown" })] }), items: workspaceMenu }) }), onSearch === undefined ? null : (_jsx(Button, { variant: "secondary", icon: "search", shortcut: ['mod', 'k'], isFullWidth: true, align: "start", onClick: onSearch, children: "Search" })), _jsx("div", { ...stylex.props(styles.body), children: _jsx(ScrollArea, { children: _jsxs("div", { ...stylex.props(styles.group), children: [_jsx("span", { ...stylex.props(styles.groupLabel), children: _jsx(Text, { variant: "label", tone: "tertiary", children: "Workspace" }) }), links.map((item) => (_jsxs(NavLink, { onClick: () => followLink(item), to: item.to, end: item.to === '/' ||
|
|
19
|
-
links.some((other) => String(other.to).startsWith(`${String(item.to)}/`)), className: ({ isActive }) => stylex.props(styles.item, isActive && styles.itemCurrent, focusRing.visible, focusRing.inside).className ?? '', children: [_jsx(Icon, { name: item.icon }), _jsx(Text, { variant: "label", isTruncated: true, style: styles.itemLabel, children: item.label }), typeof item.count !== 'number' ? null :
|
|
19
|
+
links.some((other) => String(other.to).startsWith(`${String(item.to)}/`)), className: ({ isActive }) => stylex.props(styles.item, isActive && styles.itemCurrent, focusRing.visible, focusRing.inside).className ?? '', children: [_jsx(Icon, { name: item.icon }), _jsx(Text, { variant: "label", isTruncated: true, style: styles.itemLabel, children: item.label }), typeof item.count !== 'number' ? null : item.countTone ===
|
|
20
|
+
'brand' ? (_jsx("span", { ...stylex.props(styles.count), children: item.count })) : (_jsx(Badge, { variant: "transparent", children: item.count }))] }, item.to)))] }) }) })] }));
|
|
20
21
|
}
|
|
21
22
|
const styles = stylex.create({
|
|
22
23
|
sidebar: {
|
|
@@ -132,6 +133,21 @@ const styles = stylex.create({
|
|
|
132
133
|
color: colors.primaryForeground,
|
|
133
134
|
backgroundColor: colors.transparentHover
|
|
134
135
|
},
|
|
136
|
+
count: {
|
|
137
|
+
display: 'inline-flex',
|
|
138
|
+
flexShrink: 0,
|
|
139
|
+
alignItems: 'center',
|
|
140
|
+
justifyContent: 'center',
|
|
141
|
+
minWidth: `calc(${space.lg} + ${space.xxs})`,
|
|
142
|
+
height: `calc(${space.lg} + ${space.xxs})`,
|
|
143
|
+
paddingInline: `calc(${space.xs} + ${space.xxs} / 2)`,
|
|
144
|
+
fontSize: fontSizes.captionSm,
|
|
145
|
+
fontWeight: fontWeights.medium,
|
|
146
|
+
lineHeight: lineHeights.caption,
|
|
147
|
+
color: colors.onBrandSolidForeground,
|
|
148
|
+
backgroundColor: colors.brandSolid,
|
|
149
|
+
borderRadius: radii.full
|
|
150
|
+
},
|
|
135
151
|
itemLabel: {
|
|
136
152
|
flexGrow: 1
|
|
137
153
|
}
|
|
@@ -2,8 +2,9 @@ import type { ReactNode } from 'react';
|
|
|
2
2
|
type TableRowProps = {
|
|
3
3
|
label: string;
|
|
4
4
|
isCurrent?: boolean;
|
|
5
|
+
size?: 'md' | 'lg';
|
|
5
6
|
onClick: () => void;
|
|
6
7
|
children: ReactNode;
|
|
7
8
|
};
|
|
8
|
-
export declare function TableRow({ label, isCurrent, onClick, children }: TableRowProps): import("react").JSX.Element;
|
|
9
|
+
export declare function TableRow({ label, isCurrent, size, onClick, children }: TableRowProps): import("react").JSX.Element;
|
|
9
10
|
export {};
|
|
@@ -4,11 +4,11 @@ import * as stylex from '@stylexjs/stylex';
|
|
|
4
4
|
import { borders, colors, space } from "../theme/tokens.stylex.js";
|
|
5
5
|
import { focusRing } from "./controlLook.js";
|
|
6
6
|
import { interact } from "./interaction.js";
|
|
7
|
-
export function TableRow({ label, isCurrent = false, onClick, children }) {
|
|
7
|
+
export function TableRow({ label, isCurrent = false, size = 'md', onClick, children }) {
|
|
8
8
|
return (_jsx(BaseButton, { "aria-label": label, "aria-pressed": isCurrent, onClick: () => {
|
|
9
9
|
interact({ primitive: 'TableRow', event: 'click', label });
|
|
10
10
|
onClick();
|
|
11
|
-
}, className: stylex.props(styles.row, isCurrent && styles.current, focusRing.visible, focusRing.inside).className, children: children }));
|
|
11
|
+
}, className: stylex.props(styles.row, size === 'lg' && styles.large, isCurrent && styles.current, focusRing.visible, focusRing.inside).className, children: children }));
|
|
12
12
|
}
|
|
13
13
|
const styles = stylex.create({
|
|
14
14
|
row: {
|
|
@@ -30,6 +30,9 @@ const styles = stylex.create({
|
|
|
30
30
|
borderBottomStyle: 'solid',
|
|
31
31
|
borderBottomWidth: borders.thin
|
|
32
32
|
},
|
|
33
|
+
large: {
|
|
34
|
+
height: `calc(${space.xxxl} + ${space.sm})`
|
|
35
|
+
},
|
|
33
36
|
current: {
|
|
34
37
|
backgroundColor: colors.transparentHover
|
|
35
38
|
}
|
package/dist/components/icons.js
CHANGED
|
@@ -102,8 +102,7 @@ const builtInIcons = {
|
|
|
102
102
|
],
|
|
103
103
|
arrowDown: 'M12 5v14M6 13l6 6 6-6',
|
|
104
104
|
logOut: [
|
|
105
|
-
'
|
|
106
|
-
'M19.5 12H10M16 8.5l3.5 3.5-3.5 3.5'
|
|
105
|
+
'M20.25 12L9 12M20.25 12L15.75 16.5M20.25 12L15.75 7.5M11.25 20.25H5.75C4.64543 20.25 3.75 19.3546 3.75 18.25L3.75 5.75C3.75 4.64543 4.64543 3.75 5.75 3.75L11.25 3.75'
|
|
107
106
|
],
|
|
108
107
|
today: 'M11.998 3.291V1.768M5.84 18.159L4.763 19.236M11.998 22.233V20.709M19.233 4.765L18.156 5.842M20.707 12H22.23M18.156 18.159L19.233 19.236M1.766 12H3.289M4.763 4.765L5.84 5.842M15.71 8.288C17.761 10.338 17.761 13.662 15.71 15.712C13.66 17.763 10.336 17.763 8.286 15.712C6.235 13.662 6.235 10.338 8.286 8.288C10.336 6.238 13.66 6.238 15.71 8.288Z',
|
|
109
108
|
checklist: 'M11.75 16.25H20.25M11.75 7.75H20.25M3.75 8.625L5.417 9.75L8.75 5.25M3.75 17.625L5.417 18.75L8.75 14.25',
|
package/dist/flows/browser.js
CHANGED
|
@@ -4,6 +4,7 @@ export const viewports = {
|
|
|
4
4
|
compact: { width: 390, height: 844, deviceScaleFactor: 2 }
|
|
5
5
|
};
|
|
6
6
|
export const viewportNames = ['desktop', 'compact'];
|
|
7
|
+
const STAND_IN_PHOTO = '<svg xmlns="http://www.w3.org/2000/svg" width="4" height="3"><rect width="4" height="3" fill="#d9d4c8"/></svg>';
|
|
7
8
|
const QUIET_NETWORK_MS = 250;
|
|
8
9
|
const SETTLE_TIMEOUT_MS = 10_000;
|
|
9
10
|
const HOOK_TIMEOUT_MS = 10_000;
|
|
@@ -122,6 +123,12 @@ export async function openApp(browser, baseUrl, path, viewport, signedInAs, memo
|
|
|
122
123
|
});
|
|
123
124
|
}, { ...memory, signedInAs });
|
|
124
125
|
}
|
|
126
|
+
await context.route((url) => url.origin !== new URL(baseUrl).origin, (route) => route.request().resourceType() === 'image'
|
|
127
|
+
? route.fulfill({
|
|
128
|
+
contentType: 'image/svg+xml',
|
|
129
|
+
body: STAND_IN_PHOTO
|
|
130
|
+
})
|
|
131
|
+
: route.continue());
|
|
125
132
|
const page = await context.newPage();
|
|
126
133
|
const problems = [];
|
|
127
134
|
recordProblems(page, problems);
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export { defineApp } from './app.ts';
|
|
2
2
|
export { Bookings, bookings } from './bookingsStore.ts';
|
|
3
3
|
export type { BookingsWorld } from './bookingsMemory.ts';
|
|
4
|
+
export { Inquiries, inquiries } from './inquiriesStore.ts';
|
|
5
|
+
export type { InquiriesWorld } from './inquiriesStore.ts';
|
|
4
6
|
export type { AppBlock } from './blocks.ts';
|
|
5
7
|
export { ActionsStore, bindActions, rankActions } from './actions.ts';
|
|
6
8
|
export type { Action, ActionsBuilder } from './actions.ts';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { defineApp } from "./app.js";
|
|
2
2
|
export { Bookings, bookings } from "./bookingsStore.js";
|
|
3
|
+
export { Inquiries, inquiries } from "./inquiriesStore.js";
|
|
3
4
|
export { ActionsStore, bindActions, rankActions } from "./actions.js";
|
|
4
5
|
export { startApp } from "./start.js";
|
|
5
6
|
export { appearance } from "./appearance.js";
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { WriteOutcome } from './access.ts';
|
|
3
|
+
import { inquiryValue } from './requests.ts';
|
|
4
|
+
import type { ServiceClient, ServiceMemory } from './services.ts';
|
|
5
|
+
type Fields = Readonly<Record<string, z.output<typeof inquiryValue>>>;
|
|
6
|
+
export type InquiriesWorld = {
|
|
7
|
+
sent?: {
|
|
8
|
+
source: string;
|
|
9
|
+
id: string;
|
|
10
|
+
fields: Fields;
|
|
11
|
+
}[];
|
|
12
|
+
sources?: string[];
|
|
13
|
+
};
|
|
14
|
+
declare const Inquiries_base: import("./store.ts").StoreClass<{
|
|
15
|
+
sent: z.ZodDefault<z.ZodNullable<z.ZodObject<{
|
|
16
|
+
source: z.ZodString;
|
|
17
|
+
id: z.ZodString;
|
|
18
|
+
}, z.core.$strip>>>;
|
|
19
|
+
isSending: z.ZodDefault<z.ZodBoolean>;
|
|
20
|
+
}, [], ServiceClient<{
|
|
21
|
+
readonly inquiries: {
|
|
22
|
+
readonly send: NoInfer<import("./services.ts").ServiceCall<"/requests/:source", {
|
|
23
|
+
fields: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>>;
|
|
24
|
+
}, z.ZodObject<{
|
|
25
|
+
id: z.ZodString;
|
|
26
|
+
}, z.core.$strip>, false>>;
|
|
27
|
+
};
|
|
28
|
+
}>>;
|
|
29
|
+
export declare class Inquiries extends Inquiries_base {
|
|
30
|
+
send(source: string, fields: Fields): Promise<WriteOutcome>;
|
|
31
|
+
}
|
|
32
|
+
export declare const inquiries: {
|
|
33
|
+
readonly name: "inquiries";
|
|
34
|
+
readonly tables: {
|
|
35
|
+
requestStates: import("./table.ts").TableOf<"requestStates", {
|
|
36
|
+
readonly columns: {
|
|
37
|
+
readonly source: z.ZodString;
|
|
38
|
+
readonly requestId: z.ZodString;
|
|
39
|
+
readonly state: z.ZodEnum<{
|
|
40
|
+
done: "done";
|
|
41
|
+
open: "open";
|
|
42
|
+
}>;
|
|
43
|
+
readonly revision: z.ZodNumber;
|
|
44
|
+
readonly sequence: z.ZodNumber;
|
|
45
|
+
readonly updatedAt: z.ZodString;
|
|
46
|
+
};
|
|
47
|
+
readonly access: {
|
|
48
|
+
readonly read: "server";
|
|
49
|
+
readonly create: "server";
|
|
50
|
+
readonly update: "server";
|
|
51
|
+
readonly delete: "server";
|
|
52
|
+
};
|
|
53
|
+
readonly writes: "confirmed";
|
|
54
|
+
}>;
|
|
55
|
+
requestEvents: import("./table.ts").TableOf<"requestEvents", {
|
|
56
|
+
readonly columns: {
|
|
57
|
+
readonly source: z.ZodString;
|
|
58
|
+
readonly requestId: z.ZodString;
|
|
59
|
+
readonly kind: z.ZodEnum<{
|
|
60
|
+
done: "done";
|
|
61
|
+
accepted: "accepted";
|
|
62
|
+
declined: "declined";
|
|
63
|
+
replied: "replied";
|
|
64
|
+
received: "received";
|
|
65
|
+
}>;
|
|
66
|
+
};
|
|
67
|
+
readonly access: {
|
|
68
|
+
readonly read: "server";
|
|
69
|
+
readonly create: "server";
|
|
70
|
+
readonly update: "server";
|
|
71
|
+
readonly delete: "server";
|
|
72
|
+
};
|
|
73
|
+
readonly writes: "confirmed";
|
|
74
|
+
}>;
|
|
75
|
+
requestOutbox: import("./table.ts").TableOf<"requestOutbox", {
|
|
76
|
+
readonly columns: {
|
|
77
|
+
readonly source: z.ZodString;
|
|
78
|
+
readonly requestId: z.ZodString;
|
|
79
|
+
readonly subject: z.ZodString;
|
|
80
|
+
readonly text: z.ZodString;
|
|
81
|
+
readonly replyTo: z.ZodString;
|
|
82
|
+
readonly status: z.ZodEnum<{
|
|
83
|
+
failed: "failed";
|
|
84
|
+
pending: "pending";
|
|
85
|
+
sent: "sent";
|
|
86
|
+
sending: "sending";
|
|
87
|
+
}>;
|
|
88
|
+
readonly attempts: z.ZodNumber;
|
|
89
|
+
readonly nextAt: z.ZodNullable<z.ZodString>;
|
|
90
|
+
readonly sentAt: z.ZodNullable<z.ZodString>;
|
|
91
|
+
readonly claimedAt: z.ZodNullable<z.ZodString>;
|
|
92
|
+
};
|
|
93
|
+
readonly access: {
|
|
94
|
+
readonly read: "server";
|
|
95
|
+
readonly create: "server";
|
|
96
|
+
readonly update: "server";
|
|
97
|
+
readonly delete: "server";
|
|
98
|
+
};
|
|
99
|
+
readonly writes: "confirmed";
|
|
100
|
+
}>;
|
|
101
|
+
requestOperations: import("./table.ts").TableOf<"requestOperations", {
|
|
102
|
+
readonly columns: {
|
|
103
|
+
readonly result: z.ZodString;
|
|
104
|
+
};
|
|
105
|
+
readonly access: {
|
|
106
|
+
readonly read: "server";
|
|
107
|
+
readonly create: "server";
|
|
108
|
+
readonly update: "server";
|
|
109
|
+
readonly delete: "server";
|
|
110
|
+
};
|
|
111
|
+
readonly writes: "confirmed";
|
|
112
|
+
}>;
|
|
113
|
+
};
|
|
114
|
+
readonly services: {
|
|
115
|
+
readonly inquiries: {
|
|
116
|
+
readonly send: NoInfer<import("./services.ts").ServiceCall<"/requests/:source", {
|
|
117
|
+
fields: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>>;
|
|
118
|
+
}, z.ZodObject<{
|
|
119
|
+
id: z.ZodString;
|
|
120
|
+
}, z.core.$strip>, false>>;
|
|
121
|
+
};
|
|
122
|
+
};
|
|
123
|
+
readonly memory: ServiceMemory<{
|
|
124
|
+
readonly inquiries: {
|
|
125
|
+
readonly send: NoInfer<import("./services.ts").ServiceCall<"/requests/:source", {
|
|
126
|
+
fields: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>>;
|
|
127
|
+
}, z.ZodObject<{
|
|
128
|
+
id: z.ZodString;
|
|
129
|
+
}, z.core.$strip>, false>>;
|
|
130
|
+
};
|
|
131
|
+
}, unknown>;
|
|
132
|
+
readonly Store: typeof Inquiries;
|
|
133
|
+
};
|
|
134
|
+
export {};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { inquiryServices, inquiryValue, requestTables } from "./requests.js";
|
|
3
|
+
import { ServiceRefused, refused } from "./services.js";
|
|
4
|
+
import { defineStore } from "./store.js";
|
|
5
|
+
const Store = defineStore();
|
|
6
|
+
function worldOf(world) {
|
|
7
|
+
const found = Reflect.get(Object(world), 'inquiries');
|
|
8
|
+
return typeof found === 'object' && found !== null ? found : {};
|
|
9
|
+
}
|
|
10
|
+
const inquiryMemory = {
|
|
11
|
+
inquiries: {
|
|
12
|
+
send: (world, { source, fields }) => {
|
|
13
|
+
const current = worldOf(world);
|
|
14
|
+
if (current.sources !== undefined && !current.sources.includes(source)) {
|
|
15
|
+
throw refused(404, 'No such form');
|
|
16
|
+
}
|
|
17
|
+
const sent = current.sent ?? [];
|
|
18
|
+
const id = `${source}-${sent.length + 1}`;
|
|
19
|
+
Reflect.set(Object(world), 'inquiries', {
|
|
20
|
+
...current,
|
|
21
|
+
sent: [...sent, { source, id, fields }]
|
|
22
|
+
});
|
|
23
|
+
return { id };
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
function outcomeOf(error) {
|
|
28
|
+
if (!(error instanceof ServiceRefused))
|
|
29
|
+
return 'failed';
|
|
30
|
+
if (error.status === 400 || error.status === 404)
|
|
31
|
+
return 'forbidden';
|
|
32
|
+
return error.status === 409 ? 'conflict' : 'failed';
|
|
33
|
+
}
|
|
34
|
+
export class Inquiries extends Store({
|
|
35
|
+
state: {
|
|
36
|
+
sent: z
|
|
37
|
+
.object({ source: z.string(), id: z.string() })
|
|
38
|
+
.nullable()
|
|
39
|
+
.default(null),
|
|
40
|
+
isSending: z.boolean().default(false)
|
|
41
|
+
}
|
|
42
|
+
}) {
|
|
43
|
+
async send(source, fields) {
|
|
44
|
+
this.set({ isSending: true });
|
|
45
|
+
try {
|
|
46
|
+
const { id } = await this.services.inquiries.send({ source, fields });
|
|
47
|
+
this.set({ sent: { source, id } });
|
|
48
|
+
return 'saved';
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
return outcomeOf(error);
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
this.set({ isSending: false });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export const inquiries = {
|
|
59
|
+
name: 'inquiries',
|
|
60
|
+
tables: requestTables,
|
|
61
|
+
services: inquiryServices,
|
|
62
|
+
memory: inquiryMemory,
|
|
63
|
+
Store: Inquiries
|
|
64
|
+
};
|
package/dist/media.d.ts
CHANGED
|
@@ -40,7 +40,8 @@ export type ImageSources = {
|
|
|
40
40
|
width: number;
|
|
41
41
|
height: number;
|
|
42
42
|
};
|
|
43
|
-
export declare function imageSources(image: MediaImage, { aspect, transforms }: {
|
|
43
|
+
export declare function imageSources(image: MediaImage, { aspect, transforms, origin }: {
|
|
44
44
|
aspect?: number | undefined;
|
|
45
45
|
transforms: boolean;
|
|
46
|
+
origin?: string;
|
|
46
47
|
}): ImageSources;
|
package/dist/media.js
CHANGED
|
@@ -46,14 +46,14 @@ function transformed(key, width, aspect) {
|
|
|
46
46
|
];
|
|
47
47
|
return `/cdn-cgi/image/${options.join(',')}${mediaPath(key)}`;
|
|
48
48
|
}
|
|
49
|
-
export function imageSources(image, { aspect, transforms }) {
|
|
49
|
+
export function imageSources(image, { aspect, transforms, origin = '' }) {
|
|
50
50
|
const largest = largestWidth(image, aspect);
|
|
51
51
|
const height = aspect === undefined
|
|
52
52
|
? image.height
|
|
53
53
|
: Math.max(1, Math.round(largest / aspect));
|
|
54
54
|
if (!transforms) {
|
|
55
55
|
return {
|
|
56
|
-
src: mediaPath(image.key)
|
|
56
|
+
src: `${origin}${mediaPath(image.key)}`,
|
|
57
57
|
srcSet: undefined,
|
|
58
58
|
width: largest,
|
|
59
59
|
height
|
|
@@ -64,9 +64,9 @@ export function imageSources(image, { aspect, transforms }) {
|
|
|
64
64
|
largest
|
|
65
65
|
].filter((width, index, all) => all.indexOf(width) === index);
|
|
66
66
|
return {
|
|
67
|
-
src: transformed(image.key, Math.min(FALLBACK_WIDTH, largest), aspect)
|
|
67
|
+
src: `${origin}${transformed(image.key, Math.min(FALLBACK_WIDTH, largest), aspect)}`,
|
|
68
68
|
srcSet: widths
|
|
69
|
-
.map((width) => `${transformed(image.key, width, aspect)} ${width}w`)
|
|
69
|
+
.map((width) => `${origin}${transformed(image.key, width, aspect)} ${width}w`)
|
|
70
70
|
.join(', '),
|
|
71
71
|
width: largest,
|
|
72
72
|
height
|
package/dist/patternAccess.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ import { z } from 'zod';
|
|
|
2
2
|
export declare const TABLES_READ = "tables:read";
|
|
3
3
|
export declare const MEDIA_WRITE = "media:write";
|
|
4
4
|
export declare const TABLES_WRITE = "tables:write";
|
|
5
|
+
export declare const REQUESTS_READ = "requests:read";
|
|
6
|
+
export declare const REQUESTS_ACT = "requests:act";
|
|
5
7
|
export declare const ASSERTION_SECONDS = 60;
|
|
6
8
|
export declare const NEXT_PAGE_HEADER = "x-pattern-next";
|
|
7
9
|
export declare const assertionKey: z.ZodObject<{
|
|
@@ -16,17 +18,21 @@ declare const assertionScope: z.ZodEnum<{
|
|
|
16
18
|
"tables:read": "tables:read";
|
|
17
19
|
"media:write": "media:write";
|
|
18
20
|
"tables:write": "tables:write";
|
|
21
|
+
"requests:read": "requests:read";
|
|
22
|
+
"requests:act": "requests:act";
|
|
19
23
|
}>;
|
|
20
24
|
export type AssertionScope = z.output<typeof assertionScope>;
|
|
21
|
-
export declare function signAssertion(key: AssertionKey, { sub, aud, scope }: {
|
|
25
|
+
export declare function signAssertion(key: AssertionKey, { sub, aud, scope, bound }: {
|
|
22
26
|
sub: string;
|
|
23
27
|
aud: string;
|
|
24
28
|
scope: AssertionScope;
|
|
29
|
+
bound?: string;
|
|
25
30
|
}, now: number): Promise<string>;
|
|
26
|
-
export declare function verifiedAssertion(token: string, { hostname, keys, now, scope }: {
|
|
31
|
+
export declare function verifiedAssertion(token: string, { hostname, keys, now, scope, bound }: {
|
|
27
32
|
hostname: string;
|
|
28
33
|
keys: Readonly<Record<string, string>>;
|
|
29
34
|
now: number;
|
|
30
35
|
scope: AssertionScope;
|
|
36
|
+
bound?: string;
|
|
31
37
|
}): Promise<string | undefined>;
|
|
32
38
|
export {};
|
package/dist/patternAccess.js
CHANGED
|
@@ -2,6 +2,8 @@ import { z } from 'zod';
|
|
|
2
2
|
export const TABLES_READ = 'tables:read';
|
|
3
3
|
export const MEDIA_WRITE = 'media:write';
|
|
4
4
|
export const TABLES_WRITE = 'tables:write';
|
|
5
|
+
export const REQUESTS_READ = 'requests:read';
|
|
6
|
+
export const REQUESTS_ACT = 'requests:act';
|
|
5
7
|
export const ASSERTION_SECONDS = 60;
|
|
6
8
|
export const NEXT_PAGE_HEADER = 'x-pattern-next';
|
|
7
9
|
const CLOCK_SKEW_SECONDS = 30;
|
|
@@ -13,7 +15,13 @@ export const assertionKey = z.object({
|
|
|
13
15
|
x: z.string(),
|
|
14
16
|
d: z.string()
|
|
15
17
|
});
|
|
16
|
-
const assertionScope = z.enum([
|
|
18
|
+
const assertionScope = z.enum([
|
|
19
|
+
TABLES_READ,
|
|
20
|
+
TABLES_WRITE,
|
|
21
|
+
MEDIA_WRITE,
|
|
22
|
+
REQUESTS_READ,
|
|
23
|
+
REQUESTS_ACT
|
|
24
|
+
]);
|
|
17
25
|
const assertionHeader = z.object({
|
|
18
26
|
alg: z.literal('EdDSA'),
|
|
19
27
|
typ: z.literal('JWT'),
|
|
@@ -24,7 +32,8 @@ const assertionClaims = z.object({
|
|
|
24
32
|
aud: z.string(),
|
|
25
33
|
scope: assertionScope,
|
|
26
34
|
iat: z.int(),
|
|
27
|
-
exp: z.int()
|
|
35
|
+
exp: z.int(),
|
|
36
|
+
bnd: z.string().optional()
|
|
28
37
|
});
|
|
29
38
|
const encoder = new TextEncoder();
|
|
30
39
|
function base64url(bytes) {
|
|
@@ -56,14 +65,15 @@ function jsonOf(text) {
|
|
|
56
65
|
function encoded(value) {
|
|
57
66
|
return base64url(encoder.encode(JSON.stringify(value)));
|
|
58
67
|
}
|
|
59
|
-
export async function signAssertion(key, { sub, aud, scope }, now) {
|
|
68
|
+
export async function signAssertion(key, { sub, aud, scope, bound }, now) {
|
|
60
69
|
const privateKey = await crypto.subtle.importKey('jwk', { kty: key.kty, crv: key.crv, x: key.x, d: key.d }, ED25519, false, ['sign']);
|
|
61
70
|
const signed = `${encoded({ alg: 'EdDSA', typ: 'JWT', kid: key.kid })}.${encoded({
|
|
62
71
|
sub,
|
|
63
72
|
aud,
|
|
64
73
|
scope,
|
|
65
74
|
iat: now,
|
|
66
|
-
exp: now + ASSERTION_SECONDS
|
|
75
|
+
exp: now + ASSERTION_SECONDS,
|
|
76
|
+
...(bound === undefined ? {} : { bnd: bound })
|
|
67
77
|
})}`;
|
|
68
78
|
const signature = await crypto.subtle.sign(ED25519, privateKey, encoder.encode(signed));
|
|
69
79
|
return `${signed}.${base64url(new Uint8Array(signature))}`;
|
|
@@ -83,7 +93,7 @@ async function isSignedBy(publicKey, signed, signature) {
|
|
|
83
93
|
return false;
|
|
84
94
|
}
|
|
85
95
|
}
|
|
86
|
-
export async function verifiedAssertion(token, { hostname, keys, now, scope }) {
|
|
96
|
+
export async function verifiedAssertion(token, { hostname, keys, now, scope, bound }) {
|
|
87
97
|
const [header, claims, signature, ...rest] = token.split('.');
|
|
88
98
|
if (header === undefined ||
|
|
89
99
|
claims === undefined ||
|
|
@@ -107,6 +117,8 @@ export async function verifiedAssertion(token, { hostname, keys, now, scope }) {
|
|
|
107
117
|
const { sub, aud, iat, exp } = parsed.data;
|
|
108
118
|
if (parsed.data.scope !== scope)
|
|
109
119
|
return undefined;
|
|
120
|
+
if (bound !== undefined && parsed.data.bnd !== bound)
|
|
121
|
+
return undefined;
|
|
110
122
|
const isFresh = exp > iat &&
|
|
111
123
|
exp - iat <= ASSERTION_SECONDS &&
|
|
112
124
|
iat <= now + CLOCK_SKEW_SECONDS &&
|