@devdogsuga/devtools 0.1.26 → 0.1.28
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 +72 -11
- package/bin/devtools.mjs +4 -4
- package/dist/cli-CZKkNHK5.js +6619 -0
- package/dist/failure-log-BBPGejQv.js +256 -0
- package/dist/launch.js +267 -281
- package/dist/local-env-D8HC2JK_.js +351 -0
- package/dist/local-env-DrBCXqD8.js +2 -0
- package/dist/menu-CQBsQRgH.js +1684 -0
- package/dist/peer-redirect-hooks.js +15 -0
- package/dist/peers-RBplwovA.js +229 -0
- package/dist/root-C2OmZDwD.js +94 -0
- package/dist/stack-Bx-55oi_.js +2 -0
- package/dist/stack-BynCgRVS.js +181 -0
- package/dist/supabase-project-0knM0mqd.js +426 -0
- package/dist/telemetry-C4foD8MD.js +249 -0
- package/dist/telemetry.js +2 -148
- package/package.json +10 -29
- package/bin/devtools-ci-bare.mjs +0 -48
- package/bin/devtools-ci.mjs +0 -31
- package/dist/args.d.ts +0 -26
- package/dist/args.js +0 -78
- package/dist/bws/bw.d.ts +0 -18
- package/dist/bws/bw.js +0 -70
- package/dist/bws/client.d.ts +0 -43
- package/dist/bws/client.js +0 -363
- package/dist/bws/environments.d.ts +0 -96
- package/dist/bws/environments.js +0 -111
- package/dist/bws/pace.d.ts +0 -25
- package/dist/bws/pace.js +0 -43
- package/dist/bws/retry.d.ts +0 -12
- package/dist/bws/retry.js +0 -39
- package/dist/bws/store.d.ts +0 -1
- package/dist/bws/store.js +0 -32
- package/dist/bws/token.d.ts +0 -31
- package/dist/bws/token.js +0 -88
- package/dist/bws/vault.d.ts +0 -29
- package/dist/bws/vault.js +0 -200
- package/dist/catalog.d.ts +0 -57
- package/dist/catalog.js +0 -57
- package/dist/cf/build.d.ts +0 -13
- package/dist/cf/build.js +0 -65
- package/dist/cf/commands.d.ts +0 -1
- package/dist/cf/commands.js +0 -172
- package/dist/cf/local-env.d.ts +0 -58
- package/dist/cf/local-env.js +0 -177
- package/dist/ci.d.ts +0 -1
- package/dist/ci.js +0 -328
- package/dist/cli.d.ts +0 -11
- package/dist/cli.js +0 -1094
- package/dist/commands.d.ts +0 -261
- package/dist/commands.js +0 -1318
- package/dist/completions.d.ts +0 -4
- package/dist/completions.js +0 -110
- package/dist/cron/commands.d.ts +0 -47
- package/dist/cron/commands.js +0 -372
- package/dist/cron/discovery.d.ts +0 -55
- package/dist/cron/discovery.js +0 -85
- package/dist/cron/schema.d.ts +0 -45
- package/dist/cron/schema.js +0 -35
- package/dist/db/config-push.d.ts +0 -8
- package/dist/db/config-push.js +0 -11
- package/dist/db/connection.d.ts +0 -79
- package/dist/db/connection.js +0 -114
- package/dist/db/exec.d.ts +0 -1
- package/dist/db/exec.js +0 -11
- package/dist/db/generate-types.d.ts +0 -1
- package/dist/db/generate-types.js +0 -4
- package/dist/db/generated-env.d.ts +0 -69
- package/dist/db/generated-env.js +0 -113
- package/dist/db/introspect.d.ts +0 -1
- package/dist/db/introspect.js +0 -122
- package/dist/db/local-env.d.ts +0 -65
- package/dist/db/local-env.js +0 -23
- package/dist/db/new-migration.d.ts +0 -8
- package/dist/db/new-migration.js +0 -69
- package/dist/db/pooler.d.ts +0 -23
- package/dist/db/pooler.js +0 -55
- package/dist/db/run.d.ts +0 -68
- package/dist/db/run.js +0 -156
- package/dist/db/seed-buckets.d.ts +0 -2
- package/dist/db/seed-buckets.js +0 -4
- package/dist/db/seed-production.d.ts +0 -8
- package/dist/db/seed-production.js +0 -79
- package/dist/db/seed-roles.d.ts +0 -7
- package/dist/db/seed-roles.js +0 -33
- package/dist/db/session-refresh.d.ts +0 -43
- package/dist/db/session-refresh.js +0 -40
- package/dist/deploy/migrations.d.ts +0 -20
- package/dist/deploy/migrations.js +0 -68
- package/dist/deploy/orphans.d.ts +0 -20
- package/dist/deploy/orphans.js +0 -177
- package/dist/deploy/preflight.d.ts +0 -53
- package/dist/deploy/preflight.js +0 -189
- package/dist/deploy/report.d.ts +0 -26
- package/dist/deploy/report.js +0 -70
- package/dist/deploy/require-planner.d.ts +0 -2
- package/dist/deploy/require-planner.js +0 -60
- package/dist/deploy/require-token.d.ts +0 -15
- package/dist/deploy/require-token.js +0 -78
- package/dist/deploy/secrets-file.d.ts +0 -17
- package/dist/deploy/secrets-file.js +0 -166
- package/dist/deploy/write-env.d.ts +0 -40
- package/dist/deploy/write-env.js +0 -395
- package/dist/docs/index-pages.d.ts +0 -38
- package/dist/docs/index-pages.js +0 -186
- package/dist/emails/commands.d.ts +0 -15
- package/dist/emails/commands.js +0 -130
- package/dist/emails/email-types.d.ts +0 -13
- package/dist/emails/email-types.js +0 -1
- package/dist/emails/fixtures.d.ts +0 -20
- package/dist/emails/fixtures.js +0 -20
- package/dist/env/audit.d.ts +0 -206
- package/dist/env/audit.js +0 -436
- package/dist/env/cloudflare.d.ts +0 -21
- package/dist/env/cloudflare.js +0 -61
- package/dist/env/commands.d.ts +0 -99
- package/dist/env/commands.js +0 -613
- package/dist/env/discovery.d.ts +0 -15
- package/dist/env/discovery.js +0 -239
- package/dist/env/document.d.ts +0 -119
- package/dist/env/document.js +0 -323
- package/dist/env/example.d.ts +0 -68
- package/dist/env/example.js +0 -605
- package/dist/env/selection.d.ts +0 -105
- package/dist/env/selection.js +0 -228
- package/dist/env-entry.d.ts +0 -49
- package/dist/env-entry.js +0 -204
- package/dist/environment-doctor.d.ts +0 -45
- package/dist/environment-doctor.js +0 -429
- package/dist/environment.d.ts +0 -92
- package/dist/environment.js +0 -185
- package/dist/fingerprint.d.ts +0 -10
- package/dist/fingerprint.js +0 -18
- package/dist/gen/campus-map.d.ts +0 -1
- package/dist/gen/campus-map.js +0 -473
- package/dist/gen/commands.d.ts +0 -1
- package/dist/gen/commands.js +0 -25
- package/dist/gen/hypno.d.ts +0 -1
- package/dist/gen/hypno.js +0 -134
- package/dist/gh/client.d.ts +0 -80
- package/dist/gh/client.js +0 -259
- package/dist/gh/environments.d.ts +0 -83
- package/dist/gh/environments.js +0 -178
- package/dist/gh/rulesets/actors.d.ts +0 -14
- package/dist/gh/rulesets/actors.js +0 -69
- package/dist/gh/rulesets/api.d.ts +0 -15
- package/dist/gh/rulesets/api.js +0 -104
- package/dist/gh/rulesets/commands.d.ts +0 -3
- package/dist/gh/rulesets/commands.js +0 -203
- package/dist/gh/rulesets/desired.d.ts +0 -112
- package/dist/gh/rulesets/desired.js +0 -271
- package/dist/gh/rulesets/diff.d.ts +0 -65
- package/dist/gh/rulesets/diff.js +0 -170
- package/dist/gh/rulesets/types.d.ts +0 -76
- package/dist/gh/rulesets/types.js +0 -13
- package/dist/gh/settings/api.d.ts +0 -40
- package/dist/gh/settings/api.js +0 -180
- package/dist/gh/settings/commands.d.ts +0 -1
- package/dist/gh/settings/commands.js +0 -254
- package/dist/gh/settings/desired.d.ts +0 -53
- package/dist/gh/settings/desired.js +0 -43
- package/dist/gh/settings/diff.d.ts +0 -57
- package/dist/gh/settings/diff.js +0 -161
- package/dist/gh/settings/types.d.ts +0 -92
- package/dist/gh/settings/types.js +0 -11
- package/dist/gh/settings/workflows.d.ts +0 -41
- package/dist/gh/settings/workflows.js +0 -128
- package/dist/grantRoot.d.ts +0 -42
- package/dist/grantRoot.js +0 -89
- package/dist/help.d.ts +0 -18
- package/dist/help.js +0 -169
- package/dist/images/commands.d.ts +0 -32
- package/dist/images/commands.js +0 -231
- package/dist/images/events.d.ts +0 -47
- package/dist/images/events.js +0 -116
- package/dist/images/graphics.d.ts +0 -70
- package/dist/images/graphics.js +0 -234
- package/dist/images/open-graph-types.d.ts +0 -75
- package/dist/images/open-graph-types.js +0 -1
- package/dist/images/prompts.d.ts +0 -34
- package/dist/images/prompts.js +0 -100
- package/dist/images/render.d.ts +0 -21
- package/dist/images/render.js +0 -61
- package/dist/images/select.d.ts +0 -64
- package/dist/images/select.js +0 -96
- package/dist/instance.d.ts +0 -69
- package/dist/instance.js +0 -102
- package/dist/invocation.d.ts +0 -49
- package/dist/invocation.js +0 -78
- package/dist/launch-ci.d.ts +0 -1
- package/dist/launch-ci.js +0 -90
- package/dist/launch.d.ts +0 -18
- package/dist/menu.d.ts +0 -43
- package/dist/menu.js +0 -300
- package/dist/moderation.d.ts +0 -70
- package/dist/moderation.js +0 -128
- package/dist/oauth/browser.d.ts +0 -1
- package/dist/oauth/browser.js +0 -13
- package/dist/oauth/config.d.ts +0 -24
- package/dist/oauth/config.js +0 -24
- package/dist/oauth/connect-url.d.ts +0 -17
- package/dist/oauth/connect-url.js +0 -23
- package/dist/oauth/db.d.ts +0 -37
- package/dist/oauth/db.js +0 -108
- package/dist/oauth/device.d.ts +0 -93
- package/dist/oauth/device.js +0 -198
- package/dist/oauth/discovery.d.ts +0 -28
- package/dist/oauth/discovery.js +0 -54
- package/dist/oauth/env-file.d.ts +0 -6
- package/dist/oauth/env-file.js +0 -35
- package/dist/oauth/exchange.d.ts +0 -43
- package/dist/oauth/exchange.js +0 -93
- package/dist/oauth/label.d.ts +0 -1
- package/dist/oauth/label.js +0 -25
- package/dist/oauth/loopback.d.ts +0 -52
- package/dist/oauth/loopback.js +0 -168
- package/dist/oauth/pkce.d.ts +0 -6
- package/dist/oauth/pkce.js +0 -34
- package/dist/oauth/target.d.ts +0 -54
- package/dist/oauth/target.js +0 -102
- package/dist/oauth/transport.d.ts +0 -41
- package/dist/oauth/transport.js +0 -32
- package/dist/oauth/wizard.d.ts +0 -40
- package/dist/oauth/wizard.js +0 -578
- package/dist/persona.d.ts +0 -18
- package/dist/persona.js +0 -221
- package/dist/pick.d.ts +0 -6
- package/dist/pick.js +0 -64
- package/dist/pipes.d.ts +0 -1
- package/dist/pipes.js +0 -25
- package/dist/planner/checks.d.ts +0 -26
- package/dist/planner/checks.js +0 -104
- package/dist/planner/commands.d.ts +0 -10
- package/dist/planner/commands.js +0 -367
- package/dist/planner/db.d.ts +0 -15
- package/dist/planner/db.js +0 -33
- package/dist/planner/role.d.ts +0 -33
- package/dist/planner/role.js +0 -68
- package/dist/repo/peer-redirect-hooks.d.ts +0 -16
- package/dist/repo/peer-redirect-hooks.js +0 -10
- package/dist/repo/peer-redirect.d.ts +0 -4
- package/dist/repo/peer-redirect.js +0 -26
- package/dist/repo/peers.d.ts +0 -39
- package/dist/repo/peers.js +0 -139
- package/dist/repo/resolve.d.ts +0 -41
- package/dist/repo/resolve.js +0 -152
- package/dist/repo/root.d.ts +0 -23
- package/dist/repo/root.js +0 -91
- package/dist/repo/source.d.ts +0 -14
- package/dist/repo/source.js +0 -54
- package/dist/repo/supabase-project.d.ts +0 -60
- package/dist/repo/supabase-project.js +0 -124
- package/dist/repo/tsx-loader.d.ts +0 -6
- package/dist/repo/tsx-loader.js +0 -41
- package/dist/run/pick.d.ts +0 -90
- package/dist/run/pick.js +0 -664
- package/dist/setup.d.ts +0 -1
- package/dist/setup.js +0 -280
- package/dist/stack.d.ts +0 -48
- package/dist/stack.js +0 -403
- package/dist/telemetry.d.ts +0 -59
- package/dist/tier.d.ts +0 -40
- package/dist/tier.js +0 -96
- package/dist/ui.d.ts +0 -40
- package/dist/ui.js +0 -71
- package/dist/version.d.ts +0 -7
- package/dist/version.js +0 -17
- package/dist/workers.d.ts +0 -5
- package/dist/workers.js +0 -33
- package/dist/workflows/commands.d.ts +0 -54
- package/dist/workflows/commands.js +0 -763
package/dist/images/graphics.js
DELETED
|
@@ -1,234 +0,0 @@
|
|
|
1
|
-
import { loadOpenGraph } from "../repo/source.js";
|
|
2
|
-
import { UsageError } from "../ui.js";
|
|
3
|
-
const BRAND = "apps/platform/public/brand";
|
|
4
|
-
/** The two renditions anything may need, because anything may reach the GDG platform. */
|
|
5
|
-
const GDG = ["gdgc-wide", "gdgc-square"];
|
|
6
|
-
const EVENT_FORMATS = ["og", ...GDG, "involvement-network"];
|
|
7
|
-
const EMAIL = ["email-1x", "email-2x", "email-3x"];
|
|
8
|
-
/* ------------------------------------------------------------------- brand */
|
|
9
|
-
/**
|
|
10
|
-
* The club lockup, on each of the two grounds it is ever set on.
|
|
11
|
-
*
|
|
12
|
-
* One subject, two grounds, and the rendition decides which template draws it:
|
|
13
|
-
* the email densities want the signature's tight horizontal strip, everything
|
|
14
|
-
* else wants the banner. That is why "the email signature" is not a graphic of
|
|
15
|
-
* its own — it is this lockup at `email-2x`, and its GDG export is the same
|
|
16
|
-
* lockup at `gdgc-square` rather than a second drawing that has to be kept in
|
|
17
|
-
* step with the first.
|
|
18
|
-
*/
|
|
19
|
-
function brandGraphics(og) {
|
|
20
|
-
return ["dark", "light"].map((ground) => ({
|
|
21
|
-
name: ground === "dark" ? "brand/club" : "brand/club-light",
|
|
22
|
-
group: "brand",
|
|
23
|
-
stem: ground === "dark" ? "club" : "club-light",
|
|
24
|
-
formats: [
|
|
25
|
-
"og",
|
|
26
|
-
...GDG,
|
|
27
|
-
...(ground === "dark" ? ["savvycal"] : []),
|
|
28
|
-
...EMAIL,
|
|
29
|
-
],
|
|
30
|
-
why: `The DevDogs lockup with the GDG chapter cobrand, on a ${ground} ground.`,
|
|
31
|
-
render: (format) => format.family === "email"
|
|
32
|
-
? og.EmailSignature({ width: format.width, ground })
|
|
33
|
-
: og.Banner({ width: format.width, height: format.height, ground }),
|
|
34
|
-
destination: (format) => ({
|
|
35
|
-
dir: format.family === "email" ? `${BRAND}/email` : `${BRAND}/banners`,
|
|
36
|
-
file: format.family === "email"
|
|
37
|
-
? `signature-${ground}${format.scale > 1 ? `@${format.scale}x` : ""}.png`
|
|
38
|
-
: `club-${ground === "dark" ? "" : "light-"}${format.name}.png`,
|
|
39
|
-
}),
|
|
40
|
-
}));
|
|
41
|
-
}
|
|
42
|
-
/* -------------------------------------------------------------------- pages */
|
|
43
|
-
/**
|
|
44
|
-
* One graphic per public page, keyed by the route it belongs to.
|
|
45
|
-
*
|
|
46
|
-
* The platform renders these live from `opengraph-image.tsx`, so the files
|
|
47
|
-
* written here are for looking at — an Open Graph card is otherwise only
|
|
48
|
-
* visible by deploying and pasting a link somewhere, which is a slow way to
|
|
49
|
-
* find out that a title wrapped badly.
|
|
50
|
-
*/
|
|
51
|
-
function pageGraphics(og) {
|
|
52
|
-
return Object.entries(og.PAGE_CARDS).map(([route, copy]) => {
|
|
53
|
-
const stem = route === "/" ? "home" : route.slice(1).replace(/\//g, "-");
|
|
54
|
-
return {
|
|
55
|
-
name: `page/${stem}`,
|
|
56
|
-
group: "page",
|
|
57
|
-
stem,
|
|
58
|
-
formats: ["og", ...GDG],
|
|
59
|
-
why: `The card ${route} unfurls as.`,
|
|
60
|
-
render: (format) => og.PageCard({
|
|
61
|
-
width: format.width,
|
|
62
|
-
height: format.height,
|
|
63
|
-
cobrand: true,
|
|
64
|
-
...copy,
|
|
65
|
-
}),
|
|
66
|
-
// The format is in the filename, not just the directory: `og` and
|
|
67
|
-
// `gdgc-square` are the same page at two sizes, and without it the second
|
|
68
|
-
// silently overwrites the first.
|
|
69
|
-
destination: (format) => ({
|
|
70
|
-
dir: `${BRAND}/og`,
|
|
71
|
-
file: `${stem}-${format.name}.png`,
|
|
72
|
-
}),
|
|
73
|
-
};
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
/* --------------------------------------------------------------------- apps */
|
|
77
|
-
/** Which app's icons go where, and which of them Next serves by convention. */
|
|
78
|
-
const APP_HOME = {
|
|
79
|
-
platform: { icons: `${BRAND}/icons`, app: "apps/platform/src/app" },
|
|
80
|
-
dogdays: {
|
|
81
|
-
icons: "apps/schedule-builder/public/icons",
|
|
82
|
-
app: "apps/schedule-builder/src/app",
|
|
83
|
-
},
|
|
84
|
-
// Flutter, and nothing deployed yet, so its icons wait in the club's own
|
|
85
|
-
// brand folder rather than in an app directory that does not exist.
|
|
86
|
-
dogpack: { icons: `${BRAND}/icons` },
|
|
87
|
-
};
|
|
88
|
-
function appGraphics(og) {
|
|
89
|
-
return Object.keys(og.APPS).map((app) => {
|
|
90
|
-
// Non-null by construction: every real AppKey (`Object.keys(og.APPS)`)
|
|
91
|
-
// has an APP_HOME entry. `AppKey` itself is widened to `string` here
|
|
92
|
-
// (see `open-graph-types.ts`'s header on why), so TS can no longer see
|
|
93
|
-
// that closed correspondence the way it could against the real literal
|
|
94
|
-
// union.
|
|
95
|
-
const home = APP_HOME[app];
|
|
96
|
-
const sizes = [16, 32, 48, 64, 96, 128, 180, 192, 256, 384, 512, 1024];
|
|
97
|
-
// Non-null by construction, same reasoning as `home` above: every real
|
|
98
|
-
// AppKey this loop iterates came from `Object.keys(og.APPS)` itself.
|
|
99
|
-
const brand = og.APPS[app];
|
|
100
|
-
return {
|
|
101
|
-
name: `app/${app}`,
|
|
102
|
-
group: "app",
|
|
103
|
-
stem: app,
|
|
104
|
-
formats: [
|
|
105
|
-
...sizes.map((size) => `icon-${size}`),
|
|
106
|
-
...(home.app ? ["icon-favicon", "icon-apple"] : []),
|
|
107
|
-
"og",
|
|
108
|
-
...GDG,
|
|
109
|
-
],
|
|
110
|
-
why: `${brand.name} — its mark as an icon, and its card as a link.`,
|
|
111
|
-
render: (format) => format.family === "icon"
|
|
112
|
-
? og.AppIcon({
|
|
113
|
-
app,
|
|
114
|
-
size: format.width,
|
|
115
|
-
background: og.OPAQUE_FORMATS.has(format.name)
|
|
116
|
-
? og.THEME.background
|
|
117
|
-
: undefined,
|
|
118
|
-
})
|
|
119
|
-
: og.PageCard({
|
|
120
|
-
width: format.width,
|
|
121
|
-
height: format.height,
|
|
122
|
-
title: brand.name,
|
|
123
|
-
description: brand.blurb,
|
|
124
|
-
eyebrow: brand.tagline,
|
|
125
|
-
accent: brand.ground,
|
|
126
|
-
footer: brand.host,
|
|
127
|
-
cobrand: true,
|
|
128
|
-
}),
|
|
129
|
-
destination: (format) => {
|
|
130
|
-
// The two Next reads by name, which have to land in `src/app` under
|
|
131
|
-
// exactly those filenames or the framework does not see them.
|
|
132
|
-
if (home.app && format.name === "icon-favicon") {
|
|
133
|
-
return { dir: home.app, file: "icon.png" };
|
|
134
|
-
}
|
|
135
|
-
if (home.app && format.name === "icon-apple") {
|
|
136
|
-
return { dir: home.app, file: "apple-icon.png" };
|
|
137
|
-
}
|
|
138
|
-
// The platform serves its own link cards per request, so only the apps
|
|
139
|
-
// WITHOUT a live renderer get a static `opengraph-image.png`.
|
|
140
|
-
if (home.app && app !== "platform" && format.name === "og") {
|
|
141
|
-
return { dir: home.app, file: "opengraph-image.png" };
|
|
142
|
-
}
|
|
143
|
-
return format.family === "icon"
|
|
144
|
-
? {
|
|
145
|
-
dir: home.icons,
|
|
146
|
-
file: `${app}-${format.name.replace("icon-", "")}.png`,
|
|
147
|
-
}
|
|
148
|
-
: { dir: `${BRAND}/og`, file: `app-${app}-${format.name}.png` };
|
|
149
|
-
},
|
|
150
|
-
};
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
/**
|
|
154
|
-
* One graphic per meeting.
|
|
155
|
-
*
|
|
156
|
-
* These are the reason the two axes were split at all: "this meeting" x "the
|
|
157
|
-
* GDG platform's banner" is a pairing nobody could have enumerated in advance.
|
|
158
|
-
*
|
|
159
|
-
* Their default directory is the one that is NOT in the repo. Every other
|
|
160
|
-
* graphic is a committed asset the club re-renders; an event poster is an
|
|
161
|
-
* export headed somewhere else, made once for one night. Writing those into
|
|
162
|
-
* `public/brand` would fill a tracked directory with a growing pile of
|
|
163
|
-
* one-offs, so they go to {@link EVENT_SCRATCH_DIR}, which `.gitignore` covers.
|
|
164
|
-
*/
|
|
165
|
-
export const EVENT_SCRATCH_DIR = ".images/events";
|
|
166
|
-
export function eventGraphics(og, events) {
|
|
167
|
-
return events.flatMap((event) => {
|
|
168
|
-
const meeting = {
|
|
169
|
-
name: `event/${event.slug}/meeting`,
|
|
170
|
-
group: "event",
|
|
171
|
-
stem: event.slug,
|
|
172
|
-
formats: EVENT_FORMATS,
|
|
173
|
-
why: `The ${event.slug} meeting — ${event.hint}.`,
|
|
174
|
-
render: (format) => og.EventCard({
|
|
175
|
-
width: format.width,
|
|
176
|
-
height: format.height,
|
|
177
|
-
cobrand: true,
|
|
178
|
-
...event.detail,
|
|
179
|
-
}),
|
|
180
|
-
destination: (format) => ({
|
|
181
|
-
dir: `${EVENT_SCRATCH_DIR}/${event.slug}`,
|
|
182
|
-
file: `meeting-${format.name}.png`,
|
|
183
|
-
}),
|
|
184
|
-
};
|
|
185
|
-
if ((event.items?.length ?? 0) < 2)
|
|
186
|
-
return [meeting];
|
|
187
|
-
const occurrences = new Map();
|
|
188
|
-
const items = event.items.map((item) => {
|
|
189
|
-
const occurrence = (occurrences.get(item.stem) ?? 0) + 1;
|
|
190
|
-
occurrences.set(item.stem, occurrence);
|
|
191
|
-
const path = occurrence === 1 ? item.stem : `${item.stem}-${occurrence}`;
|
|
192
|
-
return {
|
|
193
|
-
name: `event/${event.slug}/${path}`,
|
|
194
|
-
group: "event",
|
|
195
|
-
// `--out` is deliberately flat, so its private stem still carries
|
|
196
|
-
// the meeting even though default output can use nested folders.
|
|
197
|
-
stem: `${event.slug}-${path}`,
|
|
198
|
-
formats: EVENT_FORMATS,
|
|
199
|
-
why: `${item.detail.badge?.label ?? "Agenda item"}: ${item.detail.title} — ${event.hint}.`,
|
|
200
|
-
render: (format) => og.EventCard({
|
|
201
|
-
width: format.width,
|
|
202
|
-
height: format.height,
|
|
203
|
-
cobrand: true,
|
|
204
|
-
...item.detail,
|
|
205
|
-
}),
|
|
206
|
-
destination: (format) => ({
|
|
207
|
-
dir: `${EVENT_SCRATCH_DIR}/${event.slug}`,
|
|
208
|
-
file: `${path}-${format.name}.png`,
|
|
209
|
-
}),
|
|
210
|
-
};
|
|
211
|
-
});
|
|
212
|
-
return [meeting, ...items];
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
/* --------------------------------------------------------------------- all */
|
|
216
|
-
/** Everything that does not need a database. */
|
|
217
|
-
export async function staticGraphics() {
|
|
218
|
-
const og = await loadOpenGraph();
|
|
219
|
-
return [...brandGraphics(og), ...pageGraphics(og), ...appGraphics(og)];
|
|
220
|
-
}
|
|
221
|
-
/**
|
|
222
|
-
* Every leaf is unique, so a flat `--out` directory cannot silently overwrite
|
|
223
|
-
* one graphic's file with another's.
|
|
224
|
-
*/
|
|
225
|
-
export function assertUniqueStems(graphics) {
|
|
226
|
-
const seen = new Map();
|
|
227
|
-
for (const graphic of graphics) {
|
|
228
|
-
const clash = seen.get(graphic.stem);
|
|
229
|
-
if (clash) {
|
|
230
|
-
throw new UsageError(`${graphic.name} and ${clash} share the leaf "${graphic.stem}", so a flat --out would overwrite one with the other.`);
|
|
231
|
-
}
|
|
232
|
-
seen.set(graphic.stem, graphic.name);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Type-only surface for `@devdogsuga/open-graph` — a hand-maintained mirror
|
|
3
|
-
* of what `images/*.ts` actually uses, not a full re-export.
|
|
4
|
-
*
|
|
5
|
-
* `@devdogsuga/open-graph` is `"private": true` and never published — it
|
|
6
|
-
* stays in DevDogsUGA (see the ledger) and devtools resolves it FROM the
|
|
7
|
-
* repo at runtime (`repo/source.ts`'s `loadOpenGraph()`), never as a
|
|
8
|
-
* Backstage dependency. Backstage has no copy of the package to pull real
|
|
9
|
-
* types from, so these are duplicated for editor/typecheck convenience only
|
|
10
|
-
* and MUST be kept in sync by hand if the real shape changes. Runtime
|
|
11
|
-
* values always come from `loadOpenGraph()`, never from here.
|
|
12
|
-
*
|
|
13
|
-
* `EventDetail` and the `THEME` shape are NOT duplicated here: both are
|
|
14
|
-
* re-exported by open-graph from `@devdogsuga/brand`, which — unlike
|
|
15
|
-
* open-graph — IS a real Backstage package (published; see the ledger), so
|
|
16
|
-
* those two types are imported for real below.
|
|
17
|
-
*/
|
|
18
|
-
import type { ReactElement } from "react";
|
|
19
|
-
import type { EventDetail } from "@devdogsuga/brand/event";
|
|
20
|
-
import type { LoadedFont } from "@devdogsuga/brand";
|
|
21
|
-
export type { EventDetail, LoadedFont };
|
|
22
|
-
export type FormatFamily = "card" | "email" | "icon";
|
|
23
|
-
export interface Format {
|
|
24
|
-
name: string;
|
|
25
|
-
width: number;
|
|
26
|
-
height: number;
|
|
27
|
-
scale: number;
|
|
28
|
-
family: FormatFamily;
|
|
29
|
-
why: string;
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* `@devdogsuga/open-graph`'s `AppKey` is a string-literal union
|
|
33
|
-
* (`"platform" | "dogdays" | "dogpack"` as of this writing). Widened to
|
|
34
|
-
* `string` here rather than duplicating the literal list, which would
|
|
35
|
-
* silently drift the moment a fourth app gets an icon — `APPS`'s real keys
|
|
36
|
-
* are what devtools actually iterates over at runtime.
|
|
37
|
-
*/
|
|
38
|
-
export type AppKey = string;
|
|
39
|
-
export interface AppBrand {
|
|
40
|
-
name: string;
|
|
41
|
-
tagline: string;
|
|
42
|
-
blurb: string;
|
|
43
|
-
host: string;
|
|
44
|
-
ground: string;
|
|
45
|
-
mark: string;
|
|
46
|
-
}
|
|
47
|
-
export interface PageCardCopy {
|
|
48
|
-
title: string;
|
|
49
|
-
description: string;
|
|
50
|
-
eyebrow?: string;
|
|
51
|
-
accent?: string;
|
|
52
|
-
footer?: string;
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* The five JSX template components. Typed loosely (`(...args) =>
|
|
56
|
-
* ReactElement`, props as `any`) rather than duplicating each one's exact
|
|
57
|
-
* props interface — those are the deepest, most likely to drift part of
|
|
58
|
-
* open-graph's surface, and devtools only ever calls them with object
|
|
59
|
-
* literals built right next to the call site, where a real typo would show
|
|
60
|
-
* up as a runtime prop-shape mismatch the same way it would with `any`.
|
|
61
|
-
*/
|
|
62
|
-
export type TemplateComponent = (props: any) => ReactElement;
|
|
63
|
-
export interface OpenGraphModule {
|
|
64
|
-
FORMATS: Record<string, Format>;
|
|
65
|
-
OPAQUE_FORMATS: ReadonlySet<string>;
|
|
66
|
-
loadFonts: () => LoadedFont[];
|
|
67
|
-
APPS: Record<AppKey, AppBrand>;
|
|
68
|
-
AppIcon: TemplateComponent;
|
|
69
|
-
Banner: TemplateComponent;
|
|
70
|
-
EmailSignature: TemplateComponent;
|
|
71
|
-
EventCard: TemplateComponent;
|
|
72
|
-
PageCard: TemplateComponent;
|
|
73
|
-
PAGE_CARDS: Record<string, PageCardCopy>;
|
|
74
|
-
THEME: Record<string, string>;
|
|
75
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/images/prompts.d.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import type { Graphic } from "./graphics.js";
|
|
2
|
-
/**
|
|
3
|
-
* What the command asks for when the command line did not say.
|
|
4
|
-
*
|
|
5
|
-
* Three questions, and each one is skipped entirely when a flag answered it, so
|
|
6
|
-
* a scripted invocation never blocks. That is the whole contract: this command
|
|
7
|
-
* has to be usable both as "make me the September 8 posters" typed by an
|
|
8
|
-
* officer who does not remember the format names, and as a line in a build.
|
|
9
|
-
*
|
|
10
|
-
* {@link requireInteractive} is what keeps the second half true. Without it a
|
|
11
|
-
* missing flag in CI would hang on a prompt nobody can answer until the job
|
|
12
|
-
* times out, which is the least debuggable failure available.
|
|
13
|
-
*/
|
|
14
|
-
/** Refuses, naming the flag, rather than prompting where nobody can answer. */
|
|
15
|
-
export declare function requireInteractive(what: string, flag: string): void;
|
|
16
|
-
/**
|
|
17
|
-
* Which graphics.
|
|
18
|
-
*
|
|
19
|
-
* Autocomplete rather than a plain list: there are dozens once a semester of
|
|
20
|
-
* meetings is loaded, and the useful interaction is typing "sep" or "dogdays"
|
|
21
|
-
* rather than scrolling. Grouped by prefix in the label so the list reads as
|
|
22
|
-
* the `group/name` vocabulary the flags use.
|
|
23
|
-
*/
|
|
24
|
-
export declare function pickGraphics(graphics: readonly Graphic[]): Promise<Graphic[]>;
|
|
25
|
-
/**
|
|
26
|
-
* Which formats.
|
|
27
|
-
*
|
|
28
|
-
* Offered from the union of what the chosen graphics support, so the list never
|
|
29
|
-
* contains a size that would be skipped — asking somebody to pick `savvycal`
|
|
30
|
-
* for an event and then quietly not rendering it is worse than not offering it.
|
|
31
|
-
*/
|
|
32
|
-
export declare function pickFormats(graphics: readonly Graphic[]): Promise<string[]>;
|
|
33
|
-
/** `undefined` means "each graphic's own default directory". */
|
|
34
|
-
export declare function pickOutput(graphics: readonly Graphic[]): Promise<string | undefined>;
|
package/dist/images/prompts.js
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import { autocompleteMultiselect, isTTY, multiselect, path, select, } from "@clack/prompts";
|
|
2
|
-
import { loadOpenGraph } from "../repo/source.js";
|
|
3
|
-
import { unwrap, UsageError } from "../ui.js";
|
|
4
|
-
import { formatsFor } from "./select.js";
|
|
5
|
-
/**
|
|
6
|
-
* What the command asks for when the command line did not say.
|
|
7
|
-
*
|
|
8
|
-
* Three questions, and each one is skipped entirely when a flag answered it, so
|
|
9
|
-
* a scripted invocation never blocks. That is the whole contract: this command
|
|
10
|
-
* has to be usable both as "make me the September 8 posters" typed by an
|
|
11
|
-
* officer who does not remember the format names, and as a line in a build.
|
|
12
|
-
*
|
|
13
|
-
* {@link requireInteractive} is what keeps the second half true. Without it a
|
|
14
|
-
* missing flag in CI would hang on a prompt nobody can answer until the job
|
|
15
|
-
* times out, which is the least debuggable failure available.
|
|
16
|
-
*/
|
|
17
|
-
/** Refuses, naming the flag, rather than prompting where nobody can answer. */
|
|
18
|
-
export function requireInteractive(what, flag) {
|
|
19
|
-
if (isTTY(process.stdout))
|
|
20
|
-
return;
|
|
21
|
-
throw new UsageError(`No terminal to ask which ${what} to use. Pass ${flag} — this is not an interactive shell.`);
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Which graphics.
|
|
25
|
-
*
|
|
26
|
-
* Autocomplete rather than a plain list: there are dozens once a semester of
|
|
27
|
-
* meetings is loaded, and the useful interaction is typing "sep" or "dogdays"
|
|
28
|
-
* rather than scrolling. Grouped by prefix in the label so the list reads as
|
|
29
|
-
* the `group/name` vocabulary the flags use.
|
|
30
|
-
*/
|
|
31
|
-
export async function pickGraphics(graphics) {
|
|
32
|
-
requireInteractive("graphics", "a graphic name, or `*` for all of them");
|
|
33
|
-
const chosen = unwrap(await autocompleteMultiselect({
|
|
34
|
-
message: "Which images?",
|
|
35
|
-
options: graphics.map((graphic) => ({
|
|
36
|
-
value: graphic.name,
|
|
37
|
-
label: graphic.name,
|
|
38
|
-
hint: graphic.why,
|
|
39
|
-
})),
|
|
40
|
-
}));
|
|
41
|
-
return graphics.filter((graphic) => chosen.includes(graphic.name));
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Which formats.
|
|
45
|
-
*
|
|
46
|
-
* Offered from the union of what the chosen graphics support, so the list never
|
|
47
|
-
* contains a size that would be skipped — asking somebody to pick `savvycal`
|
|
48
|
-
* for an event and then quietly not rendering it is worse than not offering it.
|
|
49
|
-
*/
|
|
50
|
-
export async function pickFormats(graphics) {
|
|
51
|
-
requireInteractive("formats", "--format or --all-formats");
|
|
52
|
-
const available = await formatsFor(graphics);
|
|
53
|
-
return unwrap(await multiselect({
|
|
54
|
-
message: "Which formats?",
|
|
55
|
-
options: available.map((format) => ({
|
|
56
|
-
value: format.name,
|
|
57
|
-
label: format.name,
|
|
58
|
-
hint: format.why,
|
|
59
|
-
})),
|
|
60
|
-
// The link card is what most of these are for, and it is supported by
|
|
61
|
-
// every graphic that is not an icon.
|
|
62
|
-
initialValues: available.some((format) => format.name === "og")
|
|
63
|
-
? ["og"]
|
|
64
|
-
: [],
|
|
65
|
-
}));
|
|
66
|
-
}
|
|
67
|
-
/** `undefined` means "each graphic's own default directory". */
|
|
68
|
-
export async function pickOutput(graphics) {
|
|
69
|
-
requireInteractive("output directory", "--out or --default-out");
|
|
70
|
-
const { FORMATS } = await loadOpenGraph();
|
|
71
|
-
// Shown so the choice is concrete. One directory names itself; several are
|
|
72
|
-
// summarised, because listing eight paths in a prompt hint is unreadable.
|
|
73
|
-
const defaults = [
|
|
74
|
-
...new Set(graphics.flatMap((graphic) => graphic.formats
|
|
75
|
-
.map((name) => FORMATS[name])
|
|
76
|
-
.filter((format) => format !== undefined)
|
|
77
|
-
.map((format) => graphic.destination(format).dir))),
|
|
78
|
-
];
|
|
79
|
-
const choice = unwrap(await select({
|
|
80
|
-
message: "Where should these go?",
|
|
81
|
-
options: [
|
|
82
|
-
{
|
|
83
|
-
value: "default",
|
|
84
|
-
label: defaults.length === 1 ? defaults[0] : "Their default directories",
|
|
85
|
-
hint: defaults.length === 1
|
|
86
|
-
? "where this one belongs"
|
|
87
|
-
: `${defaults.length} directories, one per graphic`,
|
|
88
|
-
},
|
|
89
|
-
{ value: "other", label: "Other…", hint: "a directory of your own" },
|
|
90
|
-
],
|
|
91
|
-
}));
|
|
92
|
-
if (choice === "default")
|
|
93
|
-
return undefined;
|
|
94
|
-
return unwrap(await path({
|
|
95
|
-
message: "Which directory?",
|
|
96
|
-
// Completes against the filesystem, and only offers directories — the
|
|
97
|
-
// answer is a folder, and offering files would invite naming one.
|
|
98
|
-
directory: true,
|
|
99
|
-
}));
|
|
100
|
-
}
|
package/dist/images/render.d.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import type { ReactElement } from "react";
|
|
2
|
-
export interface RenderOptions {
|
|
3
|
-
/** The layout's own width. Every size in the template is against this. */
|
|
4
|
-
width: number;
|
|
5
|
-
height: number;
|
|
6
|
-
/**
|
|
7
|
-
* Output pixels per layout pixel. The layout is computed once at `width` and
|
|
8
|
-
* the vector result is rasterised larger, so 3x is genuinely three times the
|
|
9
|
-
* detail rather than an upscaled 1x — and the type stays on the same metrics
|
|
10
|
-
* at every scale, which is what makes @1x/@2x/@3x a set rather than three
|
|
11
|
-
* slightly different pictures.
|
|
12
|
-
*/
|
|
13
|
-
scale?: number;
|
|
14
|
-
}
|
|
15
|
-
export interface Rendered {
|
|
16
|
-
png: Buffer;
|
|
17
|
-
svg: string;
|
|
18
|
-
width: number;
|
|
19
|
-
height: number;
|
|
20
|
-
}
|
|
21
|
-
export declare function render(element: ReactElement, { width, height, scale }: RenderOptions): Promise<Rendered>;
|
package/dist/images/render.js
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Turning a template into a PNG on disk.
|
|
3
|
-
*
|
|
4
|
-
* Two libraries, and the choice of each is load-bearing:
|
|
5
|
-
*
|
|
6
|
-
* * **Satori** lays the template out and emits SVG. It is the same engine
|
|
7
|
-
* `next/og` runs inside the platform, so a card exported here and a card
|
|
8
|
-
* unfurled from a URL are the same picture rather than two implementations
|
|
9
|
-
* of one design.
|
|
10
|
-
* * **resvg** rasterises it, for the same reason. The obvious alternative was
|
|
11
|
-
* sharp, which this package already depends on — but sharp rasterises
|
|
12
|
-
* through librsvg, and librsvg does not read `rgba()` in a gradient stop,
|
|
13
|
-
* which is exactly the notation Satori emits. The background washes came
|
|
14
|
-
* out as flat black, silently and only in the CLI's copy of an image the
|
|
15
|
-
* site rendered correctly. resvg is what `@vercel/og` itself uses.
|
|
16
|
-
*
|
|
17
|
-
* `@vercel/og` is deliberately NOT used here even though this mirrors it. Its
|
|
18
|
-
* Node build is bundled for a CommonJS loader: imported as ESM it wants a
|
|
19
|
-
* global `require`, then a global `__dirname`, and then an `hb.wasm` the
|
|
20
|
-
* package does not ship. `next/og` cannot be imported outside Next's bundler at
|
|
21
|
-
* all. Driving Satori directly is the same rendering, three hacks fewer.
|
|
22
|
-
*/
|
|
23
|
-
import { Resvg } from "@resvg/resvg-js";
|
|
24
|
-
import satori, {} from "satori";
|
|
25
|
-
import { loadOpenGraph } from "../repo/source.js";
|
|
26
|
-
let fonts;
|
|
27
|
-
/** The embedded faces, in Satori's shape. Decoded once per process. */
|
|
28
|
-
async function satoriFonts() {
|
|
29
|
-
if (fonts)
|
|
30
|
-
return fonts;
|
|
31
|
-
const { loadFonts } = await loadOpenGraph();
|
|
32
|
-
return (fonts = loadFonts().map((font) => ({
|
|
33
|
-
name: font.name,
|
|
34
|
-
data: font.data,
|
|
35
|
-
weight: font.weight,
|
|
36
|
-
style: font.style,
|
|
37
|
-
})));
|
|
38
|
-
}
|
|
39
|
-
export async function render(element, { width, height, scale = 1 }) {
|
|
40
|
-
const svg = await satori(element, {
|
|
41
|
-
width,
|
|
42
|
-
height,
|
|
43
|
-
fonts: await satoriFonts(),
|
|
44
|
-
});
|
|
45
|
-
const png = new Resvg(svg, {
|
|
46
|
-
fitTo: { mode: "width", value: Math.round(width * scale) },
|
|
47
|
-
// Satori has already turned every glyph into a path, so resvg never needs
|
|
48
|
-
// to resolve a family. Saying so keeps it from probing the host's fonts,
|
|
49
|
-
// which is the one thing here that could differ between a contributor's
|
|
50
|
-
// machine and CI.
|
|
51
|
-
font: { loadSystemFonts: false },
|
|
52
|
-
})
|
|
53
|
-
.render()
|
|
54
|
-
.asPng();
|
|
55
|
-
return {
|
|
56
|
-
png,
|
|
57
|
-
svg,
|
|
58
|
-
width: Math.round(width * scale),
|
|
59
|
-
height: Math.round(height * scale),
|
|
60
|
-
};
|
|
61
|
-
}
|
package/dist/images/select.d.ts
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import type { Format } from "./open-graph-types.js";
|
|
2
|
-
import type { Graphic } from "./graphics.js";
|
|
3
|
-
/**
|
|
4
|
-
* Turning what somebody typed into a list of (graphic, format) pairs.
|
|
5
|
-
*
|
|
6
|
-
* Kept apart from `commands.ts` because all of it is pure: given a registry and
|
|
7
|
-
* some patterns, these answer the same way every time and can be tested
|
|
8
|
-
* without a terminal, a database or a renderer.
|
|
9
|
-
*/
|
|
10
|
-
/** One image to render: a subject, and the size to draw it at. */
|
|
11
|
-
export interface Selection {
|
|
12
|
-
graphic: Graphic;
|
|
13
|
-
format: Format;
|
|
14
|
-
}
|
|
15
|
-
export interface GraphicMatch {
|
|
16
|
-
matched: Graphic[];
|
|
17
|
-
/** Patterns that named nothing, so the caller can say which. */
|
|
18
|
-
unmatched: string[];
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Resolves graphic patterns.
|
|
22
|
-
*
|
|
23
|
-
* Every graphic is `group/name`, and three shapes of pattern read against
|
|
24
|
-
* that: a lone star (or a star over a star) is everything, `group/` plus a star
|
|
25
|
-
* is a whole group, and anything else is an exact name. Deliberately not a
|
|
26
|
-
* general glob — `event/2026-` plus a star looks like it should work and would
|
|
27
|
-
* be one more thing to specify, and the picker covers the case where somebody
|
|
28
|
-
* does not know the exact name.
|
|
29
|
-
*
|
|
30
|
-
* A bare group is accepted as shorthand for `group/*`, because `images icons`
|
|
31
|
-
* is what people type and refusing it teaches nothing.
|
|
32
|
-
*/
|
|
33
|
-
export declare function matchGraphics(patterns: readonly string[], graphics: readonly Graphic[]): GraphicMatch;
|
|
34
|
-
/**
|
|
35
|
-
* Pairs each graphic with each requested format it actually supports.
|
|
36
|
-
*
|
|
37
|
-
* The matrix is sparse, and silently dropping a pair is the wrong answer in one
|
|
38
|
-
* direction only: asking for `*` at every format should quietly skip the
|
|
39
|
-
* combinations that do not exist, while asking for one graphic at one format it
|
|
40
|
-
* cannot do should say so. The caller decides which case it is; this reports
|
|
41
|
-
* both halves.
|
|
42
|
-
*/
|
|
43
|
-
export declare function pair(graphics: readonly Graphic[], formats: readonly string[]): Promise<{
|
|
44
|
-
selections: Selection[];
|
|
45
|
-
unsupported: {
|
|
46
|
-
graphic: string;
|
|
47
|
-
format: string;
|
|
48
|
-
}[];
|
|
49
|
-
}>;
|
|
50
|
-
/** Every format any of these graphics can be drawn at, in registry order. */
|
|
51
|
-
export declare function formatsFor(graphics: readonly Graphic[]): Promise<Format[]>;
|
|
52
|
-
/**
|
|
53
|
-
* Splits `--flag=value` into two tokens.
|
|
54
|
-
*
|
|
55
|
-
* The rest of this CLI reads flags as `--flag value`, and `positionals()`
|
|
56
|
-
* depends on that to tell a flag's value from a subcommand. `--format=a,b`
|
|
57
|
-
* arrives as one token, so it would be read as a positional — that is, as a
|
|
58
|
-
* graphic name — and `images --format=og` would try to render a graphic called
|
|
59
|
-
* `--format=og` and report nothing matched. Normalising here lets both spellings
|
|
60
|
-
* work without teaching the shared parser about either.
|
|
61
|
-
*/
|
|
62
|
-
export declare function normalizeArgv(argv: readonly string[]): string[];
|
|
63
|
-
/** A comma-separated flag value, trimmed and emptied of blanks. */
|
|
64
|
-
export declare function commaList(value: string | undefined): string[];
|