@byline/cli 1.6.2 → 1.7.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/phases/wire/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,KAAK,EAA2B,MAAM,gBAAgB,CAAA;AAKpE,eAAO,MAAM,SAAS,EAAE,KA+CvB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/phases/wire/index.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,KAAK,EAA2B,MAAM,gBAAgB,CAAA;AAiBpE,eAAO,MAAM,SAAS,EAAE,KA+CvB,CAAA"}
@@ -1,9 +1,22 @@
1
1
  import { wireRootTsx } from './root-tsx.js';
2
2
  import { wireServerTs } from './server-ts.js';
3
+ import { wireServerUploads } from './server-uploads.js';
3
4
  import { wireStartTs } from './start-ts.js';
4
5
  import { wireTsconfig } from './tsconfig.js';
5
6
  import { wireViteConfig } from './vite-config.js';
6
- const SUB_EDITS = [wireServerTs, wireStartTs, wireRootTsx, wireTsconfig, wireViteConfig];
7
+ // Order matters: `wireServerTs` injects the side-effect import for
8
+ // `byline/server.config` at the top of `src/server.ts`; `wireServerUploads`
9
+ // then runs in the same file to wrap the `fetch` handler with the runtime
10
+ // uploads helper. The two sub-edits are independent, but keeping them
11
+ // adjacent makes the resulting diff easier to read.
12
+ const SUB_EDITS = [
13
+ wireServerTs,
14
+ wireServerUploads,
15
+ wireStartTs,
16
+ wireRootTsx,
17
+ wireTsconfig,
18
+ wireViteConfig,
19
+ ];
7
20
  export const wirePhase = {
8
21
  id: 'wire',
9
22
  title: 'Wire — inject imports + path mappings + verify vite.config.ts',
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/phases/wire/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/phases/wire/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { SubEdit } from './shared.js';
2
+ export declare const wireServerUploads: SubEdit;
3
+ //# sourceMappingURL=server-uploads.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-uploads.d.ts","sourceRoot":"","sources":["../../../src/phases/wire/server-uploads.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,OAAO,EAAiB,MAAM,aAAa,CAAA;AAgBzD,eAAO,MAAM,iBAAiB,EAAE,OAS/B,CAAA"}
@@ -0,0 +1,195 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { Node, Project, SyntaxKind, } from 'ts-morph';
3
+ const REL = 'src/server.ts';
4
+ const SERVE_UPLOADS_NAME = 'serveUploads';
5
+ const SERVE_UPLOADS_MODULE = '@byline/host-tanstack-start/integrations/serve-uploads';
6
+ const SNIPPET = `import { ${SERVE_UPLOADS_NAME} } from '${SERVE_UPLOADS_MODULE}'
7
+
8
+ // Inside createServerEntry({ ... }), replace the fetch handler with:
9
+ async fetch(request) {
10
+ const upload = await ${SERVE_UPLOADS_NAME}(request)
11
+ if (upload) return upload
12
+ return handler.fetch(request)
13
+ },
14
+ `;
15
+ export const wireServerUploads = {
16
+ key: 'server-uploads',
17
+ title: `Wrap fetch with \`${SERVE_UPLOADS_NAME}\` runtime handler in ${REL}`,
18
+ async preview(ctx) {
19
+ return run(ctx, true);
20
+ },
21
+ async apply(ctx) {
22
+ return run(ctx, false);
23
+ },
24
+ };
25
+ async function run(ctx, dryRun) {
26
+ const path = ctx.resolve(REL);
27
+ if (!existsSync(path)) {
28
+ return { status: 'blocked', message: `${REL} not found — host phase should have caught this` };
29
+ }
30
+ // Cheap pre-check: if the helper is already referenced anywhere, we
31
+ // assume it's wired. Avoids a full AST round-trip on the common
32
+ // already-wired case and on any non-trivial server.ts the user has
33
+ // hand-customized to call serveUploads themselves.
34
+ const text = readFileSync(path, 'utf8');
35
+ if (text.includes(SERVE_UPLOADS_NAME)) {
36
+ return { status: 'skipped', message: `${REL}: ${SERVE_UPLOADS_NAME} already wired` };
37
+ }
38
+ const project = new Project({ useInMemoryFileSystem: false, skipAddingFilesFromTsConfig: true });
39
+ let source;
40
+ try {
41
+ source = project.addSourceFileAtPath(path);
42
+ }
43
+ catch {
44
+ return manualBail(`${REL}: could not parse`);
45
+ }
46
+ const createServerEntryCall = findCreateServerEntryCall(source);
47
+ if (!createServerEntryCall) {
48
+ return manualBail(`${REL}: no \`createServerEntry(...)\` call found`);
49
+ }
50
+ const optionsLiteral = getFirstObjectArg(createServerEntryCall);
51
+ if (!optionsLiteral) {
52
+ return manualBail(`${REL}: \`createServerEntry\` argument is not an inline object literal — cannot safely auto-edit`);
53
+ }
54
+ const fetchProp = optionsLiteral.getProperty('fetch');
55
+ if (!fetchProp) {
56
+ return manualBail(`${REL}: no \`fetch\` property on \`createServerEntry\` options`);
57
+ }
58
+ if (!isCanonicalFetchProperty(fetchProp)) {
59
+ return manualBail(`${REL}: existing \`fetch\` does not match the canonical scaffold (\`fetch(request) { return handler.fetch(request) }\`) — manual wire required`);
60
+ }
61
+ if (dryRun) {
62
+ return {
63
+ status: 'done',
64
+ message: `${REL}: will wrap fetch with ${SERVE_UPLOADS_NAME}`,
65
+ };
66
+ }
67
+ ensureImport(source);
68
+ replaceFetchMethod(optionsLiteral);
69
+ source.saveSync();
70
+ return { status: 'done', message: `${REL}: wrapped fetch with ${SERVE_UPLOADS_NAME}` };
71
+ }
72
+ function manualBail(message) {
73
+ return { status: 'manual', message, snippet: SNIPPET };
74
+ }
75
+ function findCreateServerEntryCall(source) {
76
+ // Walk top-level descendants looking for any `createServerEntry(...)`
77
+ // call. The TanStack Start scaffold exports it via `export default`,
78
+ // but we accept any position in the file.
79
+ for (const call of source.getDescendantsOfKind(SyntaxKind.CallExpression)) {
80
+ if (call.getExpression().getText() === 'createServerEntry')
81
+ return call;
82
+ }
83
+ return undefined;
84
+ }
85
+ function getFirstObjectArg(call) {
86
+ const args = call.getArguments();
87
+ if (args.length === 0)
88
+ return undefined;
89
+ const first = args[0];
90
+ if (first && Node.isObjectLiteralExpression(first))
91
+ return first;
92
+ return undefined;
93
+ }
94
+ /**
95
+ * Recognise the canonical TanStack Start scaffold shape so we only edit
96
+ * a `fetch` we fully understand. Three accepted forms (all equivalent
97
+ * in behaviour):
98
+ *
99
+ * fetch(request) { return handler.fetch(request) } // method shorthand
100
+ * fetch: (request) => handler.fetch(request) // arrow w/ expr body
101
+ * fetch: (request) => { return handler.fetch(request) }
102
+ * fetch: function (request) { return handler.fetch(request) }
103
+ *
104
+ * Anything else — extra statements, async, type annotations on the
105
+ * parameter, a different parameter name, a wrapped/intercepted call —
106
+ * forces a manual bail.
107
+ */
108
+ function isCanonicalFetchProperty(prop) {
109
+ const shape = extractFunctionShape(prop);
110
+ if (!shape)
111
+ return false;
112
+ const { parameters, body } = shape;
113
+ if (parameters.length !== 1)
114
+ return false;
115
+ if (parameters[0]?.getName() !== 'request')
116
+ return false;
117
+ if (!body)
118
+ return false;
119
+ // Body should evaluate `handler.fetch(request)` — either as the sole
120
+ // statement of a block (`{ return handler.fetch(request) }`) or as
121
+ // the bare expression of an arrow function.
122
+ let returnExpr;
123
+ if (Node.isBlock(body)) {
124
+ const stmts = body.getStatements();
125
+ if (stmts.length !== 1)
126
+ return false;
127
+ const only = stmts[0];
128
+ if (!only || !Node.isReturnStatement(only))
129
+ return false;
130
+ returnExpr = only.getExpression();
131
+ }
132
+ else {
133
+ returnExpr = body;
134
+ }
135
+ if (!returnExpr || !Node.isCallExpression(returnExpr))
136
+ return false;
137
+ // Callee text, whitespace-stripped, must be exactly `handler.fetch`.
138
+ const callee = returnExpr.getExpression().getText().replace(/\s+/g, '');
139
+ if (callee !== 'handler.fetch')
140
+ return false;
141
+ const callArgs = returnExpr.getArguments();
142
+ if (callArgs.length !== 1)
143
+ return false;
144
+ if (callArgs[0]?.getText() !== 'request')
145
+ return false;
146
+ return true;
147
+ }
148
+ function extractFunctionShape(prop) {
149
+ if (Node.isMethodDeclaration(prop)) {
150
+ return { parameters: prop.getParameters(), body: prop.getBody() };
151
+ }
152
+ if (Node.isPropertyAssignment(prop)) {
153
+ const init = prop.getInitializer();
154
+ if (!init)
155
+ return undefined;
156
+ if (Node.isArrowFunction(init) || Node.isFunctionExpression(init)) {
157
+ return { parameters: init.getParameters(), body: init.getBody() };
158
+ }
159
+ }
160
+ return undefined;
161
+ }
162
+ function ensureImport(source) {
163
+ const existing = source
164
+ .getImportDeclarations()
165
+ .find((d) => d.getModuleSpecifierValue() === SERVE_UPLOADS_MODULE);
166
+ if (existing) {
167
+ const already = existing.getNamedImports().some((n) => n.getName() === SERVE_UPLOADS_NAME);
168
+ if (!already)
169
+ existing.addNamedImport(SERVE_UPLOADS_NAME);
170
+ return;
171
+ }
172
+ // Insert after the last existing import; placement among siblings
173
+ // doesn't matter for behaviour.
174
+ const imports = source.getImportDeclarations();
175
+ source.insertImportDeclaration(imports.length, {
176
+ moduleSpecifier: SERVE_UPLOADS_MODULE,
177
+ namedImports: [SERVE_UPLOADS_NAME],
178
+ });
179
+ }
180
+ function replaceFetchMethod(options) {
181
+ const existing = options.getProperty('fetch');
182
+ if (existing)
183
+ existing.remove();
184
+ options.addMethod({
185
+ name: 'fetch',
186
+ isAsync: true,
187
+ parameters: [{ name: 'request' }],
188
+ statements: [
189
+ `const upload = await ${SERVE_UPLOADS_NAME}(request)`,
190
+ 'if (upload) return upload',
191
+ 'return handler.fetch(request)',
192
+ ],
193
+ });
194
+ }
195
+ //# sourceMappingURL=server-uploads.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-uploads.js","sourceRoot":"","sources":["../../../src/phases/wire/server-uploads.ts"],"names":[],"mappings":"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"}
@@ -77,8 +77,15 @@ async function buildBylineCore(): Promise<BylineCore<AdminStore>> {
77
77
  // Local filesystem — suitable for development and self-hosted
78
78
  // deployments. For cloud/production, swap to `@byline/storage-s3`
79
79
  // (see the commented example below).
80
+ //
81
+ // IMPORTANT: `uploadDir` lives OUTSIDE `public/` on purpose. With
82
+ // TanStack Start + Nitro, anything under `public/` is snapshotted
83
+ // into `.output/public/` at build time, and the static handler reads
84
+ // from that snapshot — so newly-uploaded files 404 until the next
85
+ // rebuild. Pair this with a runtime `/uploads/*` handler in
86
+ // `src/server.ts` that streams from `uploadDir` on every request.
80
87
  storage: localStorageProvider({
81
- uploadDir: './public/uploads',
88
+ uploadDir: './uploads',
82
89
  baseUrl: '/uploads',
83
90
  }),
84
91
  // S3-compatible alternative (AWS S3 / Cloudflare R2 / MinIO). Replace
@@ -47,19 +47,6 @@ function formatNumber(n: number, decimalPlaces: number): string {
47
47
  })
48
48
  }
