storyblok 4.15.0 → 4.15.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.
- package/dist/index.mjs +117 -102
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
package/dist/index.mjs
CHANGED
|
@@ -11,7 +11,7 @@ import { Command } from 'commander';
|
|
|
11
11
|
import path, { join, resolve as resolve$1, parse, dirname as dirname$1, extname, relative, basename } from 'node:path';
|
|
12
12
|
import { MultiBar, Presets } from 'cli-progress';
|
|
13
13
|
import { Spinner } from '@topcli/spinner';
|
|
14
|
-
import fs, { mkdir, writeFile, readFile as readFile$1, appendFile, access, constants, readdir, unlink
|
|
14
|
+
import fs, { mkdir, writeFile, readFile as readFile$1, appendFile, access, constants, readdir, unlink } from 'node:fs/promises';
|
|
15
15
|
import filenamify from 'filenamify';
|
|
16
16
|
import { ManagementApiClient } from '@storyblok/management-api-client';
|
|
17
17
|
import { RateLimit, Sema } from 'async-sema';
|
|
@@ -103,6 +103,44 @@ const regionNames = {
|
|
|
103
103
|
({
|
|
104
104
|
SB_Agent_Version: process.env.npm_package_version || "4.x"
|
|
105
105
|
});
|
|
106
|
+
const SUPPORTED_ASSET_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
107
|
+
// Images: image/png, image/x-png, image/gif, image/jpeg, image/avif, image/svg+xml, image/webp
|
|
108
|
+
".jpg",
|
|
109
|
+
".jpeg",
|
|
110
|
+
".png",
|
|
111
|
+
".gif",
|
|
112
|
+
".webp",
|
|
113
|
+
".avif",
|
|
114
|
+
".svg",
|
|
115
|
+
// Video: video/*, application/mp4, application/x-mpegurl, application/vnd.apple.mpegurl
|
|
116
|
+
".mp4",
|
|
117
|
+
".mov",
|
|
118
|
+
".avi",
|
|
119
|
+
".webm",
|
|
120
|
+
".wmv",
|
|
121
|
+
".mkv",
|
|
122
|
+
".flv",
|
|
123
|
+
".ogv",
|
|
124
|
+
".3gp",
|
|
125
|
+
".m4v",
|
|
126
|
+
".mpg",
|
|
127
|
+
".mpeg",
|
|
128
|
+
".m3u8",
|
|
129
|
+
// Audio: audio/*
|
|
130
|
+
".mp3",
|
|
131
|
+
".wav",
|
|
132
|
+
".ogg",
|
|
133
|
+
".aac",
|
|
134
|
+
".flac",
|
|
135
|
+
".wma",
|
|
136
|
+
".m4a",
|
|
137
|
+
".opus",
|
|
138
|
+
// Documents: application/msword, text/plain, application/pdf, application/vnd.openxmlformats-officedocument.wordprocessingml.document
|
|
139
|
+
".pdf",
|
|
140
|
+
".doc",
|
|
141
|
+
".docx",
|
|
142
|
+
".txt"
|
|
143
|
+
]);
|
|
106
144
|
const directories = {
|
|
107
145
|
assets: "assets",
|
|
108
146
|
components: "components",
|
|
@@ -651,25 +689,38 @@ const API_ACTIONS = {
|
|
|
651
689
|
const API_ERRORS = {
|
|
652
690
|
unauthorized: "The user is not authorized to access the API",
|
|
653
691
|
network_error: "No response from server, please check if you are correctly connected to internet",
|
|
692
|
+
server_error: "The server returned an error",
|
|
654
693
|
invalid_credentials: "The provided credentials are invalid",
|
|
655
694
|
timeout: "The API request timed out",
|
|
656
695
|
generic: "Error fetching data from the API",
|
|
657
696
|
not_found: "The requested resource was not found",
|
|
658
697
|
unprocessable_entity: "The request was well-formed but was unable to be followed due to semantic errors"
|
|
659
698
|
};
|
|
699
|
+
function getErrorId(status) {
|
|
700
|
+
switch (status) {
|
|
701
|
+
case 401:
|
|
702
|
+
return "unauthorized";
|
|
703
|
+
case 404:
|
|
704
|
+
return "not_found";
|
|
705
|
+
case 422:
|
|
706
|
+
return "unprocessable_entity";
|
|
707
|
+
default:
|
|
708
|
+
return status >= 500 ? "server_error" : "generic";
|
|
709
|
+
}
|
|
710
|
+
}
|
|
660
711
|
function handleAPIError(action, error, customMessage) {
|
|
661
712
|
if (error instanceof FetchError) {
|
|
662
|
-
const
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
713
|
+
const errorId = getErrorId(error.response.status);
|
|
714
|
+
throw new APIError(errorId, action, error, customMessage);
|
|
715
|
+
}
|
|
716
|
+
const response = error?.response;
|
|
717
|
+
if (response?.status) {
|
|
718
|
+
const wrappedError = new FetchError(
|
|
719
|
+
response.statusText ?? error.message,
|
|
720
|
+
{ status: response.status, statusText: response.statusText ?? "", data: response.data }
|
|
721
|
+
);
|
|
722
|
+
const errorId = getErrorId(response.status);
|
|
723
|
+
throw new APIError(errorId, action, wrappedError, customMessage);
|
|
673
724
|
}
|
|
674
725
|
throw new APIError("generic", action, error, customMessage);
|
|
675
726
|
}
|
|
@@ -1888,34 +1939,10 @@ const getUser = async (token, region) => {
|
|
|
1888
1939
|
});
|
|
1889
1940
|
return data?.user;
|
|
1890
1941
|
} catch (error) {
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
throw new APIError("unauthorized", "get_user", error, `The token provided ${chalk.bold(maskToken(token))} is invalid.
|
|
1896
|
-
Please make sure you are using the correct token and try again.`);
|
|
1897
|
-
default:
|
|
1898
|
-
throw new APIError("network_error", "get_user", error);
|
|
1899
|
-
}
|
|
1900
|
-
}
|
|
1901
|
-
if (typeof error === "string" && error === "Unauthorized") {
|
|
1902
|
-
const mockFetchError = new FetchError("Non-JSON response", {
|
|
1903
|
-
status: 401,
|
|
1904
|
-
statusText: "Unauthorized",
|
|
1905
|
-
data: null
|
|
1906
|
-
});
|
|
1907
|
-
throw new APIError("unauthorized", "get_user", mockFetchError, `The token provided ${chalk.bold(maskToken(token))} is invalid.
|
|
1908
|
-
Please make sure you are using the correct token and try again.`);
|
|
1909
|
-
}
|
|
1910
|
-
if (typeof error === "object" && error !== null && Object.keys(error).length === 0) {
|
|
1911
|
-
const mockFetchError = new FetchError("Network Error", {
|
|
1912
|
-
status: 500,
|
|
1913
|
-
statusText: "Internal Server Error",
|
|
1914
|
-
data: null
|
|
1915
|
-
});
|
|
1916
|
-
throw new APIError("network_error", "get_user", mockFetchError);
|
|
1917
|
-
}
|
|
1918
|
-
throw new APIError("generic", "get_user", error);
|
|
1942
|
+
const status = error?.response?.status;
|
|
1943
|
+
const customMessage = status === 401 ? `The token provided ${chalk.bold(maskToken(token))} is invalid.
|
|
1944
|
+
Please make sure you are using the correct token and try again.` : void 0;
|
|
1945
|
+
handleAPIError("get_user", error, customMessage);
|
|
1919
1946
|
}
|
|
1920
1947
|
};
|
|
1921
1948
|
|
|
@@ -1926,17 +1953,7 @@ const loginWithToken = async (token, region) => {
|
|
|
1926
1953
|
if (error instanceof APIError) {
|
|
1927
1954
|
throw error;
|
|
1928
1955
|
}
|
|
1929
|
-
|
|
1930
|
-
const status = error.response.status;
|
|
1931
|
-
switch (status) {
|
|
1932
|
-
case 401:
|
|
1933
|
-
throw new APIError("unauthorized", "login_with_token", error, `The token provided ${chalk.bold(maskToken(token))} is invalid.
|
|
1934
|
-
Please make sure you are using the correct token and try again.`);
|
|
1935
|
-
default:
|
|
1936
|
-
throw new APIError("network_error", "login_with_token", error);
|
|
1937
|
-
}
|
|
1938
|
-
}
|
|
1939
|
-
throw new APIError("generic", "login_with_token", error, "The provided credentials are invalid");
|
|
1956
|
+
handleAPIError("login_with_token", error);
|
|
1940
1957
|
}
|
|
1941
1958
|
};
|
|
1942
1959
|
const loginWithEmailAndPassword = async (email, password, region) => {
|
|
@@ -4025,13 +4042,13 @@ const createStory = async (spaceId, payload) => {
|
|
|
4025
4042
|
},
|
|
4026
4043
|
body: {
|
|
4027
4044
|
story: payload.story,
|
|
4028
|
-
publish: payload.publish
|
|
4045
|
+
...payload.publish ? { publish: payload.publish } : {}
|
|
4029
4046
|
},
|
|
4030
4047
|
throwOnError: true
|
|
4031
4048
|
});
|
|
4032
4049
|
return data?.story;
|
|
4033
|
-
} catch (
|
|
4034
|
-
handleAPIError("create_story",
|
|
4050
|
+
} catch (error) {
|
|
4051
|
+
handleAPIError("create_story", error);
|
|
4035
4052
|
}
|
|
4036
4053
|
};
|
|
4037
4054
|
const updateStory = async (spaceId, storyId, payload) => {
|
|
@@ -4045,18 +4062,20 @@ const updateStory = async (spaceId, storyId, payload) => {
|
|
|
4045
4062
|
body: {
|
|
4046
4063
|
story: payload.story,
|
|
4047
4064
|
force_update: payload.force_update === "1" ? "1" : "0",
|
|
4048
|
-
publish: payload.publish
|
|
4065
|
+
...payload.publish ? { publish: payload.publish } : {}
|
|
4049
4066
|
},
|
|
4050
4067
|
throwOnError: true
|
|
4051
4068
|
});
|
|
4052
|
-
const
|
|
4069
|
+
const story = data?.story;
|
|
4053
4070
|
if (!story) {
|
|
4054
4071
|
throw new Error("Failed to update story");
|
|
4055
4072
|
}
|
|
4056
4073
|
return story;
|
|
4057
|
-
} catch (
|
|
4058
|
-
|
|
4059
|
-
|
|
4074
|
+
} catch (error) {
|
|
4075
|
+
if (error instanceof Error && error.message === "Failed to update story") {
|
|
4076
|
+
throw error;
|
|
4077
|
+
}
|
|
4078
|
+
handleAPIError("update_story", error);
|
|
4060
4079
|
}
|
|
4061
4080
|
};
|
|
4062
4081
|
|
|
@@ -7460,31 +7479,25 @@ const readLocalAssetsStream = ({
|
|
|
7460
7479
|
const iterator = async function* readAssets() {
|
|
7461
7480
|
try {
|
|
7462
7481
|
const files = await readdir(directoryPath);
|
|
7463
|
-
const
|
|
7464
|
-
setTotalAssets?.(
|
|
7465
|
-
for (const file of
|
|
7466
|
-
const
|
|
7482
|
+
const binaryFiles = files.filter((f) => SUPPORTED_ASSET_EXTENSIONS.has(extname(f).toLowerCase()));
|
|
7483
|
+
setTotalAssets?.(binaryFiles.length);
|
|
7484
|
+
for (const file of binaryFiles) {
|
|
7485
|
+
const binaryFilePath = join(directoryPath, file);
|
|
7467
7486
|
try {
|
|
7468
|
-
const
|
|
7469
|
-
|
|
7470
|
-
continue;
|
|
7471
|
-
}
|
|
7472
|
-
const metadataContent = await readFile$1(filePath, "utf8");
|
|
7473
|
-
const assetRaw = JSON.parse(metadataContent);
|
|
7487
|
+
const sidecar = await loadSidecarAssetData(binaryFilePath);
|
|
7488
|
+
const shortFilename = sidecar.short_filename || (sidecar.filename ? basename(sidecar.filename) : void 0) || file;
|
|
7474
7489
|
const asset = {
|
|
7475
|
-
...
|
|
7476
|
-
short_filename:
|
|
7490
|
+
...sidecar,
|
|
7491
|
+
short_filename: shortFilename
|
|
7477
7492
|
};
|
|
7478
|
-
const
|
|
7479
|
-
const
|
|
7480
|
-
const assetBinaryPath = join(directoryPath, `${baseName}${extFromMetadata}`);
|
|
7481
|
-
const fileBuffer = await readFile$1(assetBinaryPath);
|
|
7493
|
+
const fileBuffer = await readFile$1(binaryFilePath);
|
|
7494
|
+
const sidecarPath = getSidecarFilename(binaryFilePath);
|
|
7482
7495
|
yield {
|
|
7483
7496
|
asset,
|
|
7484
7497
|
context: {
|
|
7485
7498
|
fileBuffer,
|
|
7486
|
-
assetBinaryPath,
|
|
7487
|
-
assetPath:
|
|
7499
|
+
assetBinaryPath: binaryFilePath,
|
|
7500
|
+
assetPath: sidecarPath
|
|
7488
7501
|
}
|
|
7489
7502
|
};
|
|
7490
7503
|
} catch (maybeError) {
|
|
@@ -7868,6 +7881,9 @@ const traverseAndMapBySchema = (data, {
|
|
|
7868
7881
|
processedFields,
|
|
7869
7882
|
missingSchemas
|
|
7870
7883
|
}) => {
|
|
7884
|
+
if (!data?.component) {
|
|
7885
|
+
return data ?? {};
|
|
7886
|
+
}
|
|
7871
7887
|
const schema = schemas[data.component];
|
|
7872
7888
|
if (!schema) {
|
|
7873
7889
|
missingSchemas.add(data.component);
|
|
@@ -7911,7 +7927,7 @@ const traverseAndMapRichtextDoc = (data, {
|
|
|
7911
7927
|
}));
|
|
7912
7928
|
}
|
|
7913
7929
|
if (data && typeof data === "object") {
|
|
7914
|
-
if (data.type === "link" && data.attrs
|
|
7930
|
+
if (data.type === "link" && data.attrs?.linktype === "story") {
|
|
7915
7931
|
return {
|
|
7916
7932
|
...data,
|
|
7917
7933
|
attrs: {
|
|
@@ -7925,7 +7941,7 @@ const traverseAndMapRichtextDoc = (data, {
|
|
|
7925
7941
|
...data,
|
|
7926
7942
|
attrs: {
|
|
7927
7943
|
...data.attrs,
|
|
7928
|
-
body: data.attrs
|
|
7944
|
+
body: (data.attrs?.body ?? []).map((d) => traverseAndMapBySchema(d, {
|
|
7929
7945
|
schemas,
|
|
7930
7946
|
maps,
|
|
7931
7947
|
fieldRefMappers: fieldRefMappers2,
|
|
@@ -7967,7 +7983,7 @@ const multilinkFieldRefMapper = (data, { maps }) => {
|
|
|
7967
7983
|
};
|
|
7968
7984
|
const bloksFieldRefMapper = (data, { schemas, maps, fieldRefMappers: fieldRefMappers2, processedFields, missingSchemas }) => {
|
|
7969
7985
|
if (!Array.isArray(data)) {
|
|
7970
|
-
throw new TypeError(
|
|
7986
|
+
throw new TypeError(`Invalid bloks field: expected an array, but received ${JSON.stringify(data)}. Please make sure your bloks field value is an array of components (e.g. [{ component: "my_blok", ... }]).`);
|
|
7971
7987
|
}
|
|
7972
7988
|
return data.map((d) => traverseAndMapBySchema(d, {
|
|
7973
7989
|
schemas,
|
|
@@ -7986,7 +8002,7 @@ const assetFieldRefMapper = (data, { maps }) => {
|
|
|
7986
8002
|
};
|
|
7987
8003
|
const multiassetFieldRefMapper = (data, options) => {
|
|
7988
8004
|
if (!Array.isArray(data)) {
|
|
7989
|
-
throw new TypeError(
|
|
8005
|
+
throw new TypeError(`Invalid multiasset field: expected an array, but received ${JSON.stringify(data)}. Please make sure your multiasset field value is an array of asset objects (e.g. [{ filename: "...", id: 123 }]).`);
|
|
7990
8006
|
}
|
|
7991
8007
|
return data.map((d) => assetFieldRefMapper(d, options));
|
|
7992
8008
|
};
|
|
@@ -8009,23 +8025,23 @@ const storyRefMapper = (story, { schemas, maps }) => {
|
|
|
8009
8025
|
const missingSchemas = /* @__PURE__ */ new Set();
|
|
8010
8026
|
const alternates = story.alternates ? story.alternates.map((a) => ({
|
|
8011
8027
|
...a,
|
|
8012
|
-
id: maps.stories?.get(a.id)
|
|
8013
|
-
parent_id: maps.stories?.get(a.parent_id)
|
|
8028
|
+
id: maps.stories?.get(a.id) ?? a.id,
|
|
8029
|
+
parent_id: maps.stories?.get(a.parent_id) ?? a.parent_id
|
|
8014
8030
|
})) : story.alternates;
|
|
8015
|
-
const parentId = maps.stories?.get(story.parent_id)
|
|
8031
|
+
const parentId = maps.stories?.get(story.parent_id) ?? story.parent_id;
|
|
8016
8032
|
const mappedStory = {
|
|
8017
8033
|
...story,
|
|
8018
|
-
content: traverseAndMapBySchema(story.content, {
|
|
8034
|
+
content: story.content?.component ? traverseAndMapBySchema(story.content, {
|
|
8019
8035
|
schemas,
|
|
8020
8036
|
maps,
|
|
8021
8037
|
fieldRefMappers,
|
|
8022
8038
|
processedFields,
|
|
8023
8039
|
missingSchemas
|
|
8024
|
-
}),
|
|
8025
|
-
id: Number(maps.stories?.get(story.id)
|
|
8026
|
-
uuid: String(maps.stories?.get(story.uuid)
|
|
8040
|
+
}) : story.content,
|
|
8041
|
+
id: Number(maps.stories?.get(story.id) ?? story.id),
|
|
8042
|
+
uuid: String(maps.stories?.get(story.uuid) ?? story.uuid),
|
|
8027
8043
|
// @ts-expect-error Our types are wrong.
|
|
8028
|
-
parent_id: parentId ? Number(parentId) : null,
|
|
8044
|
+
parent_id: parentId != null ? Number(parentId) : null,
|
|
8029
8045
|
alternates
|
|
8030
8046
|
};
|
|
8031
8047
|
return {
|
|
@@ -8187,14 +8203,14 @@ const getRemoteStory = async ({ spaceId, storyId }) => {
|
|
|
8187
8203
|
return data?.story;
|
|
8188
8204
|
};
|
|
8189
8205
|
const makeCreateStoryAPITransport = ({ spaceId }) => async (localStory) => {
|
|
8190
|
-
const { id: _id, uuid: _uuid,
|
|
8206
|
+
const { id: _id, uuid: _uuid, parent_id: _parentId, content, ...newStoryData } = localStory;
|
|
8207
|
+
if (!localStory.is_folder && !content?.component) {
|
|
8208
|
+
throw new Error(`Story "${localStory.slug}" is missing a content type (content.component). Every story must define a content field with a valid component.`);
|
|
8209
|
+
}
|
|
8191
8210
|
const remoteStory = await createStory(spaceId, {
|
|
8192
8211
|
story: {
|
|
8193
8212
|
...newStoryData,
|
|
8194
|
-
content: {
|
|
8195
|
-
_uid: "",
|
|
8196
|
-
component: "__tmp__"
|
|
8197
|
-
}
|
|
8213
|
+
...content?.component ? { content: { _uid: "", component: "__migration_artifact__" } } : {}
|
|
8198
8214
|
},
|
|
8199
8215
|
publish: 0
|
|
8200
8216
|
});
|
|
@@ -8270,7 +8286,7 @@ const makeWriteStoryFSTransport = ({ directoryPath }) => async (story) => {
|
|
|
8270
8286
|
};
|
|
8271
8287
|
const makeWriteStoryAPITransport = ({ spaceId, publish }) => (mappedLocalStory) => updateStory(spaceId, mappedLocalStory.id, {
|
|
8272
8288
|
story: mappedLocalStory,
|
|
8273
|
-
publish: publish ?? (mappedLocalStory
|
|
8289
|
+
publish: publish ?? (isStoryPublishedWithoutChanges(mappedLocalStory) ? 1 : 0)
|
|
8274
8290
|
});
|
|
8275
8291
|
const makeCleanupStoryFSTransport = ({ directoryPath, maps }) => async (mappedStory) => {
|
|
8276
8292
|
const mapEntry = maps.stories?.entries().find(([_, v]) => v === mappedStory.uuid);
|
|
@@ -8896,14 +8912,14 @@ pushCmd.action(async (options, command) => {
|
|
|
8896
8912
|
processProgress.setTotal(total);
|
|
8897
8913
|
updateProgress.setTotal(total);
|
|
8898
8914
|
},
|
|
8899
|
-
onStoryError(error) {
|
|
8915
|
+
onStoryError(error, filename) {
|
|
8900
8916
|
summary.creationResults.failed += 1;
|
|
8901
8917
|
summary.processResults.total -= 1;
|
|
8902
8918
|
summary.updateResults.total -= 1;
|
|
8903
8919
|
processProgress.setTotal(summary.processResults.total);
|
|
8904
8920
|
updateProgress.setTotal(summary.updateResults.total);
|
|
8905
8921
|
creationProgress.increment();
|
|
8906
|
-
handleError(error, verbose);
|
|
8922
|
+
handleError(error, verbose, { storyFile: filename });
|
|
8907
8923
|
}
|
|
8908
8924
|
}),
|
|
8909
8925
|
// Create remote stories.
|
|
@@ -8912,7 +8928,6 @@ pushCmd.action(async (options, command) => {
|
|
|
8912
8928
|
spaceId: space,
|
|
8913
8929
|
transports: {
|
|
8914
8930
|
createStory: options.dryRun ? async (story) => story : makeCreateStoryAPITransport({
|
|
8915
|
-
maps,
|
|
8916
8931
|
spaceId: space
|
|
8917
8932
|
}),
|
|
8918
8933
|
appendStoryManifest: options.dryRun ? () => Promise.resolve() : makeAppendToManifestFSTransport({
|
|
@@ -8937,13 +8952,13 @@ pushCmd.action(async (options, command) => {
|
|
|
8937
8952
|
logger.info("Skipped creating story", { storyId: localStory.uuid });
|
|
8938
8953
|
summary.creationResults.skipped += 1;
|
|
8939
8954
|
},
|
|
8940
|
-
onStoryError(error) {
|
|
8955
|
+
onStoryError(error, localStory) {
|
|
8941
8956
|
summary.creationResults.failed += 1;
|
|
8942
8957
|
summary.processResults.total -= 1;
|
|
8943
8958
|
summary.updateResults.total -= 1;
|
|
8944
8959
|
processProgress.setTotal(summary.processResults.total);
|
|
8945
8960
|
updateProgress.setTotal(summary.updateResults.total);
|
|
8946
|
-
handleError(error, verbose);
|
|
8961
|
+
handleError(error, verbose, { storyId: localStory?.uuid });
|
|
8947
8962
|
},
|
|
8948
8963
|
onIncrement() {
|
|
8949
8964
|
creationProgress.increment();
|
|
@@ -8963,13 +8978,13 @@ pushCmd.action(async (options, command) => {
|
|
|
8963
8978
|
processProgress.setTotal(total);
|
|
8964
8979
|
updateProgress.setTotal(total);
|
|
8965
8980
|
},
|
|
8966
|
-
onStoryError(error) {
|
|
8981
|
+
onStoryError(error, filename) {
|
|
8967
8982
|
summary.creationResults.failed += 1;
|
|
8968
8983
|
summary.processResults.total -= 1;
|
|
8969
8984
|
summary.updateResults.total -= 1;
|
|
8970
8985
|
processProgress.setTotal(summary.processResults.total);
|
|
8971
8986
|
updateProgress.setTotal(summary.updateResults.total);
|
|
8972
|
-
handleError(error, verbose);
|
|
8987
|
+
handleError(error, verbose, { storyFile: filename });
|
|
8973
8988
|
}
|
|
8974
8989
|
}),
|
|
8975
8990
|
// Map all references to numeric ids and uuids.
|