@valbuild/server 0.136.2 → 0.136.4

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/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # @valbuild/server
2
2
 
3
+ ## 0.136.4
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [[`d19b1f6`](https://github.com/valbuild/val/commit/d19b1f67360b057e63536ac020f7109bd2496389)]:
8
+ - @valbuild/ui@0.136.4
9
+
10
+ ## 0.136.3
11
+
12
+ ### Patch Changes
13
+
14
+ - [#726](https://github.com/valbuild/val/pull/726) [`3b19534`](https://github.com/valbuild/val/commit/3b19534bb7aa16452a104906d9594083c3bc3e89) Thanks [@freekh](https://github.com/freekh)! - A Studio publish of a project with no repository now ships every edit that was saved, and keeps the site styled.
15
+
16
+ - A host that embeds the project's source can hand it to Val as `projectSource` (`initValServer(..., { http: { projectSource } })`). A publish then patches that text, not text fetched from the content service at a commit, which a project with no repository does not have. The build platform's wiring passes the build's own source, so `.val.ts` can be rendered for a project made on `/new`. Before, its first save produced no files, and the build that followed carried none of the save's edits.
17
+ - New route `GET /api/val/built-source`: the `.val.ts` text of every module changed since the running build, with every commit since it applied. The Studio builds from it, so an edit whose own publish failed, and a Finish publishing, are no longer left out of the next build.
18
+ - A Studio build that compiles no stylesheet (a browser cannot run Tailwind `@plugin`s) keeps the live site's. A Studio save never changes a stylesheet or a component, so the live CSS is still the right one.
19
+
20
+ - Updated dependencies [[`38d24f7`](https://github.com/valbuild/val/commit/38d24f723639ee109d1007854507c4571bc82575), [`3b19534`](https://github.com/valbuild/val/commit/3b19534bb7aa16452a104906d9594083c3bc3e89), [`fa28164`](https://github.com/valbuild/val/commit/fa28164dfd1e05ff53eccf375334741d857fd2ef)]:
21
+ - @valbuild/ui@0.136.3
22
+ - @valbuild/shared@0.136.3
23
+
3
24
  ## 0.136.2
4
25
 
5
26
  ### Patch Changes
@@ -52,6 +52,19 @@ export declare class ValOpsHttp extends ValOps {
52
52
  * initialiser runs before the constructor body has assigned it.
53
53
  */
54
54
  protected readonly mirrorsSourceFiles: boolean;
55
+ /**
56
+ * The running build's own source, when the host embedded it.
57
+ *
58
+ * Preferred over the content service for the `.val.ts` text a publish
59
+ * patches, for two reasons. It is exactly the text this build was made from,
60
+ * which is what the patches in its chain were written against. And it is the
61
+ * only copy there is for a project with no repository: the content service
62
+ * keeps a managed project's content as Source, not as files, and answers a
63
+ * file read for one with "no GitHub repo".
64
+ */
65
+ private readonly projectSource;
66
+ /** Did the host hand over the running build's source? See `projectSource`. */
67
+ embedsSource(): boolean;
55
68
  /**
56
69
  * What the content service last said this project expects of its publisher.
57
70
  *
@@ -102,6 +115,8 @@ export declare class ValOpsHttp extends ValOps {
102
115
  * the root would be /apps/my-app
103
116
  */
104
117
  root?: string;
118
+ /** See `projectSource` on {@link ValApiOptions}. */
119
+ projectSource?: Record<string, string>;
105
120
  });
106
121
  /**
107
122
  * A deployment that cannot mirror a project which expects to be mirrored.
@@ -128,6 +128,18 @@ type ValServerOverrides = Partial<{
128
128
  * @example "main"
129
129
  */
130
130
  gitBranch?: string;
131
+ /**
132
+ * The project's own source files, by path with no leading slash, as the
133
+ * RUNNING build was made from them.
134
+ *
135
+ * Where a publish reads the `.val.ts` text it patches. Without it that text
136
+ * is fetched from the content service at {@link gitCommit}, which only a
137
+ * project with a repository has -- so a project whose content service is the
138
+ * store of record could not produce `.val.ts` at all, and a build made after
139
+ * a save carried none of the save's edits. A host that builds the project
140
+ * itself embeds its source in the build and hands it over here.
141
+ */
142
+ projectSource?: Record<string, string>;
131
143
  /**
132
144
  * The base url of Val.
133
145
  *
@@ -133,6 +133,8 @@ export type ValServerConfig = ValServerOptions & ({
133
133
  branch: string;
134
134
  };
135
135
  root?: string;
136
+ /** See `projectSource` on {@link ValApiOptions}. */
137
+ projectSource?: Record<string, string>;
136
138
  config: ValConfig;
137
139
  }
138
140
  /**
@@ -7948,6 +7948,22 @@ class ValOpsHttp extends ValOps {
7948
7948
  * initialiser runs before the constructor body has assigned it.
7949
7949
  */
7950
7950
  mirrorsSourceFiles;
7951
+ /**
7952
+ * The running build's own source, when the host embedded it.
7953
+ *
7954
+ * Preferred over the content service for the `.val.ts` text a publish
7955
+ * patches, for two reasons. It is exactly the text this build was made from,
7956
+ * which is what the patches in its chain were written against. And it is the
7957
+ * only copy there is for a project with no repository: the content service
7958
+ * keeps a managed project's content as Source, not as files, and answers a
7959
+ * file read for one with "no GitHub repo".
7960
+ */
7961
+ projectSource;
7962
+
7963
+ /** Did the host hand over the running build's source? See `projectSource`. */
7964
+ embedsSource() {
7965
+ return this.projectSource !== null;
7966
+ }
7951
7967
  /**
7952
7968
  * What the content service last said this project expects of its publisher.
7953
7969
  *
@@ -7995,7 +8011,8 @@ class ValOpsHttp extends ValOps {
7995
8011
  Authorization: `Bearer ${auth.apiKey}`
7996
8012
  };
7997
8013
  this.root = (options === null || options === void 0 ? void 0 : options.root) ?? "";
7998
- this.mirrorsSourceFiles = git !== null;
8014
+ this.projectSource = (options === null || options === void 0 ? void 0 : options.projectSource) ?? null;
8015
+ this.mirrorsSourceFiles = git !== null || this.projectSource !== null;
7999
8016
  }
8000
8017
  /**
8001
8018
  * A deployment that cannot mirror a project which expects to be mirrored.
@@ -9181,6 +9198,19 @@ class ValOpsHttp extends ValOps {
9181
9198
  });
9182
9199
  }
9183
9200
  async getSourceFile(path) {
9201
+ if (this.projectSource !== null) {
9202
+ const text = this.projectSource[path.replace(/^\/+/, "")];
9203
+ if (text === undefined) {
9204
+ return {
9205
+ error: {
9206
+ message: `Cannot read the source of ${path}: it is not in the source this build was made from.`
9207
+ }
9208
+ };
9209
+ }
9210
+ return {
9211
+ data: text
9212
+ };
9213
+ }
9184
9214
  /*
9185
9215
  * There is no file to read without a repository to read it from.
9186
9216
  *
@@ -11310,6 +11340,9 @@ async function initHandlerOptions(route, opts, config) {
11310
11340
  }
11311
11341
  } : {}),
11312
11342
  root: opts.root,
11343
+ ...(opts.projectSource !== undefined ? {
11344
+ projectSource: opts.projectSource
11345
+ } : {}),
11313
11346
  project: maybeValProject,
11314
11347
  valEnableRedirectUrl,
11315
11348
  valDisableRedirectUrl,
@@ -11376,7 +11409,10 @@ function createValOps(valModules, options) {
11376
11409
  }, valModules, {
11377
11410
  formatter: options.formatter,
11378
11411
  root: options.root,
11379
- config: options.config
11412
+ config: options.config,
11413
+ ...(options.projectSource !== undefined ? {
11414
+ projectSource: options.projectSource
11415
+ } : {})
11380
11416
  });
11381
11417
  }
11382
11418
  if (options.mode === "memory") {
@@ -13369,6 +13405,71 @@ const ValServer = (valModules, options, callbacks) => {
13369
13405
  return res;
13370
13406
  }
13371
13407
  },
13408
+ "/built-source": {
13409
+ GET: async req => {
13410
+ const auth = getAuth(req.cookies);
13411
+ if (auth.error) {
13412
+ return {
13413
+ status: 401,
13414
+ json: {
13415
+ message: auth.error
13416
+ }
13417
+ };
13418
+ }
13419
+ if (!(serverOps instanceof ValOpsHttp) || !serverOps.embedsSource()) {
13420
+ return {
13421
+ status: 409,
13422
+ json: {
13423
+ message: "This server has no source of its own to render commits into."
13424
+ }
13425
+ };
13426
+ }
13427
+ const chain = await serverOps.fetchPatches({
13428
+ patchIds: undefined,
13429
+ excludePatchOps: false
13430
+ });
13431
+ if (chain.error) {
13432
+ return {
13433
+ status: 500,
13434
+ json: {
13435
+ message: chain.error.message
13436
+ }
13437
+ };
13438
+ }
13439
+ /*
13440
+ * The patches committed SINCE this build, as though pending.
13441
+ *
13442
+ * `applicable/patches` answers with the ones applied at a commit after
13443
+ * the one this build was made from, which is exactly what the text
13444
+ * embedded in it is missing. `analyzePatches` skips applied patches --
13445
+ * for a save they are already in the file it reads -- so they are
13446
+ * handed over unapplied: here the file predates them.
13447
+ */
13448
+ const committed = chain.patches.filter(patch => patch.appliedAt !== null).map(patch => ({
13449
+ ...patch,
13450
+ appliedAt: null
13451
+ }));
13452
+ const prepared = await serverOps.prepare({
13453
+ ...serverOps.analyzePatches(committed, chain.commits, commit),
13454
+ ...chain,
13455
+ patches: committed
13456
+ });
13457
+ if (prepared.hasErrors) {
13458
+ return {
13459
+ status: 500,
13460
+ json: {
13461
+ message: "The commits since this build could not be rendered into source: " + JSON.stringify(prepared.sourceFilePatchErrors).slice(0, 500)
13462
+ }
13463
+ };
13464
+ }
13465
+ return {
13466
+ status: 200,
13467
+ json: {
13468
+ files: prepared.patchedSourceFiles
13469
+ }
13470
+ };
13471
+ }
13472
+ },
13372
13473
  "/profiles": {
13373
13474
  GET: async req => {
13374
13475
  const cookies = req.cookies;
@@ -7948,6 +7948,22 @@ class ValOpsHttp extends ValOps {
7948
7948
  * initialiser runs before the constructor body has assigned it.
7949
7949
  */
7950
7950
  mirrorsSourceFiles;
7951
+ /**
7952
+ * The running build's own source, when the host embedded it.
7953
+ *
7954
+ * Preferred over the content service for the `.val.ts` text a publish
7955
+ * patches, for two reasons. It is exactly the text this build was made from,
7956
+ * which is what the patches in its chain were written against. And it is the
7957
+ * only copy there is for a project with no repository: the content service
7958
+ * keeps a managed project's content as Source, not as files, and answers a
7959
+ * file read for one with "no GitHub repo".
7960
+ */
7961
+ projectSource;
7962
+
7963
+ /** Did the host hand over the running build's source? See `projectSource`. */
7964
+ embedsSource() {
7965
+ return this.projectSource !== null;
7966
+ }
7951
7967
  /**
7952
7968
  * What the content service last said this project expects of its publisher.
7953
7969
  *
@@ -7995,7 +8011,8 @@ class ValOpsHttp extends ValOps {
7995
8011
  Authorization: `Bearer ${auth.apiKey}`
7996
8012
  };
7997
8013
  this.root = (options === null || options === void 0 ? void 0 : options.root) ?? "";
7998
- this.mirrorsSourceFiles = git !== null;
8014
+ this.projectSource = (options === null || options === void 0 ? void 0 : options.projectSource) ?? null;
8015
+ this.mirrorsSourceFiles = git !== null || this.projectSource !== null;
7999
8016
  }
8000
8017
  /**
8001
8018
  * A deployment that cannot mirror a project which expects to be mirrored.
@@ -9181,6 +9198,19 @@ class ValOpsHttp extends ValOps {
9181
9198
  });
9182
9199
  }
9183
9200
  async getSourceFile(path) {
9201
+ if (this.projectSource !== null) {
9202
+ const text = this.projectSource[path.replace(/^\/+/, "")];
9203
+ if (text === undefined) {
9204
+ return {
9205
+ error: {
9206
+ message: `Cannot read the source of ${path}: it is not in the source this build was made from.`
9207
+ }
9208
+ };
9209
+ }
9210
+ return {
9211
+ data: text
9212
+ };
9213
+ }
9184
9214
  /*
9185
9215
  * There is no file to read without a repository to read it from.
9186
9216
  *
@@ -11310,6 +11340,9 @@ async function initHandlerOptions(route, opts, config) {
11310
11340
  }
11311
11341
  } : {}),
11312
11342
  root: opts.root,
11343
+ ...(opts.projectSource !== undefined ? {
11344
+ projectSource: opts.projectSource
11345
+ } : {}),
11313
11346
  project: maybeValProject,
11314
11347
  valEnableRedirectUrl,
11315
11348
  valDisableRedirectUrl,
@@ -11376,7 +11409,10 @@ function createValOps(valModules, options) {
11376
11409
  }, valModules, {
11377
11410
  formatter: options.formatter,
11378
11411
  root: options.root,
11379
- config: options.config
11412
+ config: options.config,
11413
+ ...(options.projectSource !== undefined ? {
11414
+ projectSource: options.projectSource
11415
+ } : {})
11380
11416
  });
11381
11417
  }
11382
11418
  if (options.mode === "memory") {
@@ -13369,6 +13405,71 @@ const ValServer = (valModules, options, callbacks) => {
13369
13405
  return res;
13370
13406
  }
13371
13407
  },
13408
+ "/built-source": {
13409
+ GET: async req => {
13410
+ const auth = getAuth(req.cookies);
13411
+ if (auth.error) {
13412
+ return {
13413
+ status: 401,
13414
+ json: {
13415
+ message: auth.error
13416
+ }
13417
+ };
13418
+ }
13419
+ if (!(serverOps instanceof ValOpsHttp) || !serverOps.embedsSource()) {
13420
+ return {
13421
+ status: 409,
13422
+ json: {
13423
+ message: "This server has no source of its own to render commits into."
13424
+ }
13425
+ };
13426
+ }
13427
+ const chain = await serverOps.fetchPatches({
13428
+ patchIds: undefined,
13429
+ excludePatchOps: false
13430
+ });
13431
+ if (chain.error) {
13432
+ return {
13433
+ status: 500,
13434
+ json: {
13435
+ message: chain.error.message
13436
+ }
13437
+ };
13438
+ }
13439
+ /*
13440
+ * The patches committed SINCE this build, as though pending.
13441
+ *
13442
+ * `applicable/patches` answers with the ones applied at a commit after
13443
+ * the one this build was made from, which is exactly what the text
13444
+ * embedded in it is missing. `analyzePatches` skips applied patches --
13445
+ * for a save they are already in the file it reads -- so they are
13446
+ * handed over unapplied: here the file predates them.
13447
+ */
13448
+ const committed = chain.patches.filter(patch => patch.appliedAt !== null).map(patch => ({
13449
+ ...patch,
13450
+ appliedAt: null
13451
+ }));
13452
+ const prepared = await serverOps.prepare({
13453
+ ...serverOps.analyzePatches(committed, chain.commits, commit),
13454
+ ...chain,
13455
+ patches: committed
13456
+ });
13457
+ if (prepared.hasErrors) {
13458
+ return {
13459
+ status: 500,
13460
+ json: {
13461
+ message: "The commits since this build could not be rendered into source: " + JSON.stringify(prepared.sourceFilePatchErrors).slice(0, 500)
13462
+ }
13463
+ };
13464
+ }
13465
+ return {
13466
+ status: 200,
13467
+ json: {
13468
+ files: prepared.patchedSourceFiles
13469
+ }
13470
+ };
13471
+ }
13472
+ },
13372
13473
  "/profiles": {
13373
13474
  GET: async req => {
13374
13475
  const cookies = req.cookies;
@@ -7914,6 +7914,22 @@ class ValOpsHttp extends ValOps {
7914
7914
  * initialiser runs before the constructor body has assigned it.
7915
7915
  */
7916
7916
  mirrorsSourceFiles;
7917
+ /**
7918
+ * The running build's own source, when the host embedded it.
7919
+ *
7920
+ * Preferred over the content service for the `.val.ts` text a publish
7921
+ * patches, for two reasons. It is exactly the text this build was made from,
7922
+ * which is what the patches in its chain were written against. And it is the
7923
+ * only copy there is for a project with no repository: the content service
7924
+ * keeps a managed project's content as Source, not as files, and answers a
7925
+ * file read for one with "no GitHub repo".
7926
+ */
7927
+ projectSource;
7928
+
7929
+ /** Did the host hand over the running build's source? See `projectSource`. */
7930
+ embedsSource() {
7931
+ return this.projectSource !== null;
7932
+ }
7917
7933
  /**
7918
7934
  * What the content service last said this project expects of its publisher.
7919
7935
  *
@@ -7961,7 +7977,8 @@ class ValOpsHttp extends ValOps {
7961
7977
  Authorization: `Bearer ${auth.apiKey}`
7962
7978
  };
7963
7979
  this.root = (options === null || options === void 0 ? void 0 : options.root) ?? "";
7964
- this.mirrorsSourceFiles = git !== null;
7980
+ this.projectSource = (options === null || options === void 0 ? void 0 : options.projectSource) ?? null;
7981
+ this.mirrorsSourceFiles = git !== null || this.projectSource !== null;
7965
7982
  }
7966
7983
  /**
7967
7984
  * A deployment that cannot mirror a project which expects to be mirrored.
@@ -9147,6 +9164,19 @@ class ValOpsHttp extends ValOps {
9147
9164
  });
9148
9165
  }
9149
9166
  async getSourceFile(path) {
9167
+ if (this.projectSource !== null) {
9168
+ const text = this.projectSource[path.replace(/^\/+/, "")];
9169
+ if (text === undefined) {
9170
+ return {
9171
+ error: {
9172
+ message: `Cannot read the source of ${path}: it is not in the source this build was made from.`
9173
+ }
9174
+ };
9175
+ }
9176
+ return {
9177
+ data: text
9178
+ };
9179
+ }
9150
9180
  /*
9151
9181
  * There is no file to read without a repository to read it from.
9152
9182
  *
@@ -11276,6 +11306,9 @@ async function initHandlerOptions(route, opts, config) {
11276
11306
  }
11277
11307
  } : {}),
11278
11308
  root: opts.root,
11309
+ ...(opts.projectSource !== undefined ? {
11310
+ projectSource: opts.projectSource
11311
+ } : {}),
11279
11312
  project: maybeValProject,
11280
11313
  valEnableRedirectUrl,
11281
11314
  valDisableRedirectUrl,
@@ -11342,7 +11375,10 @@ function createValOps(valModules, options) {
11342
11375
  }, valModules, {
11343
11376
  formatter: options.formatter,
11344
11377
  root: options.root,
11345
- config: options.config
11378
+ config: options.config,
11379
+ ...(options.projectSource !== undefined ? {
11380
+ projectSource: options.projectSource
11381
+ } : {})
11346
11382
  });
11347
11383
  }
11348
11384
  if (options.mode === "memory") {
@@ -13335,6 +13371,71 @@ const ValServer = (valModules, options, callbacks) => {
13335
13371
  return res;
13336
13372
  }
13337
13373
  },
13374
+ "/built-source": {
13375
+ GET: async req => {
13376
+ const auth = getAuth(req.cookies);
13377
+ if (auth.error) {
13378
+ return {
13379
+ status: 401,
13380
+ json: {
13381
+ message: auth.error
13382
+ }
13383
+ };
13384
+ }
13385
+ if (!(serverOps instanceof ValOpsHttp) || !serverOps.embedsSource()) {
13386
+ return {
13387
+ status: 409,
13388
+ json: {
13389
+ message: "This server has no source of its own to render commits into."
13390
+ }
13391
+ };
13392
+ }
13393
+ const chain = await serverOps.fetchPatches({
13394
+ patchIds: undefined,
13395
+ excludePatchOps: false
13396
+ });
13397
+ if (chain.error) {
13398
+ return {
13399
+ status: 500,
13400
+ json: {
13401
+ message: chain.error.message
13402
+ }
13403
+ };
13404
+ }
13405
+ /*
13406
+ * The patches committed SINCE this build, as though pending.
13407
+ *
13408
+ * `applicable/patches` answers with the ones applied at a commit after
13409
+ * the one this build was made from, which is exactly what the text
13410
+ * embedded in it is missing. `analyzePatches` skips applied patches --
13411
+ * for a save they are already in the file it reads -- so they are
13412
+ * handed over unapplied: here the file predates them.
13413
+ */
13414
+ const committed = chain.patches.filter(patch => patch.appliedAt !== null).map(patch => ({
13415
+ ...patch,
13416
+ appliedAt: null
13417
+ }));
13418
+ const prepared = await serverOps.prepare({
13419
+ ...serverOps.analyzePatches(committed, chain.commits, commit),
13420
+ ...chain,
13421
+ patches: committed
13422
+ });
13423
+ if (prepared.hasErrors) {
13424
+ return {
13425
+ status: 500,
13426
+ json: {
13427
+ message: "The commits since this build could not be rendered into source: " + JSON.stringify(prepared.sourceFilePatchErrors).slice(0, 500)
13428
+ }
13429
+ };
13430
+ }
13431
+ return {
13432
+ status: 200,
13433
+ json: {
13434
+ files: prepared.patchedSourceFiles
13435
+ }
13436
+ };
13437
+ }
13438
+ },
13338
13439
  "/profiles": {
13339
13440
  GET: async req => {
13340
13441
  const cookies = req.cookies;
package/package.json CHANGED
@@ -16,7 +16,7 @@
16
16
  "./package.json": "./package.json"
17
17
  },
18
18
  "types": "dist/valbuild-server.cjs.d.ts",
19
- "version": "0.136.2",
19
+ "version": "0.136.4",
20
20
  "devDependencies": {
21
21
  "@prettier/sync": "^0.6.1",
22
22
  "@types/jest": "^30.0.0",
@@ -30,9 +30,9 @@
30
30
  "typescript": "^6.0.3",
31
31
  "zod": "^4.4.3",
32
32
  "zod-validation-error": "^5.0.0",
33
- "@valbuild/shared": "0.136.2",
34
33
  "@valbuild/core": "0.136.1",
35
- "@valbuild/ui": "0.136.2"
34
+ "@valbuild/ui": "0.136.4",
35
+ "@valbuild/shared": "0.136.3"
36
36
  },
37
37
  "engines": {
38
38
  "node": "^20.19.0 || >=22"