49
49
 
50
- // ---------------------------------------------------------------------------
51
- // Helpers
52
- // ---------------------------------------------------------------------------
53
-
54
- /**
55
- * Derive the avif thumbnail URL from the original storageUrl using the same
56
- * convention as MediaThumbnailCell / the Sharp upload processor.
57
- * `/uploads/media/2026/02/img.jpg` → `/uploads/media/2026/02/img-thumbnail.avif`
58
- */
59
- function deriveThumbnailUrl(storageUrl: string): string {
60
- return storageUrl.replace(/\.[^.]+$/, '-thumbnail.avif')
61
- }
62
-
63
50
  // ---------------------------------------------------------------------------
64
51
  // Order-by config
65
52
  // ---------------------------------------------------------------------------
@@ -261,11 +248,8 @@ export function MediaListView({
261
248
  {(data.docs as any[]).map((doc) => {
262
249
  const fields = doc.fields ?? {}
263
250
  const img = fields.image as StoredFileValue | null | undefined
264
- const thumbUrl = img?.storageUrl
265
- ? img.thumbnailGenerated
266
- ? deriveThumbnailUrl(img.storageUrl)
267
- : img.storageUrl
268
- : null
251
+ const thumbVariant = img?.variants?.find((v) => v.name === 'thumbnail')
252
+ const thumbUrl = thumbVariant?.storageUrl ?? img?.storageUrl ?? null
269
253
 
270
254
  const updatedAt = doc.updatedAt ?? null
271
255
 
@@ -8,18 +8,6 @@
8
8
 
9
9
  import type { FormatterProps, StoredFileValue } from '@byline/core'
10
10
 
11
- /**
12
- * Derive the thumbnail URL from the original storageUrl.
13
- *
14
- * Sharp writes variants as siblings of the original file using the naming
15
- * convention `<basename>-<variantName>.<outputExt>`:
16
- * `/uploads/media/2026/02/abc-photo.jpg`
17
- * → `/uploads/media/2026/02/abc-photo-thumbnail.avif`
18
- */
19
- function deriveThumbnailUrl(storageUrl: string): string {
20
- return storageUrl.replace(/\.[^.]+$/, '-thumbnail.avif')
21
- }
22
-
23
11
  /**
24
12
  * FormatBadge renders a muted pill showing the image format (e.g. JPEG, PNG, SVG).
25
13
  * Intended for use alongside the status badge in list-view card meta.
@@ -34,7 +22,7 @@ export function FormatBadge({ format }: { format: string }) {
34
22
 
35
23
  /**
36
24
  * MediaThumbnailCell renders a small preview image in the Media list view.
37
- * When the `thumbnail` variant has been generated, the smaller avif is used;
25
+ * When a `thumbnail` variant has been generated its `storageUrl` is used;
38
26
  * otherwise the original storage URL is shown.
39
27
  */
40
28
  export function MediaThumbnail({ record }: FormatterProps) {
@@ -50,7 +38,8 @@ export function MediaThumbnail({ record }: FormatterProps) {
50
38
  )
51
39
  }
52
40
 
53
- const thumbUrl = img.thumbnailGenerated ? deriveThumbnailUrl(img.storageUrl) : img.storageUrl
41
+ const thumbVariant = img.variants?.find((v) => v.name === 'thumbnail')
42
+ const thumbUrl = thumbVariant?.storageUrl ?? img.storageUrl
54
43
 
55
44
  return (
56
45
  <img
@@ -119,11 +119,16 @@ async function buildBylineCore(): Promise<BylineCore<AdminStore>> {
119
119
  // addition to) this site-wide default.
120
120
  //
121
121
  // Local filesystem is suitable for development and self-hosted
122
- // deployments. The `uploadDir` is served as a static path at `baseUrl`
123
- // by your web server. For cloud/production deployments, swap to
124
- // `@byline/storage-s3` see the commented example below.
122
+ // deployments. The `uploadDir` is served at `baseUrl` by a runtime
123
+ // handler in `src/server.ts` NOT by the framework's static-asset
124
+ // pipeline. Keeping uploads outside `public/` is what lets newly-
125
+ // uploaded files appear without a rebuild: `vite build` snapshots
126
+ // `public/` into `.output/public/`, but the runtime handler reads
127
+ // `uploadDir` directly on every request. For cloud/production
128
+ // deployments, swap to `@byline/storage-s3` — see the commented
129
+ // example below.
125
130
  storage: localStorageProvider({
126
- uploadDir: './public/uploads',
131
+ uploadDir: './uploads',
127
132
  baseUrl: '/uploads',
128
133
  }),
129
134
  // S3-compatible alternative (AWS S3 / Cloudflare R2 / MinIO). Replace
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@byline/cli",
3
3
  "private": false,
4
4
  "license": "MPL-2.0",
5
- "version": "1.6.2",
5
+ "version": "1.7.1",
6
6
  "engines": {
7
7
  "node": ">=20.9.0"
8
8
  },