@valbuild/core 0.137.0 → 0.138.2
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 +29 -0
- package/dist/declarations/src/index.d.ts +3 -1
- package/dist/declarations/src/source/filename.d.ts +37 -0
- package/dist/declarations/src/source/settings.d.ts +14 -0
- package/dist/{index-42ab6700.esm.js → index-36b5e0cb.esm.js} +113 -14
- package/dist/{index-481a7dcd.cjs.prod.js → index-87b5aa27.cjs.prod.js} +113 -14
- package/dist/{index-fed897a2.cjs.dev.js → index-8e33ef62.cjs.dev.js} +113 -14
- package/dist/valbuild-core.cjs.dev.js +1 -1
- package/dist/valbuild-core.cjs.prod.js +1 -1
- package/dist/valbuild-core.esm.js +1 -1
- package/package.json +1 -1
- package/patch/dist/valbuild-core-patch.cjs.dev.js +1 -1
- package/patch/dist/valbuild-core-patch.cjs.prod.js +1 -1
- package/patch/dist/valbuild-core-patch.esm.js +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# @valbuild/core
|
|
2
2
|
|
|
3
|
+
## 0.138.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#760](https://github.com/valbuild/val/pull/760) [`cf3bbc8`](https://github.com/valbuild/val/commit/cf3bbc8fc89ff349aac5ab101d0b4f28164583e6) Thanks [@freekh](https://github.com/freekh)! - Files and images can now be renamed in the Studio, for local files and remote ones.
|
|
8
|
+
|
|
9
|
+
- **Rename a file in a gallery** from its properties: click the pencil next to the file name. Every field that uses the file is updated to the new name in the same step, including images inside rich text, and the dialog says how many places will change before you save.
|
|
10
|
+
- **Rename the file of an image or file field** with the new **Rename** button. A field that picks its file from a gallery sends you to the gallery instead, because other fields may use the same file.
|
|
11
|
+
- **You choose the name, Val keeps the rest.** The short hash at the end of the name (`_a1b2c`) and the file extension stay as they are, so two different files can never end up with the same name. The name is cleaned up the same way an upload's is: lower case, and characters that do not belong in a URL are dropped.
|
|
12
|
+
- **Renaming a local file moves it.** The file is saved under the new name and the old file is deleted when you publish, so nothing is left behind to clean up. If another field uses the same file without going through a gallery, pick the file again in that field.
|
|
13
|
+
- **Renaming a remote file is instant.** Remote files are stored by their content, so a published remote file only gets a new address and nothing is uploaded again.
|
|
14
|
+
- **Gallery images used in rich text now count as references.** A gallery file used only inside rich text could be deleted, which left the text pointing at nothing. It is now listed under the file's references and blocks the delete like any other use.
|
|
15
|
+
|
|
16
|
+
## 0.138.0
|
|
17
|
+
|
|
18
|
+
### Minor Changes
|
|
19
|
+
|
|
20
|
+
- [#751](https://github.com/valbuild/val/pull/751) [`5876fe0`](https://github.com/valbuild/val/commit/5876fe075fdee1f76f1cdaa2488ac52c58c894a8) Thanks [@freekh](https://github.com/freekh)! - Publishing no longer asks for a commit message by default.
|
|
21
|
+
|
|
22
|
+
- **Publish publishes.** In HTTP mode, pressing Publish goes straight to publishing, just as Save already did in local development. Where an AI model is available it writes the commit message (the button reads "Preparing" meanwhile, for up to 15 seconds). Otherwise the message is built from what changed, and names the exact path when a single field changed, e.g. `Update hero.title in /content/home.val.ts`. When several things changed, the message body lists the changed modules and their fields: up to six modules and five fields each, then how many more.
|
|
23
|
+
- **`studio.commitMessage: "required"`** in `s.settings()` brings the box back, redesigned. Editors can turn it on from the Studio too: Settings → Studio → "Ask for a commit message". It opens empty rather than pre-filled, so a message cannot be published unread. The AI fills it in when it can, and Publish stays disabled until there is a message. Nothing publishes on its own: the countdown that published while the AI was still writing is gone.
|
|
24
|
+
- **The overlay menu stays open** while the commit message popover is open, or a publish is under way. Before, moving onto the popover collapsed the menu and the popover moved with it.
|
|
25
|
+
|
|
26
|
+
### Patch Changes
|
|
27
|
+
|
|
28
|
+
- [#752](https://github.com/valbuild/val/pull/752) [`57af895`](https://github.com/valbuild/val/commit/57af8959cc46519a405b1ed7441623f8bf2e6d27) Thanks [@freekh](https://github.com/freekh)! - `s.date()` now reports a value that is not a real `YYYY-MM-DD` day as a validation error. It used to check only `from` / `to`, and compared those as plain strings, so a typo like `"19f81-12-30"` passed a `.from("1900-01-01").to("2024-01-01")` range without a word. Impossible days such as `"2023-02-29"` are rejected too, and a `from` / `to` bound that is not a valid date is reported as a schema error.
|
|
29
|
+
|
|
30
|
+
The Studio's date field no longer shows a year below 100 (`"0050-01-01"`) as empty: it was read as 1950 and discarded.
|
|
31
|
+
|
|
3
32
|
## 0.137.0
|
|
4
33
|
|
|
5
34
|
### Patch Changes
|
|
@@ -39,6 +39,7 @@ import { isJson, getJsonImport, resolveJsonValues } from "./source/json.js";
|
|
|
39
39
|
import { isExternal, hasInlineExternalEntries } from "./source/external.js";
|
|
40
40
|
import { createExternalFileRef, splitExternalFileRef, isExternalFileRef } from "./source/externalRef.js";
|
|
41
41
|
import { createRemoteRef } from "./source/remote.js";
|
|
42
|
+
import { createRenamedFilename } from "./source/filename.js";
|
|
42
43
|
import { getValidationBasis, getValidationHash } from "./remote/validationBasis.js";
|
|
43
44
|
import { getFileHash, hashToRemoteFileHash } from "./remote/fileHash.js";
|
|
44
45
|
import { splitRemoteRef } from "./remote/splitRemoteRef.js";
|
|
@@ -54,7 +55,7 @@ export { type SerializedImageSchema, ImageSchema } from "./schema/image.js";
|
|
|
54
55
|
export { type SerializedFileSchema, FileSchema } from "./schema/file.js";
|
|
55
56
|
export { type SerializedDateSchema, DateSchema } from "./schema/date.js";
|
|
56
57
|
export { type SerializedSettingsSchema, SettingsSchema, } from "./schema/settings.js";
|
|
57
|
-
export { type SettingsSource, type AssistantSettingsSource, type LocalesSettingsSource, type AssistantAvailability, type ThemeSettingsSource, type StudioSettingsSource, type ThemeRadius, ASSISTANT_SETTINGS_MAX_LENGTH, THEME_LOGO_DIRECTORY, THEME_RADIUS_STEPS, THEME_RADIUS_LENGTHS, assistantAvailability, } from "./source/settings.js";
|
|
58
|
+
export { type SettingsSource, type AssistantSettingsSource, type LocalesSettingsSource, type AssistantAvailability, type ThemeSettingsSource, type StudioSettingsSource, type StudioCommitMessageMode, type ThemeRadius, ASSISTANT_SETTINGS_MAX_LENGTH, THEME_LOGO_DIRECTORY, THEME_RADIUS_STEPS, THEME_RADIUS_LENGTHS, assistantAvailability, } from "./source/settings.js";
|
|
58
59
|
export { type ResolvedSettingsModule, SETTINGS_MODULE_CONVENTION, isRootModuleFilePath, resolveSettingsModule, } from "./settingsModule.js";
|
|
59
60
|
export { type Locale, localeTagError, localeOfValue } from "./locale.js";
|
|
60
61
|
export { type SerializedLocaleSchema, LocaleSchema } from "./schema/locale.js";
|
|
@@ -233,5 +234,6 @@ declare const Internal: {
|
|
|
233
234
|
VAL_STATE_COOKIE: "val_state";
|
|
234
235
|
VAL_SESSION_COOKIE: "val_session";
|
|
235
236
|
createFilename: (data: string | null, filename: string | null, metadata: FileMetadata | ImageMetadata | undefined, sha256: string) => string | null;
|
|
237
|
+
createRenamedFilename: typeof createRenamedFilename;
|
|
236
238
|
};
|
|
237
239
|
export { Internal };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How Val names a media file: a cleaned-up base name, the first five hex of the
|
|
3
|
+
* content's SHA-256, and the extension — `hero-mountains_a1b2c.jpg`.
|
|
4
|
+
*
|
|
5
|
+
* Two producers need this and must agree: an upload (`createFilename`, which
|
|
6
|
+
* has the bytes) and a rename (`createRenamedFilename`, which keeps the bytes
|
|
7
|
+
* and only changes what is in front of the hash). If they disagreed, renaming a
|
|
8
|
+
* file to the name it was uploaded with would give it a different name.
|
|
9
|
+
*/
|
|
10
|
+
/** The `_a1b2c` a Val filename ends in, before the extension. */
|
|
11
|
+
export declare const FILENAME_HASH_SUFFIX_LENGTH = 5;
|
|
12
|
+
/**
|
|
13
|
+
* The part of a filename a person chose, made safe for a URL path.
|
|
14
|
+
*
|
|
15
|
+
* Lower case, with everything `encodeURIComponent` would escape dropped rather
|
|
16
|
+
* than escaped — a path segment that needs decoding is a path segment that gets
|
|
17
|
+
* decoded twice somewhere. Can return the empty string, for a name made only of
|
|
18
|
+
* such characters; a caller that needs a name has to refuse that.
|
|
19
|
+
*/
|
|
20
|
+
export declare function sanitizeFilenameBase(base: string): string;
|
|
21
|
+
/**
|
|
22
|
+
* `base` with a trailing `_<hashPrefix>` removed, so a name that already carries
|
|
23
|
+
* the hash does not get it twice (`photo_a1b2c` + `a1b2c` → `photo_a1b2c`, not
|
|
24
|
+
* `photo_a1b2c_a1b2c`).
|
|
25
|
+
*/
|
|
26
|
+
export declare function stripHashSuffix(base: string, hashPrefix: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* The name a file gets when it is renamed to `newBase`.
|
|
29
|
+
*
|
|
30
|
+
* The hash and the extension are not the editor's to change: the hash keeps two
|
|
31
|
+
* different files from ever sharing a name, and the extension is part of a
|
|
32
|
+
* remote file's validation hash and of how its bytes are served. So they are
|
|
33
|
+
* passed in, and only the base comes from the person.
|
|
34
|
+
*
|
|
35
|
+
* Returns `null` when nothing usable is left of `newBase` after cleaning it.
|
|
36
|
+
*/
|
|
37
|
+
export declare function createRenamedFilename(newBase: string, sha256: string, ext: string): string | null;
|
|
@@ -52,7 +52,21 @@ export type StudioSettingsSource = {
|
|
|
52
52
|
* that browser and is not here.
|
|
53
53
|
*/
|
|
54
54
|
tour?: boolean | null;
|
|
55
|
+
/**
|
|
56
|
+
* Whether publishing asks for a commit message.
|
|
57
|
+
*
|
|
58
|
+
* Unset means `"automatic"`: pressing Publish publishes, with a summary the
|
|
59
|
+
* AI writes where one is available and one assembled from what changed where
|
|
60
|
+
* it is not. Nobody is shown a box. `"required"` opens the box on every
|
|
61
|
+
* publish and will not publish it empty — the AI still fills it in, where
|
|
62
|
+
* there is one, but a person has to read it and press Publish.
|
|
63
|
+
*
|
|
64
|
+
* Only the HTTP (remote) mode commits, so this changes nothing in local
|
|
65
|
+
* development, where Save writes to disk.
|
|
66
|
+
*/
|
|
67
|
+
commitMessage?: StudioCommitMessageMode | null;
|
|
55
68
|
};
|
|
69
|
+
export type StudioCommitMessageMode = "automatic" | "required";
|
|
56
70
|
/**
|
|
57
71
|
* How the Studio looks in this project.
|
|
58
72
|
*
|
|
@@ -8756,7 +8756,8 @@ function settings() {
|
|
|
8756
8756
|
}).nullable().describe("The project's own mark, shown where Val's is in the Studio. A square-ish mark rather than a wordmark: the slot is 32px wide.")
|
|
8757
8757
|
}),
|
|
8758
8758
|
studio: new SettingsSchema({
|
|
8759
|
-
tour: _boolean().nullable().describe("Whether editors are offered the guided tour of the Studio. Unset means yes. Off hides the offer for everyone on this project — the tour stays in Quick actions for anyone who wants it.")
|
|
8759
|
+
tour: _boolean().nullable().describe("Whether editors are offered the guided tour of the Studio. Unset means yes. Off hides the offer for everyone on this project — the tour stays in Quick actions for anyone who wants it."),
|
|
8760
|
+
commitMessage: enumSchema("automatic", "required").nullable().describe("Whether publishing asks for a commit message. Unset is the same as 'automatic': Publish publishes straight away, with a message the AI writes (or one listing what changed). 'required' asks every time, and will not publish an empty one.")
|
|
8760
8761
|
}),
|
|
8761
8762
|
locales: new SettingsSchema({
|
|
8762
8763
|
// Nullable because the source type is, and because the Studio writes
|
|
@@ -10928,7 +10929,7 @@ var DateSchema = /*#__PURE__*/function (_Schema) {
|
|
|
10928
10929
|
}, {
|
|
10929
10930
|
key: "executeValidate",
|
|
10930
10931
|
value: function executeValidate(path, src) {
|
|
10931
|
-
var _this$options, _this$options2, _this$options3, _this$options4;
|
|
10932
|
+
var _this$options, _this$options2, _this$options3, _this$options4, _this$options5, _this$options6;
|
|
10932
10933
|
var errors = this.executeCustomValidateFunctions(src, this.customValidateFunctions, {
|
|
10933
10934
|
path: path
|
|
10934
10935
|
});
|
|
@@ -10942,7 +10943,31 @@ var DateSchema = /*#__PURE__*/function (_Schema) {
|
|
|
10942
10943
|
});
|
|
10943
10944
|
return _defineProperty({}, path, errors);
|
|
10944
10945
|
}
|
|
10945
|
-
|
|
10946
|
+
// The bounds below are compared as strings, which only orders days when
|
|
10947
|
+
// every side is a real `YYYY-MM-DD`: "19f81-12-30" sorts between
|
|
10948
|
+
// "1900-01-01" and "2024-01-01" and would pass them.
|
|
10949
|
+
if (!isCalendarDay(src)) {
|
|
10950
|
+
errors.push({
|
|
10951
|
+
message: "Value '".concat(src, "' is not a valid date (expected YYYY-MM-DD)"),
|
|
10952
|
+
value: src
|
|
10953
|
+
});
|
|
10954
|
+
return _defineProperty({}, path, errors);
|
|
10955
|
+
}
|
|
10956
|
+
var bounds = [["From", (_this$options = this.options) === null || _this$options === void 0 ? void 0 : _this$options.from], ["To", (_this$options2 = this.options) === null || _this$options2 === void 0 ? void 0 : _this$options2.to]];
|
|
10957
|
+
for (var _i = 0, _bounds = bounds; _i < _bounds.length; _i++) {
|
|
10958
|
+
var _bounds$_i = _slicedToArray(_bounds[_i], 2),
|
|
10959
|
+
name = _bounds$_i[0],
|
|
10960
|
+
bound = _bounds$_i[1];
|
|
10961
|
+
if (bound !== undefined && !isCalendarDay(bound)) {
|
|
10962
|
+
errors.push({
|
|
10963
|
+
message: "".concat(name, " date '").concat(bound, "' is not a valid date (expected YYYY-MM-DD)"),
|
|
10964
|
+
value: src,
|
|
10965
|
+
typeError: true
|
|
10966
|
+
});
|
|
10967
|
+
return _defineProperty({}, path, errors);
|
|
10968
|
+
}
|
|
10969
|
+
}
|
|
10970
|
+
if ((_this$options3 = this.options) !== null && _this$options3 !== void 0 && _this$options3.from && (_this$options4 = this.options) !== null && _this$options4 !== void 0 && _this$options4.to) {
|
|
10946
10971
|
if (this.options.from > this.options.to) {
|
|
10947
10972
|
errors.push({
|
|
10948
10973
|
message: "From date ".concat(this.options.from, " is after to date ").concat(this.options.to),
|
|
@@ -10955,14 +10980,14 @@ var DateSchema = /*#__PURE__*/function (_Schema) {
|
|
|
10955
10980
|
value: src
|
|
10956
10981
|
});
|
|
10957
10982
|
}
|
|
10958
|
-
} else if ((_this$
|
|
10983
|
+
} else if ((_this$options5 = this.options) !== null && _this$options5 !== void 0 && _this$options5.from) {
|
|
10959
10984
|
if (src < this.options.from) {
|
|
10960
10985
|
errors.push({
|
|
10961
10986
|
message: "Date is before the minimum date ".concat(this.options.from),
|
|
10962
10987
|
value: src
|
|
10963
10988
|
});
|
|
10964
10989
|
}
|
|
10965
|
-
} else if ((_this$
|
|
10990
|
+
} else if ((_this$options6 = this.options) !== null && _this$options6 !== void 0 && _this$options6.to) {
|
|
10966
10991
|
if (src > this.options.to) {
|
|
10967
10992
|
errors.push({
|
|
10968
10993
|
message: "Date is after the maximum date ".concat(this.options.to),
|
|
@@ -11132,6 +11157,27 @@ var DateSchema = /*#__PURE__*/function (_Schema) {
|
|
|
11132
11157
|
}
|
|
11133
11158
|
}]);
|
|
11134
11159
|
}(Schema);
|
|
11160
|
+
var CALENDAR_DAY = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
11161
|
+
|
|
11162
|
+
/**
|
|
11163
|
+
* Whether `value` is a `YYYY-MM-DD` day that exists on the calendar — so
|
|
11164
|
+
* "2023-02-29" and "2024-13-01" are not, even though they have the shape.
|
|
11165
|
+
*/
|
|
11166
|
+
function isCalendarDay(value) {
|
|
11167
|
+
var match = CALENDAR_DAY.exec(value);
|
|
11168
|
+
if (!match) {
|
|
11169
|
+
return false;
|
|
11170
|
+
}
|
|
11171
|
+
var year = Number(match[1]);
|
|
11172
|
+
var month = Number(match[2]);
|
|
11173
|
+
var day = Number(match[3]);
|
|
11174
|
+
// Out-of-range fields roll over (Feb 30 -> Mar 2) instead of failing, so a
|
|
11175
|
+
// day is real only if it survives the round trip. setUTCFullYear, not
|
|
11176
|
+
// Date.UTC: the latter maps years 0-99 to 1900-1999.
|
|
11177
|
+
var date = new Date(0);
|
|
11178
|
+
date.setUTCFullYear(year, month - 1, day);
|
|
11179
|
+
return date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day;
|
|
11180
|
+
}
|
|
11135
11181
|
var date = function date(options) {
|
|
11136
11182
|
return new DateSchema(options);
|
|
11137
11183
|
};
|
|
@@ -13311,6 +13357,63 @@ function createRemoteRef(remoteHost, _ref) {
|
|
|
13311
13357
|
return "".concat(remoteHost, "/file/p/").concat(publicProjectId, "/b/").concat(bucket, "/v/").concat(coreVersion, "/h/").concat(validationHash, "/f/").concat(fileHash, "/p/").concat(filePath);
|
|
13312
13358
|
}
|
|
13313
13359
|
|
|
13360
|
+
/**
|
|
13361
|
+
* How Val names a media file: a cleaned-up base name, the first five hex of the
|
|
13362
|
+
* content's SHA-256, and the extension — `hero-mountains_a1b2c.jpg`.
|
|
13363
|
+
*
|
|
13364
|
+
* Two producers need this and must agree: an upload (`createFilename`, which
|
|
13365
|
+
* has the bytes) and a rename (`createRenamedFilename`, which keeps the bytes
|
|
13366
|
+
* and only changes what is in front of the hash). If they disagreed, renaming a
|
|
13367
|
+
* file to the name it was uploaded with would give it a different name.
|
|
13368
|
+
*/
|
|
13369
|
+
|
|
13370
|
+
/** The `_a1b2c` a Val filename ends in, before the extension. */
|
|
13371
|
+
var FILENAME_HASH_SUFFIX_LENGTH = 5;
|
|
13372
|
+
|
|
13373
|
+
/**
|
|
13374
|
+
* The part of a filename a person chose, made safe for a URL path.
|
|
13375
|
+
*
|
|
13376
|
+
* Lower case, with everything `encodeURIComponent` would escape dropped rather
|
|
13377
|
+
* than escaped — a path segment that needs decoding is a path segment that gets
|
|
13378
|
+
* decoded twice somewhere. Can return the empty string, for a name made only of
|
|
13379
|
+
* such characters; a caller that needs a name has to refuse that.
|
|
13380
|
+
*/
|
|
13381
|
+
function sanitizeFilenameBase(base) {
|
|
13382
|
+
return encodeURIComponent(base).replace(/%[0-9A-Fa-f]{2}/g, "").toLowerCase();
|
|
13383
|
+
}
|
|
13384
|
+
|
|
13385
|
+
/**
|
|
13386
|
+
* `base` with a trailing `_<hashPrefix>` removed, so a name that already carries
|
|
13387
|
+
* the hash does not get it twice (`photo_a1b2c` + `a1b2c` → `photo_a1b2c`, not
|
|
13388
|
+
* `photo_a1b2c_a1b2c`).
|
|
13389
|
+
*/
|
|
13390
|
+
function stripHashSuffix(base, hashPrefix) {
|
|
13391
|
+
var pos = base.lastIndexOf("_");
|
|
13392
|
+
if (pos !== -1 && base.slice(pos + 1) === hashPrefix) {
|
|
13393
|
+
return base.slice(0, pos);
|
|
13394
|
+
}
|
|
13395
|
+
return base;
|
|
13396
|
+
}
|
|
13397
|
+
|
|
13398
|
+
/**
|
|
13399
|
+
* The name a file gets when it is renamed to `newBase`.
|
|
13400
|
+
*
|
|
13401
|
+
* The hash and the extension are not the editor's to change: the hash keeps two
|
|
13402
|
+
* different files from ever sharing a name, and the extension is part of a
|
|
13403
|
+
* remote file's validation hash and of how its bytes are served. So they are
|
|
13404
|
+
* passed in, and only the base comes from the person.
|
|
13405
|
+
*
|
|
13406
|
+
* Returns `null` when nothing usable is left of `newBase` after cleaning it.
|
|
13407
|
+
*/
|
|
13408
|
+
function createRenamedFilename(newBase, sha256, ext) {
|
|
13409
|
+
var hashPrefix = sha256.slice(0, FILENAME_HASH_SUFFIX_LENGTH);
|
|
13410
|
+
var cleaned = sanitizeFilenameBase(stripHashSuffix(newBase.trim(), hashPrefix));
|
|
13411
|
+
if (cleaned === "") {
|
|
13412
|
+
return null;
|
|
13413
|
+
}
|
|
13414
|
+
return ext === "" ? "".concat(cleaned, "_").concat(hashPrefix) : "".concat(cleaned, "_").concat(hashPrefix, ".").concat(ext);
|
|
13415
|
+
}
|
|
13416
|
+
|
|
13314
13417
|
/**
|
|
13315
13418
|
* The validation basis is used in remote refs to determine if the remote content needs to be re-validated.
|
|
13316
13419
|
*
|
|
@@ -14339,7 +14442,7 @@ function isPageRouter(routerId) {
|
|
|
14339
14442
|
|
|
14340
14443
|
var corePackageJson = {
|
|
14341
14444
|
name: "@valbuild/core",
|
|
14342
|
-
version: "0.
|
|
14445
|
+
version: "0.138.2",
|
|
14343
14446
|
"private": false,
|
|
14344
14447
|
description: "Val - supercharged hard-coded content",
|
|
14345
14448
|
repository: {
|
|
@@ -14526,21 +14629,17 @@ var Internal = {
|
|
|
14526
14629
|
if (!data) {
|
|
14527
14630
|
return filename;
|
|
14528
14631
|
}
|
|
14529
|
-
var shaSuffix = sha256.slice(0,
|
|
14632
|
+
var shaSuffix = sha256.slice(0, FILENAME_HASH_SUFFIX_LENGTH);
|
|
14530
14633
|
var mimeType = (_Internal$getMimeType = Internal.getMimeType(data)) !== null && _Internal$getMimeType !== void 0 ? _Internal$getMimeType : "unknown";
|
|
14531
14634
|
var newExt = (_Internal$mimeTypeToF = Internal.mimeTypeToFileExt(mimeType)) !== null && _Internal$mimeTypeToF !== void 0 ? _Internal$mimeTypeToF : "unknown"; // Don't trust the file extension
|
|
14532
14635
|
if (filename) {
|
|
14533
14636
|
var cleanFilename = filename.split(".").slice(0, -1).join(".") || filename; // remove extension if it exists
|
|
14534
|
-
var
|
|
14535
|
-
var currentShaSuffix = cleanFilename.slice(maybeShaSuffixPos + 1, cleanFilename.length);
|
|
14536
|
-
if (currentShaSuffix === shaSuffix) {
|
|
14537
|
-
cleanFilename = cleanFilename.slice(0, maybeShaSuffixPos);
|
|
14538
|
-
}
|
|
14539
|
-
var escapedFilename = encodeURIComponent(cleanFilename).replace(/%[0-9A-Fa-f]{2}/g, "").toLowerCase();
|
|
14637
|
+
var escapedFilename = sanitizeFilenameBase(stripHashSuffix(cleanFilename, shaSuffix));
|
|
14540
14638
|
return "".concat(escapedFilename, "_").concat(shaSuffix, ".").concat(newExt);
|
|
14541
14639
|
}
|
|
14542
14640
|
return "".concat(sha256, ".").concat(newExt);
|
|
14543
|
-
}
|
|
14641
|
+
},
|
|
14642
|
+
createRenamedFilename: createRenamedFilename
|
|
14544
14643
|
};
|
|
14545
14644
|
function tryJsonParse(str) {
|
|
14546
14645
|
try {
|
|
@@ -8758,7 +8758,8 @@ function settings() {
|
|
|
8758
8758
|
}).nullable().describe("The project's own mark, shown where Val's is in the Studio. A square-ish mark rather than a wordmark: the slot is 32px wide.")
|
|
8759
8759
|
}),
|
|
8760
8760
|
studio: new SettingsSchema({
|
|
8761
|
-
tour: _boolean().nullable().describe("Whether editors are offered the guided tour of the Studio. Unset means yes. Off hides the offer for everyone on this project — the tour stays in Quick actions for anyone who wants it.")
|
|
8761
|
+
tour: _boolean().nullable().describe("Whether editors are offered the guided tour of the Studio. Unset means yes. Off hides the offer for everyone on this project — the tour stays in Quick actions for anyone who wants it."),
|
|
8762
|
+
commitMessage: enumSchema("automatic", "required").nullable().describe("Whether publishing asks for a commit message. Unset is the same as 'automatic': Publish publishes straight away, with a message the AI writes (or one listing what changed). 'required' asks every time, and will not publish an empty one.")
|
|
8762
8763
|
}),
|
|
8763
8764
|
locales: new SettingsSchema({
|
|
8764
8765
|
// Nullable because the source type is, and because the Studio writes
|
|
@@ -10930,7 +10931,7 @@ var DateSchema = /*#__PURE__*/function (_Schema) {
|
|
|
10930
10931
|
}, {
|
|
10931
10932
|
key: "executeValidate",
|
|
10932
10933
|
value: function executeValidate(path, src) {
|
|
10933
|
-
var _this$options, _this$options2, _this$options3, _this$options4;
|
|
10934
|
+
var _this$options, _this$options2, _this$options3, _this$options4, _this$options5, _this$options6;
|
|
10934
10935
|
var errors = this.executeCustomValidateFunctions(src, this.customValidateFunctions, {
|
|
10935
10936
|
path: path
|
|
10936
10937
|
});
|
|
@@ -10944,7 +10945,31 @@ var DateSchema = /*#__PURE__*/function (_Schema) {
|
|
|
10944
10945
|
});
|
|
10945
10946
|
return _defineProperty({}, path, errors);
|
|
10946
10947
|
}
|
|
10947
|
-
|
|
10948
|
+
// The bounds below are compared as strings, which only orders days when
|
|
10949
|
+
// every side is a real `YYYY-MM-DD`: "19f81-12-30" sorts between
|
|
10950
|
+
// "1900-01-01" and "2024-01-01" and would pass them.
|
|
10951
|
+
if (!isCalendarDay(src)) {
|
|
10952
|
+
errors.push({
|
|
10953
|
+
message: "Value '".concat(src, "' is not a valid date (expected YYYY-MM-DD)"),
|
|
10954
|
+
value: src
|
|
10955
|
+
});
|
|
10956
|
+
return _defineProperty({}, path, errors);
|
|
10957
|
+
}
|
|
10958
|
+
var bounds = [["From", (_this$options = this.options) === null || _this$options === void 0 ? void 0 : _this$options.from], ["To", (_this$options2 = this.options) === null || _this$options2 === void 0 ? void 0 : _this$options2.to]];
|
|
10959
|
+
for (var _i = 0, _bounds = bounds; _i < _bounds.length; _i++) {
|
|
10960
|
+
var _bounds$_i = _slicedToArray(_bounds[_i], 2),
|
|
10961
|
+
name = _bounds$_i[0],
|
|
10962
|
+
bound = _bounds$_i[1];
|
|
10963
|
+
if (bound !== undefined && !isCalendarDay(bound)) {
|
|
10964
|
+
errors.push({
|
|
10965
|
+
message: "".concat(name, " date '").concat(bound, "' is not a valid date (expected YYYY-MM-DD)"),
|
|
10966
|
+
value: src,
|
|
10967
|
+
typeError: true
|
|
10968
|
+
});
|
|
10969
|
+
return _defineProperty({}, path, errors);
|
|
10970
|
+
}
|
|
10971
|
+
}
|
|
10972
|
+
if ((_this$options3 = this.options) !== null && _this$options3 !== void 0 && _this$options3.from && (_this$options4 = this.options) !== null && _this$options4 !== void 0 && _this$options4.to) {
|
|
10948
10973
|
if (this.options.from > this.options.to) {
|
|
10949
10974
|
errors.push({
|
|
10950
10975
|
message: "From date ".concat(this.options.from, " is after to date ").concat(this.options.to),
|
|
@@ -10957,14 +10982,14 @@ var DateSchema = /*#__PURE__*/function (_Schema) {
|
|
|
10957
10982
|
value: src
|
|
10958
10983
|
});
|
|
10959
10984
|
}
|
|
10960
|
-
} else if ((_this$
|
|
10985
|
+
} else if ((_this$options5 = this.options) !== null && _this$options5 !== void 0 && _this$options5.from) {
|
|
10961
10986
|
if (src < this.options.from) {
|
|
10962
10987
|
errors.push({
|
|
10963
10988
|
message: "Date is before the minimum date ".concat(this.options.from),
|
|
10964
10989
|
value: src
|
|
10965
10990
|
});
|
|
10966
10991
|
}
|
|
10967
|
-
} else if ((_this$
|
|
10992
|
+
} else if ((_this$options6 = this.options) !== null && _this$options6 !== void 0 && _this$options6.to) {
|
|
10968
10993
|
if (src > this.options.to) {
|
|
10969
10994
|
errors.push({
|
|
10970
10995
|
message: "Date is after the maximum date ".concat(this.options.to),
|
|
@@ -11134,6 +11159,27 @@ var DateSchema = /*#__PURE__*/function (_Schema) {
|
|
|
11134
11159
|
}
|
|
11135
11160
|
}]);
|
|
11136
11161
|
}(Schema);
|
|
11162
|
+
var CALENDAR_DAY = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
11163
|
+
|
|
11164
|
+
/**
|
|
11165
|
+
* Whether `value` is a `YYYY-MM-DD` day that exists on the calendar — so
|
|
11166
|
+
* "2023-02-29" and "2024-13-01" are not, even though they have the shape.
|
|
11167
|
+
*/
|
|
11168
|
+
function isCalendarDay(value) {
|
|
11169
|
+
var match = CALENDAR_DAY.exec(value);
|
|
11170
|
+
if (!match) {
|
|
11171
|
+
return false;
|
|
11172
|
+
}
|
|
11173
|
+
var year = Number(match[1]);
|
|
11174
|
+
var month = Number(match[2]);
|
|
11175
|
+
var day = Number(match[3]);
|
|
11176
|
+
// Out-of-range fields roll over (Feb 30 -> Mar 2) instead of failing, so a
|
|
11177
|
+
// day is real only if it survives the round trip. setUTCFullYear, not
|
|
11178
|
+
// Date.UTC: the latter maps years 0-99 to 1900-1999.
|
|
11179
|
+
var date = new Date(0);
|
|
11180
|
+
date.setUTCFullYear(year, month - 1, day);
|
|
11181
|
+
return date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day;
|
|
11182
|
+
}
|
|
11137
11183
|
var date = function date(options) {
|
|
11138
11184
|
return new DateSchema(options);
|
|
11139
11185
|
};
|
|
@@ -13313,6 +13359,63 @@ function createRemoteRef(remoteHost, _ref) {
|
|
|
13313
13359
|
return "".concat(remoteHost, "/file/p/").concat(publicProjectId, "/b/").concat(bucket, "/v/").concat(coreVersion, "/h/").concat(validationHash, "/f/").concat(fileHash, "/p/").concat(filePath);
|
|
13314
13360
|
}
|
|
13315
13361
|
|
|
13362
|
+
/**
|
|
13363
|
+
* How Val names a media file: a cleaned-up base name, the first five hex of the
|
|
13364
|
+
* content's SHA-256, and the extension — `hero-mountains_a1b2c.jpg`.
|
|
13365
|
+
*
|
|
13366
|
+
* Two producers need this and must agree: an upload (`createFilename`, which
|
|
13367
|
+
* has the bytes) and a rename (`createRenamedFilename`, which keeps the bytes
|
|
13368
|
+
* and only changes what is in front of the hash). If they disagreed, renaming a
|
|
13369
|
+
* file to the name it was uploaded with would give it a different name.
|
|
13370
|
+
*/
|
|
13371
|
+
|
|
13372
|
+
/** The `_a1b2c` a Val filename ends in, before the extension. */
|
|
13373
|
+
var FILENAME_HASH_SUFFIX_LENGTH = 5;
|
|
13374
|
+
|
|
13375
|
+
/**
|
|
13376
|
+
* The part of a filename a person chose, made safe for a URL path.
|
|
13377
|
+
*
|
|
13378
|
+
* Lower case, with everything `encodeURIComponent` would escape dropped rather
|
|
13379
|
+
* than escaped — a path segment that needs decoding is a path segment that gets
|
|
13380
|
+
* decoded twice somewhere. Can return the empty string, for a name made only of
|
|
13381
|
+
* such characters; a caller that needs a name has to refuse that.
|
|
13382
|
+
*/
|
|
13383
|
+
function sanitizeFilenameBase(base) {
|
|
13384
|
+
return encodeURIComponent(base).replace(/%[0-9A-Fa-f]{2}/g, "").toLowerCase();
|
|
13385
|
+
}
|
|
13386
|
+
|
|
13387
|
+
/**
|
|
13388
|
+
* `base` with a trailing `_<hashPrefix>` removed, so a name that already carries
|
|
13389
|
+
* the hash does not get it twice (`photo_a1b2c` + `a1b2c` → `photo_a1b2c`, not
|
|
13390
|
+
* `photo_a1b2c_a1b2c`).
|
|
13391
|
+
*/
|
|
13392
|
+
function stripHashSuffix(base, hashPrefix) {
|
|
13393
|
+
var pos = base.lastIndexOf("_");
|
|
13394
|
+
if (pos !== -1 && base.slice(pos + 1) === hashPrefix) {
|
|
13395
|
+
return base.slice(0, pos);
|
|
13396
|
+
}
|
|
13397
|
+
return base;
|
|
13398
|
+
}
|
|
13399
|
+
|
|
13400
|
+
/**
|
|
13401
|
+
* The name a file gets when it is renamed to `newBase`.
|
|
13402
|
+
*
|
|
13403
|
+
* The hash and the extension are not the editor's to change: the hash keeps two
|
|
13404
|
+
* different files from ever sharing a name, and the extension is part of a
|
|
13405
|
+
* remote file's validation hash and of how its bytes are served. So they are
|
|
13406
|
+
* passed in, and only the base comes from the person.
|
|
13407
|
+
*
|
|
13408
|
+
* Returns `null` when nothing usable is left of `newBase` after cleaning it.
|
|
13409
|
+
*/
|
|
13410
|
+
function createRenamedFilename(newBase, sha256, ext) {
|
|
13411
|
+
var hashPrefix = sha256.slice(0, FILENAME_HASH_SUFFIX_LENGTH);
|
|
13412
|
+
var cleaned = sanitizeFilenameBase(stripHashSuffix(newBase.trim(), hashPrefix));
|
|
13413
|
+
if (cleaned === "") {
|
|
13414
|
+
return null;
|
|
13415
|
+
}
|
|
13416
|
+
return ext === "" ? "".concat(cleaned, "_").concat(hashPrefix) : "".concat(cleaned, "_").concat(hashPrefix, ".").concat(ext);
|
|
13417
|
+
}
|
|
13418
|
+
|
|
13316
13419
|
/**
|
|
13317
13420
|
* The validation basis is used in remote refs to determine if the remote content needs to be re-validated.
|
|
13318
13421
|
*
|
|
@@ -14341,7 +14444,7 @@ function isPageRouter(routerId) {
|
|
|
14341
14444
|
|
|
14342
14445
|
var corePackageJson = {
|
|
14343
14446
|
name: "@valbuild/core",
|
|
14344
|
-
version: "0.
|
|
14447
|
+
version: "0.138.2",
|
|
14345
14448
|
"private": false,
|
|
14346
14449
|
description: "Val - supercharged hard-coded content",
|
|
14347
14450
|
repository: {
|
|
@@ -14528,21 +14631,17 @@ var Internal = {
|
|
|
14528
14631
|
if (!data) {
|
|
14529
14632
|
return filename;
|
|
14530
14633
|
}
|
|
14531
|
-
var shaSuffix = sha256.slice(0,
|
|
14634
|
+
var shaSuffix = sha256.slice(0, FILENAME_HASH_SUFFIX_LENGTH);
|
|
14532
14635
|
var mimeType = (_Internal$getMimeType = Internal.getMimeType(data)) !== null && _Internal$getMimeType !== void 0 ? _Internal$getMimeType : "unknown";
|
|
14533
14636
|
var newExt = (_Internal$mimeTypeToF = Internal.mimeTypeToFileExt(mimeType)) !== null && _Internal$mimeTypeToF !== void 0 ? _Internal$mimeTypeToF : "unknown"; // Don't trust the file extension
|
|
14534
14637
|
if (filename) {
|
|
14535
14638
|
var cleanFilename = filename.split(".").slice(0, -1).join(".") || filename; // remove extension if it exists
|
|
14536
|
-
var
|
|
14537
|
-
var currentShaSuffix = cleanFilename.slice(maybeShaSuffixPos + 1, cleanFilename.length);
|
|
14538
|
-
if (currentShaSuffix === shaSuffix) {
|
|
14539
|
-
cleanFilename = cleanFilename.slice(0, maybeShaSuffixPos);
|
|
14540
|
-
}
|
|
14541
|
-
var escapedFilename = encodeURIComponent(cleanFilename).replace(/%[0-9A-Fa-f]{2}/g, "").toLowerCase();
|
|
14639
|
+
var escapedFilename = sanitizeFilenameBase(stripHashSuffix(cleanFilename, shaSuffix));
|
|
14542
14640
|
return "".concat(escapedFilename, "_").concat(shaSuffix, ".").concat(newExt);
|
|
14543
14641
|
}
|
|
14544
14642
|
return "".concat(sha256, ".").concat(newExt);
|
|
14545
|
-
}
|
|
14643
|
+
},
|
|
14644
|
+
createRenamedFilename: createRenamedFilename
|
|
14546
14645
|
};
|
|
14547
14646
|
function tryJsonParse(str) {
|
|
14548
14647
|
try {
|
|
@@ -8758,7 +8758,8 @@ function settings() {
|
|
|
8758
8758
|
}).nullable().describe("The project's own mark, shown where Val's is in the Studio. A square-ish mark rather than a wordmark: the slot is 32px wide.")
|
|
8759
8759
|
}),
|
|
8760
8760
|
studio: new SettingsSchema({
|
|
8761
|
-
tour: _boolean().nullable().describe("Whether editors are offered the guided tour of the Studio. Unset means yes. Off hides the offer for everyone on this project — the tour stays in Quick actions for anyone who wants it.")
|
|
8761
|
+
tour: _boolean().nullable().describe("Whether editors are offered the guided tour of the Studio. Unset means yes. Off hides the offer for everyone on this project — the tour stays in Quick actions for anyone who wants it."),
|
|
8762
|
+
commitMessage: enumSchema("automatic", "required").nullable().describe("Whether publishing asks for a commit message. Unset is the same as 'automatic': Publish publishes straight away, with a message the AI writes (or one listing what changed). 'required' asks every time, and will not publish an empty one.")
|
|
8762
8763
|
}),
|
|
8763
8764
|
locales: new SettingsSchema({
|
|
8764
8765
|
// Nullable because the source type is, and because the Studio writes
|
|
@@ -10930,7 +10931,7 @@ var DateSchema = /*#__PURE__*/function (_Schema) {
|
|
|
10930
10931
|
}, {
|
|
10931
10932
|
key: "executeValidate",
|
|
10932
10933
|
value: function executeValidate(path, src) {
|
|
10933
|
-
var _this$options, _this$options2, _this$options3, _this$options4;
|
|
10934
|
+
var _this$options, _this$options2, _this$options3, _this$options4, _this$options5, _this$options6;
|
|
10934
10935
|
var errors = this.executeCustomValidateFunctions(src, this.customValidateFunctions, {
|
|
10935
10936
|
path: path
|
|
10936
10937
|
});
|
|
@@ -10944,7 +10945,31 @@ var DateSchema = /*#__PURE__*/function (_Schema) {
|
|
|
10944
10945
|
});
|
|
10945
10946
|
return _defineProperty({}, path, errors);
|
|
10946
10947
|
}
|
|
10947
|
-
|
|
10948
|
+
// The bounds below are compared as strings, which only orders days when
|
|
10949
|
+
// every side is a real `YYYY-MM-DD`: "19f81-12-30" sorts between
|
|
10950
|
+
// "1900-01-01" and "2024-01-01" and would pass them.
|
|
10951
|
+
if (!isCalendarDay(src)) {
|
|
10952
|
+
errors.push({
|
|
10953
|
+
message: "Value '".concat(src, "' is not a valid date (expected YYYY-MM-DD)"),
|
|
10954
|
+
value: src
|
|
10955
|
+
});
|
|
10956
|
+
return _defineProperty({}, path, errors);
|
|
10957
|
+
}
|
|
10958
|
+
var bounds = [["From", (_this$options = this.options) === null || _this$options === void 0 ? void 0 : _this$options.from], ["To", (_this$options2 = this.options) === null || _this$options2 === void 0 ? void 0 : _this$options2.to]];
|
|
10959
|
+
for (var _i = 0, _bounds = bounds; _i < _bounds.length; _i++) {
|
|
10960
|
+
var _bounds$_i = _slicedToArray(_bounds[_i], 2),
|
|
10961
|
+
name = _bounds$_i[0],
|
|
10962
|
+
bound = _bounds$_i[1];
|
|
10963
|
+
if (bound !== undefined && !isCalendarDay(bound)) {
|
|
10964
|
+
errors.push({
|
|
10965
|
+
message: "".concat(name, " date '").concat(bound, "' is not a valid date (expected YYYY-MM-DD)"),
|
|
10966
|
+
value: src,
|
|
10967
|
+
typeError: true
|
|
10968
|
+
});
|
|
10969
|
+
return _defineProperty({}, path, errors);
|
|
10970
|
+
}
|
|
10971
|
+
}
|
|
10972
|
+
if ((_this$options3 = this.options) !== null && _this$options3 !== void 0 && _this$options3.from && (_this$options4 = this.options) !== null && _this$options4 !== void 0 && _this$options4.to) {
|
|
10948
10973
|
if (this.options.from > this.options.to) {
|
|
10949
10974
|
errors.push({
|
|
10950
10975
|
message: "From date ".concat(this.options.from, " is after to date ").concat(this.options.to),
|
|
@@ -10957,14 +10982,14 @@ var DateSchema = /*#__PURE__*/function (_Schema) {
|
|
|
10957
10982
|
value: src
|
|
10958
10983
|
});
|
|
10959
10984
|
}
|
|
10960
|
-
} else if ((_this$
|
|
10985
|
+
} else if ((_this$options5 = this.options) !== null && _this$options5 !== void 0 && _this$options5.from) {
|
|
10961
10986
|
if (src < this.options.from) {
|
|
10962
10987
|
errors.push({
|
|
10963
10988
|
message: "Date is before the minimum date ".concat(this.options.from),
|
|
10964
10989
|
value: src
|
|
10965
10990
|
});
|
|
10966
10991
|
}
|
|
10967
|
-
} else if ((_this$
|
|
10992
|
+
} else if ((_this$options6 = this.options) !== null && _this$options6 !== void 0 && _this$options6.to) {
|
|
10968
10993
|
if (src > this.options.to) {
|
|
10969
10994
|
errors.push({
|
|
10970
10995
|
message: "Date is after the maximum date ".concat(this.options.to),
|
|
@@ -11134,6 +11159,27 @@ var DateSchema = /*#__PURE__*/function (_Schema) {
|
|
|
11134
11159
|
}
|
|
11135
11160
|
}]);
|
|
11136
11161
|
}(Schema);
|
|
11162
|
+
var CALENDAR_DAY = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
11163
|
+
|
|
11164
|
+
/**
|
|
11165
|
+
* Whether `value` is a `YYYY-MM-DD` day that exists on the calendar — so
|
|
11166
|
+
* "2023-02-29" and "2024-13-01" are not, even though they have the shape.
|
|
11167
|
+
*/
|
|
11168
|
+
function isCalendarDay(value) {
|
|
11169
|
+
var match = CALENDAR_DAY.exec(value);
|
|
11170
|
+
if (!match) {
|
|
11171
|
+
return false;
|
|
11172
|
+
}
|
|
11173
|
+
var year = Number(match[1]);
|
|
11174
|
+
var month = Number(match[2]);
|
|
11175
|
+
var day = Number(match[3]);
|
|
11176
|
+
// Out-of-range fields roll over (Feb 30 -> Mar 2) instead of failing, so a
|
|
11177
|
+
// day is real only if it survives the round trip. setUTCFullYear, not
|
|
11178
|
+
// Date.UTC: the latter maps years 0-99 to 1900-1999.
|
|
11179
|
+
var date = new Date(0);
|
|
11180
|
+
date.setUTCFullYear(year, month - 1, day);
|
|
11181
|
+
return date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day;
|
|
11182
|
+
}
|
|
11137
11183
|
var date = function date(options) {
|
|
11138
11184
|
return new DateSchema(options);
|
|
11139
11185
|
};
|
|
@@ -13313,6 +13359,63 @@ function createRemoteRef(remoteHost, _ref) {
|
|
|
13313
13359
|
return "".concat(remoteHost, "/file/p/").concat(publicProjectId, "/b/").concat(bucket, "/v/").concat(coreVersion, "/h/").concat(validationHash, "/f/").concat(fileHash, "/p/").concat(filePath);
|
|
13314
13360
|
}
|
|
13315
13361
|
|
|
13362
|
+
/**
|
|
13363
|
+
* How Val names a media file: a cleaned-up base name, the first five hex of the
|
|
13364
|
+
* content's SHA-256, and the extension — `hero-mountains_a1b2c.jpg`.
|
|
13365
|
+
*
|
|
13366
|
+
* Two producers need this and must agree: an upload (`createFilename`, which
|
|
13367
|
+
* has the bytes) and a rename (`createRenamedFilename`, which keeps the bytes
|
|
13368
|
+
* and only changes what is in front of the hash). If they disagreed, renaming a
|
|
13369
|
+
* file to the name it was uploaded with would give it a different name.
|
|
13370
|
+
*/
|
|
13371
|
+
|
|
13372
|
+
/** The `_a1b2c` a Val filename ends in, before the extension. */
|
|
13373
|
+
var FILENAME_HASH_SUFFIX_LENGTH = 5;
|
|
13374
|
+
|
|
13375
|
+
/**
|
|
13376
|
+
* The part of a filename a person chose, made safe for a URL path.
|
|
13377
|
+
*
|
|
13378
|
+
* Lower case, with everything `encodeURIComponent` would escape dropped rather
|
|
13379
|
+
* than escaped — a path segment that needs decoding is a path segment that gets
|
|
13380
|
+
* decoded twice somewhere. Can return the empty string, for a name made only of
|
|
13381
|
+
* such characters; a caller that needs a name has to refuse that.
|
|
13382
|
+
*/
|
|
13383
|
+
function sanitizeFilenameBase(base) {
|
|
13384
|
+
return encodeURIComponent(base).replace(/%[0-9A-Fa-f]{2}/g, "").toLowerCase();
|
|
13385
|
+
}
|
|
13386
|
+
|
|
13387
|
+
/**
|
|
13388
|
+
* `base` with a trailing `_<hashPrefix>` removed, so a name that already carries
|
|
13389
|
+
* the hash does not get it twice (`photo_a1b2c` + `a1b2c` → `photo_a1b2c`, not
|
|
13390
|
+
* `photo_a1b2c_a1b2c`).
|
|
13391
|
+
*/
|
|
13392
|
+
function stripHashSuffix(base, hashPrefix) {
|
|
13393
|
+
var pos = base.lastIndexOf("_");
|
|
13394
|
+
if (pos !== -1 && base.slice(pos + 1) === hashPrefix) {
|
|
13395
|
+
return base.slice(0, pos);
|
|
13396
|
+
}
|
|
13397
|
+
return base;
|
|
13398
|
+
}
|
|
13399
|
+
|
|
13400
|
+
/**
|
|
13401
|
+
* The name a file gets when it is renamed to `newBase`.
|
|
13402
|
+
*
|
|
13403
|
+
* The hash and the extension are not the editor's to change: the hash keeps two
|
|
13404
|
+
* different files from ever sharing a name, and the extension is part of a
|
|
13405
|
+
* remote file's validation hash and of how its bytes are served. So they are
|
|
13406
|
+
* passed in, and only the base comes from the person.
|
|
13407
|
+
*
|
|
13408
|
+
* Returns `null` when nothing usable is left of `newBase` after cleaning it.
|
|
13409
|
+
*/
|
|
13410
|
+
function createRenamedFilename(newBase, sha256, ext) {
|
|
13411
|
+
var hashPrefix = sha256.slice(0, FILENAME_HASH_SUFFIX_LENGTH);
|
|
13412
|
+
var cleaned = sanitizeFilenameBase(stripHashSuffix(newBase.trim(), hashPrefix));
|
|
13413
|
+
if (cleaned === "") {
|
|
13414
|
+
return null;
|
|
13415
|
+
}
|
|
13416
|
+
return ext === "" ? "".concat(cleaned, "_").concat(hashPrefix) : "".concat(cleaned, "_").concat(hashPrefix, ".").concat(ext);
|
|
13417
|
+
}
|
|
13418
|
+
|
|
13316
13419
|
/**
|
|
13317
13420
|
* The validation basis is used in remote refs to determine if the remote content needs to be re-validated.
|
|
13318
13421
|
*
|
|
@@ -14341,7 +14444,7 @@ function isPageRouter(routerId) {
|
|
|
14341
14444
|
|
|
14342
14445
|
var corePackageJson = {
|
|
14343
14446
|
name: "@valbuild/core",
|
|
14344
|
-
version: "0.
|
|
14447
|
+
version: "0.138.2",
|
|
14345
14448
|
"private": false,
|
|
14346
14449
|
description: "Val - supercharged hard-coded content",
|
|
14347
14450
|
repository: {
|
|
@@ -14528,21 +14631,17 @@ var Internal = {
|
|
|
14528
14631
|
if (!data) {
|
|
14529
14632
|
return filename;
|
|
14530
14633
|
}
|
|
14531
|
-
var shaSuffix = sha256.slice(0,
|
|
14634
|
+
var shaSuffix = sha256.slice(0, FILENAME_HASH_SUFFIX_LENGTH);
|
|
14532
14635
|
var mimeType = (_Internal$getMimeType = Internal.getMimeType(data)) !== null && _Internal$getMimeType !== void 0 ? _Internal$getMimeType : "unknown";
|
|
14533
14636
|
var newExt = (_Internal$mimeTypeToF = Internal.mimeTypeToFileExt(mimeType)) !== null && _Internal$mimeTypeToF !== void 0 ? _Internal$mimeTypeToF : "unknown"; // Don't trust the file extension
|
|
14534
14637
|
if (filename) {
|
|
14535
14638
|
var cleanFilename = filename.split(".").slice(0, -1).join(".") || filename; // remove extension if it exists
|
|
14536
|
-
var
|
|
14537
|
-
var currentShaSuffix = cleanFilename.slice(maybeShaSuffixPos + 1, cleanFilename.length);
|
|
14538
|
-
if (currentShaSuffix === shaSuffix) {
|
|
14539
|
-
cleanFilename = cleanFilename.slice(0, maybeShaSuffixPos);
|
|
14540
|
-
}
|
|
14541
|
-
var escapedFilename = encodeURIComponent(cleanFilename).replace(/%[0-9A-Fa-f]{2}/g, "").toLowerCase();
|
|
14639
|
+
var escapedFilename = sanitizeFilenameBase(stripHashSuffix(cleanFilename, shaSuffix));
|
|
14542
14640
|
return "".concat(escapedFilename, "_").concat(shaSuffix, ".").concat(newExt);
|
|
14543
14641
|
}
|
|
14544
14642
|
return "".concat(sha256, ".").concat(newExt);
|
|
14545
|
-
}
|
|
14643
|
+
},
|
|
14644
|
+
createRenamedFilename: createRenamedFilename
|
|
14546
14645
|
};
|
|
14547
14646
|
function tryJsonParse(str) {
|
|
14548
14647
|
try {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { v as ASSISTANT_SETTINGS_MAX_LENGTH, A as ArraySchema, B as BooleanSchema, Y as CODE_LANGUAGES, Z as COLOR_FORMATS, X as CodeSchema, W as ColorSchema, e as DEFAULT_APP_HOST, $ as DEFAULT_COLOR_FORMAT, D as DEFAULT_CONTENT_HOST, f as DEFAULT_STATIC_HOST, n as DEFAULT_VAL_REMOTE_HOST, t as DateSchema, a0 as DateTimeSchema, a6 as DiscriminatedUnionSchema, a7 as EnumSchema, F as FATAL_ERROR_TYPES, r as FileSchema, G as GenericSelector, q as ImageSchema, I as Internal, a1 as KeyOfSchema, a8 as LiteralSchema, L as LocaleSchema, M as ModuleFilePathSep, N as NumberSchema, O as ObjectSchema, R as RecordSchema, a5 as RichTextSchema, a4 as RouteSchema, z as SETTINGS_MODULE_CONVENTION, S as Schema, u as SettingsSchema, p as StringSchema, T as THEME_LOGO_DIRECTORY, x as THEME_RADIUS_LENGTHS, w as THEME_RADIUS_STEPS, am as UNSAFE_URL_SCHEMES, V as VAL_EXTENSION, a2 as ValViewSchema, y as assistantAvailability, j as computeSourcesSha, h as computeValModuleShas, K as declaredKeySetOf, o as derefPatch, ai as describeSchemeRejection, a9 as deserializeSchema, U as discriminatedUnionBranchOf, g as extractValModules, ag as getSourcePathFromRoute, l as hasMediaSchema, k as hasRemoteFileSchema, i as initVal, aj as isCheckableScheme, ab as isInlineRender, ah as isPageRouter, C as isRootModuleFilePath, a3 as isValViewSource, J as localeOfValue, H as localeTagError, Q as missingDeclaredKeys, m as modules, ac as parseNextJsRoutePattern, ad as parseTanStackRoutePattern, aa as previewScope, ak as rejectScheme, E as resolveSettingsModule, al as schemeOf, ae as tanStackSegmentsOfRoutePath, af as validateUrlAgainstPattern } from './index-
|
|
1
|
+
export { v as ASSISTANT_SETTINGS_MAX_LENGTH, A as ArraySchema, B as BooleanSchema, Y as CODE_LANGUAGES, Z as COLOR_FORMATS, X as CodeSchema, W as ColorSchema, e as DEFAULT_APP_HOST, $ as DEFAULT_COLOR_FORMAT, D as DEFAULT_CONTENT_HOST, f as DEFAULT_STATIC_HOST, n as DEFAULT_VAL_REMOTE_HOST, t as DateSchema, a0 as DateTimeSchema, a6 as DiscriminatedUnionSchema, a7 as EnumSchema, F as FATAL_ERROR_TYPES, r as FileSchema, G as GenericSelector, q as ImageSchema, I as Internal, a1 as KeyOfSchema, a8 as LiteralSchema, L as LocaleSchema, M as ModuleFilePathSep, N as NumberSchema, O as ObjectSchema, R as RecordSchema, a5 as RichTextSchema, a4 as RouteSchema, z as SETTINGS_MODULE_CONVENTION, S as Schema, u as SettingsSchema, p as StringSchema, T as THEME_LOGO_DIRECTORY, x as THEME_RADIUS_LENGTHS, w as THEME_RADIUS_STEPS, am as UNSAFE_URL_SCHEMES, V as VAL_EXTENSION, a2 as ValViewSchema, y as assistantAvailability, j as computeSourcesSha, h as computeValModuleShas, K as declaredKeySetOf, o as derefPatch, ai as describeSchemeRejection, a9 as deserializeSchema, U as discriminatedUnionBranchOf, g as extractValModules, ag as getSourcePathFromRoute, l as hasMediaSchema, k as hasRemoteFileSchema, i as initVal, aj as isCheckableScheme, ab as isInlineRender, ah as isPageRouter, C as isRootModuleFilePath, a3 as isValViewSource, J as localeOfValue, H as localeTagError, Q as missingDeclaredKeys, m as modules, ac as parseNextJsRoutePattern, ad as parseTanStackRoutePattern, aa as previewScope, ak as rejectScheme, E as resolveSettingsModule, al as schemeOf, ae as tanStackSegmentsOfRoutePath, af as validateUrlAgainstPattern } from './index-36b5e0cb.esm.js';
|
|
2
2
|
import './result-daff1cae.esm.js';
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var dist_valbuildCore = require('../../dist/index-
|
|
5
|
+
var dist_valbuildCore = require('../../dist/index-8e33ef62.cjs.dev.js');
|
|
6
6
|
var result = require('../../dist/result-bb1f436e.cjs.dev.js');
|
|
7
7
|
var util = require('../../dist/util-b213092b.cjs.dev.js');
|
|
8
8
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var dist_valbuildCore = require('../../dist/index-
|
|
5
|
+
var dist_valbuildCore = require('../../dist/index-87b5aa27.cjs.prod.js');
|
|
6
6
|
var result = require('../../dist/result-787e35f6.cjs.prod.js');
|
|
7
7
|
var util = require('../../dist/util-030d8a1f.cjs.prod.js');
|
|
8
8
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { _ as _typeof, a as _slicedToArray, P as PatchError, s as splitModuleFilePathAndModulePath, b as _createClass, c as _classCallCheck, d as _toConsumableArray } from '../../dist/index-
|
|
2
|
-
export { P as PatchError } from '../../dist/index-
|
|
1
|
+
import { _ as _typeof, a as _slicedToArray, P as PatchError, s as splitModuleFilePathAndModulePath, b as _createClass, c as _classCallCheck, d as _toConsumableArray } from '../../dist/index-36b5e0cb.esm.js';
|
|
2
|
+
export { P as PatchError } from '../../dist/index-36b5e0cb.esm.js';
|
|
3
3
|
import { f as isNonEmpty, e as err, o as ok, m as map, g as flatMap, i as isErr, h as flatMapReduce, j as filterOrElse, k as mapErr, l as map$1, n as all, p as flatten, q as allT } from '../../dist/result-daff1cae.esm.js';
|
|
4
4
|
import { p as pipe } from '../../dist/util-18613e99.esm.js';
|
|
5
5
|
|