@slicemachine/manager 0.1.1-dev-plugins.3 → 0.1.1-dev-plugins.5
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/__node_modules/@msgpack/msgpack/dist.es5_esm/CachedKeyDecoder.cjs +65 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/CachedKeyDecoder.cjs.map +1 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/CachedKeyDecoder.js +65 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/CachedKeyDecoder.js.map +1 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/DecodeError.cjs +40 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/DecodeError.cjs.map +1 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/DecodeError.js +40 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/DecodeError.js.map +1 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/Decoder.cjs +801 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/Decoder.cjs.map +1 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/Decoder.js +801 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/Decoder.js.map +1 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/Encoder.cjs +351 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/Encoder.cjs.map +1 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/Encoder.js +351 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/Encoder.js.map +1 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/ExtData.cjs +11 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/ExtData.cjs.map +1 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/ExtData.js +11 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/ExtData.js.map +1 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/ExtensionCodec.cjs +62 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/ExtensionCodec.cjs.map +1 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/ExtensionCodec.js +62 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/ExtensionCodec.js.map +1 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/decode.cjs +14 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/decode.cjs.map +1 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/decode.js +14 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/decode.js.map +1 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/encode.cjs +13 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/encode.cjs.map +1 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/encode.js +13 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/encode.js.map +1 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/timestamp.cjs +91 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/timestamp.cjs.map +1 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/timestamp.js +91 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/timestamp.js.map +1 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/utils/int.cjs +31 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/utils/int.cjs.map +1 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/utils/int.js +31 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/utils/int.js.map +1 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/utils/prettyByte.cjs +7 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/utils/prettyByte.cjs.map +1 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/utils/prettyByte.js +7 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/utils/prettyByte.js.map +1 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/utils/typedArrays.cjs +23 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/utils/typedArrays.cjs.map +1 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/utils/typedArrays.js +23 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/utils/typedArrays.js.map +1 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/utils/utf8.cjs +132 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/utils/utf8.cjs.map +1 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/utils/utf8.js +132 -0
- package/dist/__node_modules/@msgpack/msgpack/dist.es5_esm/utils/utf8.js.map +1 -0
- package/dist/__node_modules/r19/dist/client/createRPCClient.cjs +29 -11
- package/dist/__node_modules/r19/dist/client/createRPCClient.cjs.map +1 -1
- package/dist/__node_modules/r19/dist/client/createRPCClient.js +29 -11
- package/dist/__node_modules/r19/dist/client/createRPCClient.js.map +1 -1
- package/dist/__node_modules/r19/dist/createRPCMiddleware.cjs +5 -4
- package/dist/__node_modules/r19/dist/createRPCMiddleware.cjs.map +1 -1
- package/dist/__node_modules/r19/dist/createRPCMiddleware.js +6 -5
- package/dist/__node_modules/r19/dist/createRPCMiddleware.js.map +1 -1
- package/dist/__node_modules/r19/dist/handleRPCRequest.cjs +39 -110
- package/dist/__node_modules/r19/dist/handleRPCRequest.cjs.map +1 -1
- package/dist/__node_modules/r19/dist/handleRPCRequest.js +39 -110
- package/dist/__node_modules/r19/dist/handleRPCRequest.js.map +1 -1
- package/dist/__node_modules/r19/dist/lib/replaceLeaves.cjs +28 -0
- package/dist/__node_modules/r19/dist/lib/replaceLeaves.cjs.map +1 -0
- package/dist/__node_modules/r19/dist/lib/replaceLeaves.js +28 -0
- package/dist/__node_modules/r19/dist/lib/replaceLeaves.js.map +1 -0
- package/dist/managers/createSliceMachineManagerMiddleware.cjs +0 -4
- package/dist/managers/createSliceMachineManagerMiddleware.cjs.map +1 -1
- package/dist/managers/createSliceMachineManagerMiddleware.js +0 -4
- package/dist/managers/createSliceMachineManagerMiddleware.js.map +1 -1
- package/dist/managers/screenshots/ScreenshotsManager.cjs +33 -16
- package/dist/managers/screenshots/ScreenshotsManager.cjs.map +1 -1
- package/dist/managers/screenshots/ScreenshotsManager.d.ts +2 -4
- package/dist/managers/screenshots/ScreenshotsManager.js +34 -17
- package/dist/managers/screenshots/ScreenshotsManager.js.map +1 -1
- package/dist/test/createSliceMachineManagerMSWHandler.cjs +3 -10
- package/dist/test/createSliceMachineManagerMSWHandler.cjs.map +1 -1
- package/dist/test/createSliceMachineManagerMSWHandler.js +3 -10
- package/dist/test/createSliceMachineManagerMSWHandler.js.map +1 -1
- package/package.json +4 -4
- package/src/client/index.ts +1 -1
- package/src/managers/screenshots/ScreenshotsManager.ts +65 -18
- package/src/test/createSliceMachineManagerMSWHandler.ts +2 -14
- package/dist/__node_modules/busboy/lib/index.cjs +0 -51
- package/dist/__node_modules/busboy/lib/index.cjs.map +0 -1
- package/dist/__node_modules/busboy/lib/index.js +0 -52
- package/dist/__node_modules/busboy/lib/index.js.map +0 -1
- package/dist/__node_modules/busboy/lib/types/multipart.cjs +0 -1046
- package/dist/__node_modules/busboy/lib/types/multipart.cjs.map +0 -1
- package/dist/__node_modules/busboy/lib/types/multipart.js +0 -1047
- package/dist/__node_modules/busboy/lib/types/multipart.js.map +0 -1
- package/dist/__node_modules/busboy/lib/types/urlencoded.cjs +0 -545
- package/dist/__node_modules/busboy/lib/types/urlencoded.cjs.map +0 -1
- package/dist/__node_modules/busboy/lib/types/urlencoded.js +0 -546
- package/dist/__node_modules/busboy/lib/types/urlencoded.js.map +0 -1
- package/dist/__node_modules/busboy/lib/utils.cjs +0 -1681
- package/dist/__node_modules/busboy/lib/utils.cjs.map +0 -1
- package/dist/__node_modules/busboy/lib/utils.js +0 -1682
- package/dist/__node_modules/busboy/lib/utils.js.map +0 -1
- package/dist/__node_modules/devalue/src/constants.cjs +0 -15
- package/dist/__node_modules/devalue/src/constants.cjs.map +0 -1
- package/dist/__node_modules/devalue/src/constants.js +0 -15
- package/dist/__node_modules/devalue/src/constants.js.map +0 -1
- package/dist/__node_modules/devalue/src/parse.cjs +0 -95
- package/dist/__node_modules/devalue/src/parse.cjs.map +0 -1
- package/dist/__node_modules/devalue/src/parse.js +0 -95
- package/dist/__node_modules/devalue/src/parse.js.map +0 -1
- package/dist/__node_modules/devalue/src/stringify.cjs +0 -140
- package/dist/__node_modules/devalue/src/stringify.cjs.map +0 -1
- package/dist/__node_modules/devalue/src/stringify.js +0 -140
- package/dist/__node_modules/devalue/src/stringify.js.map +0 -1
- package/dist/__node_modules/devalue/src/uneval.cjs +0 -4
- package/dist/__node_modules/devalue/src/uneval.cjs.map +0 -1
- package/dist/__node_modules/devalue/src/uneval.js +0 -3
- package/dist/__node_modules/devalue/src/uneval.js.map +0 -1
- package/dist/__node_modules/devalue/src/utils.cjs +0 -68
- package/dist/__node_modules/devalue/src/utils.cjs.map +0 -1
- package/dist/__node_modules/devalue/src/utils.js +0 -68
- package/dist/__node_modules/devalue/src/utils.js.map +0 -1
- package/dist/__node_modules/formdata-node/lib/browser.cjs +0 -16
- package/dist/__node_modules/formdata-node/lib/browser.cjs.map +0 -1
- package/dist/__node_modules/formdata-node/lib/browser.js +0 -16
- package/dist/__node_modules/formdata-node/lib/browser.js.map +0 -1
- package/dist/__node_modules/r19/dist/constants.cjs +0 -5
- package/dist/__node_modules/r19/dist/constants.cjs.map +0 -1
- package/dist/__node_modules/r19/dist/constants.js +0 -5
- package/dist/__node_modules/r19/dist/constants.js.map +0 -1
- package/dist/__node_modules/r19/dist/lib/encodeFormData.cjs +0 -46
- package/dist/__node_modules/r19/dist/lib/encodeFormData.cjs.map +0 -1
- package/dist/__node_modules/r19/dist/lib/encodeFormData.js +0 -46
- package/dist/__node_modules/r19/dist/lib/encodeFormData.js.map +0 -1
- package/dist/__node_modules/r19/dist/lib/flattenObject.cjs +0 -43
- package/dist/__node_modules/r19/dist/lib/flattenObject.cjs.map +0 -1
- package/dist/__node_modules/r19/dist/lib/flattenObject.js +0 -43
- package/dist/__node_modules/r19/dist/lib/flattenObject.js.map +0 -1
- package/dist/__node_modules/r19/dist/lib/formDataToObject.client.cjs +0 -19
- package/dist/__node_modules/r19/dist/lib/formDataToObject.client.cjs.map +0 -1
- package/dist/__node_modules/r19/dist/lib/formDataToObject.client.js +0 -19
- package/dist/__node_modules/r19/dist/lib/formDataToObject.client.js.map +0 -1
- package/dist/__node_modules/r19/dist/lib/objectToFormData.client.cjs +0 -20
- package/dist/__node_modules/r19/dist/lib/objectToFormData.client.cjs.map +0 -1
- package/dist/__node_modules/r19/dist/lib/objectToFormData.client.js +0 -20
- package/dist/__node_modules/r19/dist/lib/objectToFormData.client.js.map +0 -1
- package/dist/__node_modules/r19/dist/lib/objectToFormData.server.cjs +0 -22
- package/dist/__node_modules/r19/dist/lib/objectToFormData.server.cjs.map +0 -1
- package/dist/__node_modules/r19/dist/lib/objectToFormData.server.js +0 -22
- package/dist/__node_modules/r19/dist/lib/objectToFormData.server.js.map +0 -1
- package/dist/__node_modules/r19/dist/lib/unflattenObject.cjs +0 -20
- package/dist/__node_modules/r19/dist/lib/unflattenObject.cjs.map +0 -1
- package/dist/__node_modules/r19/dist/lib/unflattenObject.js +0 -20
- package/dist/__node_modules/r19/dist/lib/unflattenObject.js.map +0 -1
- package/dist/__node_modules/streamsearch/lib/sbmh.cjs +0 -393
- package/dist/__node_modules/streamsearch/lib/sbmh.cjs.map +0 -1
- package/dist/__node_modules/streamsearch/lib/sbmh.js +0 -394
- package/dist/__node_modules/streamsearch/lib/sbmh.js.map +0 -1
|
@@ -21,8 +21,9 @@ import Blob from './../../__node_modules/fetch-blob/index.js';
|
|
|
21
21
|
import "node:fs";
|
|
22
22
|
import "node:path";
|
|
23
23
|
import './../../__node_modules/node-domexception/index.js';
|
|
24
|
-
const
|
|
25
|
-
const
|
|
24
|
+
const SLICE_SIMULATOR_WAIT_FOR_TIMEOUT = 1e4;
|
|
25
|
+
const SLICE_SIMULATOR_WAIT_FOR_SELECTOR = "#__iframe-ready";
|
|
26
|
+
const SLICE_SIMULATOR_SCREENSHOT_SELECTOR = "#__iframe-renderer";
|
|
26
27
|
const DEFAULT_SCREENSHOT_VIEWPORT = {
|
|
27
28
|
width: 1200,
|
|
28
29
|
height: 800
|
|
@@ -37,6 +38,9 @@ function assertBrowserContextInitialized(browserContext) {
|
|
|
37
38
|
throw new Error("A browser context has not been initialized. Run `SliceMachineManager.screenshots.prototype.initBrowserContext()` before re-calling this method.");
|
|
38
39
|
}
|
|
39
40
|
}
|
|
41
|
+
const encodeSliceSimulatorURLPart = (urlPart) => {
|
|
42
|
+
return urlPart.replace(/\//g, "--");
|
|
43
|
+
};
|
|
40
44
|
class ScreenshotsManager extends BaseManager {
|
|
41
45
|
constructor() {
|
|
42
46
|
super(...arguments);
|
|
@@ -106,26 +110,41 @@ class ScreenshotsManager extends BaseManager {
|
|
|
106
110
|
if (!sliceMachineConfig.localSliceSimulatorURL) {
|
|
107
111
|
throw new Error("A local Slice Simulator URL must be configured in your Slice Machine configuration file.");
|
|
108
112
|
}
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
+
const { model } = await this.slices.readSlice({
|
|
114
|
+
libraryID: args.libraryID,
|
|
115
|
+
sliceID: args.sliceID
|
|
116
|
+
});
|
|
117
|
+
if (!model) {
|
|
118
|
+
throw new Error(`Did not find a Slice in library "${args.libraryID}" with ID "${args.sliceID}".`);
|
|
119
|
+
}
|
|
120
|
+
const viewport = args.viewport || DEFAULT_SCREENSHOT_VIEWPORT;
|
|
121
|
+
const url = new URL(`./${encodeSliceSimulatorURLPart(args.libraryID)}/${model.name}/${args.variationID}/screenshot`, args.sliceMachineUIOrigin);
|
|
122
|
+
url.searchParams.set("screenWidth", viewport.width.toString());
|
|
123
|
+
url.searchParams.set("screenHeight", viewport.height.toString());
|
|
113
124
|
const isURLAccessible = await checkIsURLAccessible(url.toString());
|
|
114
125
|
if (!isURLAccessible) {
|
|
115
|
-
throw new Error(`Slice Simulator URL is not accessible: ${url}`);
|
|
126
|
+
throw new Error(`Slice Simulator screenshot URL is not accessible: ${url}`);
|
|
116
127
|
}
|
|
117
128
|
const page = await this._browserContext.newPage();
|
|
118
|
-
page.setViewport(
|
|
119
|
-
await page.goto(url.toString());
|
|
120
|
-
await page.waitForSelector(
|
|
121
|
-
timeout:
|
|
129
|
+
page.setViewport(viewport);
|
|
130
|
+
await page.goto(url.toString(), { waitUntil: "load" });
|
|
131
|
+
await page.waitForSelector(SLICE_SIMULATOR_WAIT_FOR_SELECTOR, {
|
|
132
|
+
timeout: SLICE_SIMULATOR_WAIT_FOR_TIMEOUT
|
|
122
133
|
});
|
|
123
|
-
const element = await page.$(
|
|
134
|
+
const element = await page.$(SLICE_SIMULATOR_SCREENSHOT_SELECTOR);
|
|
124
135
|
if (!element) {
|
|
125
136
|
const baseURL = new URL(url.pathname, url.origin);
|
|
126
|
-
throw new Error(`Slice Simulator
|
|
137
|
+
throw new Error(`Slice Simulator did not find ${SLICE_SIMULATOR_WAIT_FOR_SELECTOR} on the page. Verify the URL is correct: ${baseURL}`);
|
|
127
138
|
}
|
|
128
|
-
const data = await element.screenshot({
|
|
139
|
+
const data = await element.screenshot({
|
|
140
|
+
encoding: "binary",
|
|
141
|
+
clip: {
|
|
142
|
+
width: viewport.width,
|
|
143
|
+
height: viewport.height,
|
|
144
|
+
x: 0,
|
|
145
|
+
y: 0
|
|
146
|
+
}
|
|
147
|
+
});
|
|
129
148
|
return {
|
|
130
149
|
data
|
|
131
150
|
};
|
|
@@ -161,8 +180,6 @@ class ScreenshotsManager extends BaseManager {
|
|
|
161
180
|
}
|
|
162
181
|
}
|
|
163
182
|
export {
|
|
164
|
-
ScreenshotsManager
|
|
165
|
-
assertBrowserContextInitialized,
|
|
166
|
-
assertS3ACLInitialized
|
|
183
|
+
ScreenshotsManager
|
|
167
184
|
};
|
|
168
185
|
//# sourceMappingURL=ScreenshotsManager.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScreenshotsManager.js","sources":["../../../../src/managers/screenshots/ScreenshotsManager.ts"],"sourcesContent":["import * as t from \"io-ts\";\nimport { fileTypeFromBuffer } from \"file-type\";\nimport fetch, { FormData, Blob } from \"node-fetch\";\n// puppeteer is lazy-loaded in captureSliceSimulatorScreenshot\nimport type { BrowserContext, Viewport } from \"puppeteer\";\n\nimport { checkIsURLAccessible } from \"../../lib/checkIsURLAccessible\";\nimport { createContentDigest } from \"../../lib/createContentDigest\";\nimport { decode } from \"../../lib/decode\";\n\nimport { S3ACL } from \"../../types\";\nimport { SLICE_MACHINE_USER_AGENT } from \"../../constants/SLICE_MACHINE_USER_AGENT\";\nimport { API_ENDPOINTS } from \"../../constants/API_ENDPOINTS\";\nimport { InternalError } from \"../../errors\";\n\nimport { BaseManager } from \"../BaseManager\";\n\nconst SLICE_SIMULATOR_LOAD_TIMEOUT = 10_000; // ms\nconst SLICE_SIMULATOR_ROOT_SELECTOR = \"#root\";\n\nconst DEFAULT_SCREENSHOT_VIEWPORT: Viewport = {\n\twidth: 1200,\n\theight: 800,\n};\n\nexport function assertS3ACLInitialized(\n\ts3ACL: S3ACL | undefined,\n): asserts s3ACL is NonNullable<typeof s3ACL> {\n\tif (s3ACL == undefined) {\n\t\tthrow new Error(\n\t\t\t\"An S3 ACL has not been initialized. Run `SliceMachineManager.screenshots.prototype.initS3ACL()` before re-calling this method.\",\n\t\t);\n\t}\n}\n\nexport function assertBrowserContextInitialized(\n\tbrowserContext: BrowserContext | undefined,\n): asserts browserContext is NonNullable<typeof browserContext> {\n\tif (browserContext == undefined) {\n\t\tthrow new Error(\n\t\t\t\"A browser context has not been initialized. Run `SliceMachineManager.screenshots.prototype.initBrowserContext()` before re-calling this method.\",\n\t\t);\n\t}\n}\n\ntype ScreenshotsManagerCaptureSliceSimulatorScreenshotArgs = {\n\tlibraryID: string;\n\tsliceID: string;\n\tvariationID: string;\n\tviewport?: Viewport;\n};\n\ntype ScreenshotsManagerCaptureSliceSimulatorScreenshotReturnType = {\n\tdata: Buffer;\n};\n\ntype ScreenshotsManagerUploadScreenshotArgs = {\n\tdata: Buffer;\n\tkeyPrefix?: string;\n};\n\ntype ScreenshotsManagerUploadScreenshotReturnType = {\n\turl: string;\n};\n\nexport class ScreenshotsManager extends BaseManager {\n\tprivate _browserContext: BrowserContext | undefined;\n\tprivate _s3ACL: S3ACL | undefined;\n\n\tasync initBrowserContext(): Promise<void> {\n\t\tif (this._browserContext) {\n\t\t\treturn;\n\t\t}\n\n\t\tlet puppeteer: typeof import(\"puppeteer\");\n\t\ttry {\n\t\t\t// Lazy-load Puppeteer only once it is needed.\n\t\t\tpuppeteer = await import(\"puppeteer\");\n\t\t} catch {\n\t\t\tthrow new InternalError(\n\t\t\t\t\"Screenshots require Puppeteer but Puppeteer was not found. Check that the `puppeteer` package is installed before trying again.\",\n\t\t\t);\n\t\t}\n\n\t\tconst browser = await puppeteer.launch();\n\n\t\tthis._browserContext = await browser.createIncognitoBrowserContext();\n\t}\n\n\tasync initS3ACL(): Promise<void> {\n\t\tif (this._s3ACL) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst sliceMachineConfig = await this.project.getSliceMachineConfig();\n\t\tconst authenticationToken = await this.user.getAuthenticationToken();\n\n\t\tconst awsACLURL = new URL(\"create\", API_ENDPOINTS.AwsAclProvider);\n\t\tconst awsACLRes = await fetch(awsACLURL.toString(), {\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bearer ${authenticationToken}`,\n\t\t\t\t\"User-Agent\": SLICE_MACHINE_USER_AGENT,\n\t\t\t\tRepository: sliceMachineConfig.repositoryName,\n\t\t\t},\n\t\t});\n\t\tconst awsACLJSON = await awsACLRes.json();\n\n\t\tconst { value: awsACL, error } = decode(\n\t\t\tt.intersection([\n\t\t\t\tt.type({\n\t\t\t\t\tvalues: t.type({\n\t\t\t\t\t\turl: t.string,\n\t\t\t\t\t\tfields: t.record(t.string, t.string),\n\t\t\t\t\t}),\n\t\t\t\t\timgixEndpoint: t.string,\n\t\t\t\t}),\n\t\t\t\tt.partial({\n\t\t\t\t\tmessage: t.string,\n\t\t\t\t\tMessage: t.string,\n\t\t\t\t\terror: t.string,\n\t\t\t\t}),\n\t\t\t]),\n\t\t\tawsACLJSON,\n\t\t);\n\n\t\tif (error) {\n\t\t\tthrow new Error(`Invalid AWS ACL response from ${awsACLURL}`);\n\t\t}\n\n\t\tconst errorMessage = awsACL.error || awsACL.message || awsACL.Message;\n\t\tif (errorMessage) {\n\t\t\tthrow new Error(`Failed to create an AWS ACL: ${errorMessage}`);\n\t\t}\n\n\t\tthis._s3ACL = {\n\t\t\tuploadEndpoint: awsACL.values.url,\n\t\t\trequiredFormDataFields: awsACL.values.fields,\n\t\t\timgixEndpoint: awsACL.imgixEndpoint,\n\t\t};\n\t}\n\n\t// TODO: Abstract to a generic `captureScreenshot()` method that is\n\t// used within a Slice-specific method in SliceManager.\n\tasync captureSliceSimulatorScreenshot(\n\t\targs: ScreenshotsManagerCaptureSliceSimulatorScreenshotArgs,\n\t): Promise<ScreenshotsManagerCaptureSliceSimulatorScreenshotReturnType> {\n\t\tassertBrowserContextInitialized(this._browserContext);\n\n\t\tconst sliceMachineConfig = await this.project.getSliceMachineConfig();\n\n\t\tif (!sliceMachineConfig.localSliceSimulatorURL) {\n\t\t\t// TODO: Provide a more helpful error message.\n\t\t\tthrow new Error(\n\t\t\t\t\"A local Slice Simulator URL must be configured in your Slice Machine configuration file.\",\n\t\t\t);\n\t\t}\n\n\t\tconst url = new URL(sliceMachineConfig.localSliceSimulatorURL);\n\t\turl.searchParams.set(\"lid\", args.libraryID);\n\t\turl.searchParams.set(\"sid\", args.sliceID);\n\t\turl.searchParams.set(\"vid\", args.variationID);\n\n\t\tconst isURLAccessible = await checkIsURLAccessible(url.toString());\n\n\t\tif (!isURLAccessible) {\n\t\t\tthrow new Error(`Slice Simulator URL is not accessible: ${url}`);\n\t\t}\n\n\t\tconst page = await this._browserContext.newPage();\n\t\tpage.setViewport(args.viewport || DEFAULT_SCREENSHOT_VIEWPORT);\n\n\t\t// TODO: I removed `goto`'s `{ waitUntil: \"networkidle2\" }` option.\n\t\t// Good idea? Bad idea?\n\t\tawait page.goto(url.toString());\n\t\tawait page.waitForSelector(SLICE_SIMULATOR_ROOT_SELECTOR, {\n\t\t\ttimeout: SLICE_SIMULATOR_LOAD_TIMEOUT,\n\t\t});\n\n\t\tconst element = await page.$(SLICE_SIMULATOR_ROOT_SELECTOR);\n\t\tif (!element) {\n\t\t\tconst baseURL = new URL(url.pathname, url.origin);\n\n\t\t\tthrow new Error(\n\t\t\t\t`Slice Simulator could not find the element to screenshot. Verify the URL is correct: ${baseURL}`,\n\t\t\t);\n\t\t}\n\n\t\tconst data = (await element.screenshot({ encoding: \"binary\" })) as Buffer;\n\n\t\treturn {\n\t\t\tdata,\n\t\t};\n\t}\n\n\tasync uploadScreenshot(\n\t\targs: ScreenshotsManagerUploadScreenshotArgs,\n\t): Promise<ScreenshotsManagerUploadScreenshotReturnType> {\n\t\tassertS3ACLInitialized(this._s3ACL);\n\n\t\tconst formData = new FormData();\n\n\t\tfor (const requiredFormDataFieldKey in this._s3ACL.requiredFormDataFields) {\n\t\t\tformData.append(\n\t\t\t\trequiredFormDataFieldKey,\n\t\t\t\tthis._s3ACL.requiredFormDataFields[requiredFormDataFieldKey],\n\t\t\t);\n\t\t}\n\n\t\tconst contentDigest = createContentDigest(args.data);\n\t\tconst fileType = await fileTypeFromBuffer(args.data);\n\t\tconst fileName = fileType\n\t\t\t? `${contentDigest}.${fileType.ext}`\n\t\t\t: contentDigest;\n\t\tconst key = args.keyPrefix ? `${args.keyPrefix}/${fileName}` : fileName;\n\n\t\tformData.set(\"key\", key);\n\n\t\tif (fileType) {\n\t\t\tformData.set(\"Content-Type\", fileType.mime);\n\t\t}\n\n\t\tformData.set(\"file\", new Blob([args.data], { type: fileType?.mime }));\n\n\t\tconst res = await fetch(this._s3ACL.uploadEndpoint, {\n\t\t\tmethod: \"POST\",\n\t\t\tbody: formData,\n\t\t});\n\n\t\tif (res.ok) {\n\t\t\tconst url = new URL(key, this._s3ACL.imgixEndpoint);\n\t\t\turl.searchParams.set(\"auto\", \"compress,format\");\n\n\t\t\treturn {\n\t\t\t\turl: url.toString(),\n\t\t\t};\n\t\t} else {\n\t\t\tthrow new Error(\n\t\t\t\t`Unable to upload screenshot with status code: ${res.status}`,\n\t\t\t);\n\t\t}\n\t}\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ScreenshotsManager.js","sources":["../../../../src/managers/screenshots/ScreenshotsManager.ts"],"sourcesContent":["import * as t from \"io-ts\";\nimport { fileTypeFromBuffer } from \"file-type\";\nimport fetch, { FormData, Blob } from \"node-fetch\";\n// puppeteer is lazy-loaded in captureSliceSimulatorScreenshot\nimport type { BrowserContext, Viewport } from \"puppeteer\";\n\nimport { checkIsURLAccessible } from \"../../lib/checkIsURLAccessible\";\nimport { createContentDigest } from \"../../lib/createContentDigest\";\nimport { decode } from \"../../lib/decode\";\n\nimport { S3ACL } from \"../../types\";\nimport { SLICE_MACHINE_USER_AGENT } from \"../../constants/SLICE_MACHINE_USER_AGENT\";\nimport { API_ENDPOINTS } from \"../../constants/API_ENDPOINTS\";\nimport { InternalError } from \"../../errors\";\n\nimport { BaseManager } from \"../BaseManager\";\n\nconst SLICE_SIMULATOR_WAIT_FOR_TIMEOUT = 10_000; // ms\nconst SLICE_SIMULATOR_WAIT_FOR_SELECTOR = \"#__iframe-ready\";\nconst SLICE_SIMULATOR_SCREENSHOT_SELECTOR = \"#__iframe-renderer\";\n\nconst DEFAULT_SCREENSHOT_VIEWPORT: Viewport = {\n\twidth: 1200,\n\theight: 800,\n};\n\nfunction assertS3ACLInitialized(\n\ts3ACL: S3ACL | undefined,\n): asserts s3ACL is NonNullable<typeof s3ACL> {\n\tif (s3ACL == undefined) {\n\t\tthrow new Error(\n\t\t\t\"An S3 ACL has not been initialized. Run `SliceMachineManager.screenshots.prototype.initS3ACL()` before re-calling this method.\",\n\t\t);\n\t}\n}\n\nfunction assertBrowserContextInitialized(\n\tbrowserContext: BrowserContext | undefined,\n): asserts browserContext is NonNullable<typeof browserContext> {\n\tif (browserContext == undefined) {\n\t\tthrow new Error(\n\t\t\t\"A browser context has not been initialized. Run `SliceMachineManager.screenshots.prototype.initBrowserContext()` before re-calling this method.\",\n\t\t);\n\t}\n}\n\n/**\n * Encodes a part of a Slice Simulator URL to ensure it can be added to a URL\n * safely.\n *\n * The encoding logic must match Slice Machine UI's URL encoding practices.\n * Today, that requires the following:\n *\n * - Replace \"/\" with \"--\" (e.g. a Slice Library ID of \"./slices\" should turn into\n * \".--slices\")\n *\n * @param urlPart - A part of the URL.\n *\n * @returns `urlPart` encoded for use in a URL.\n */\nconst encodeSliceSimulatorURLPart = (urlPart: string): string => {\n\treturn urlPart.replace(/\\//g, \"--\");\n};\n\ntype ScreenshotsManagerCaptureSliceSimulatorScreenshotArgs = {\n\tsliceMachineUIOrigin: string;\n\tlibraryID: string;\n\tsliceID: string;\n\tvariationID: string;\n\tviewport?: Viewport;\n};\n\ntype ScreenshotsManagerCaptureSliceSimulatorScreenshotReturnType = {\n\tdata: Buffer;\n};\n\ntype ScreenshotsManagerUploadScreenshotArgs = {\n\tdata: Buffer;\n\tkeyPrefix?: string;\n};\n\ntype ScreenshotsManagerUploadScreenshotReturnType = {\n\turl: string;\n};\n\nexport class ScreenshotsManager extends BaseManager {\n\tprivate _browserContext: BrowserContext | undefined;\n\tprivate _s3ACL: S3ACL | undefined;\n\n\tasync initBrowserContext(): Promise<void> {\n\t\tif (this._browserContext) {\n\t\t\treturn;\n\t\t}\n\n\t\tlet puppeteer: typeof import(\"puppeteer\");\n\t\ttry {\n\t\t\t// Lazy-load Puppeteer only once it is needed.\n\t\t\tpuppeteer = await import(\"puppeteer\");\n\t\t} catch {\n\t\t\tthrow new InternalError(\n\t\t\t\t\"Screenshots require Puppeteer but Puppeteer was not found. Check that the `puppeteer` package is installed before trying again.\",\n\t\t\t);\n\t\t}\n\n\t\tconst browser = await puppeteer.launch();\n\n\t\tthis._browserContext = await browser.createIncognitoBrowserContext();\n\t}\n\n\tasync initS3ACL(): Promise<void> {\n\t\tif (this._s3ACL) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst sliceMachineConfig = await this.project.getSliceMachineConfig();\n\t\tconst authenticationToken = await this.user.getAuthenticationToken();\n\n\t\tconst awsACLURL = new URL(\"create\", API_ENDPOINTS.AwsAclProvider);\n\t\tconst awsACLRes = await fetch(awsACLURL.toString(), {\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bearer ${authenticationToken}`,\n\t\t\t\t\"User-Agent\": SLICE_MACHINE_USER_AGENT,\n\t\t\t\tRepository: sliceMachineConfig.repositoryName,\n\t\t\t},\n\t\t});\n\t\tconst awsACLJSON = await awsACLRes.json();\n\n\t\tconst { value: awsACL, error } = decode(\n\t\t\tt.intersection([\n\t\t\t\tt.type({\n\t\t\t\t\tvalues: t.type({\n\t\t\t\t\t\turl: t.string,\n\t\t\t\t\t\tfields: t.record(t.string, t.string),\n\t\t\t\t\t}),\n\t\t\t\t\timgixEndpoint: t.string,\n\t\t\t\t}),\n\t\t\t\tt.partial({\n\t\t\t\t\tmessage: t.string,\n\t\t\t\t\tMessage: t.string,\n\t\t\t\t\terror: t.string,\n\t\t\t\t}),\n\t\t\t]),\n\t\t\tawsACLJSON,\n\t\t);\n\n\t\tif (error) {\n\t\t\tthrow new Error(`Invalid AWS ACL response from ${awsACLURL}`);\n\t\t}\n\n\t\tconst errorMessage = awsACL.error || awsACL.message || awsACL.Message;\n\t\tif (errorMessage) {\n\t\t\tthrow new Error(`Failed to create an AWS ACL: ${errorMessage}`);\n\t\t}\n\n\t\tthis._s3ACL = {\n\t\t\tuploadEndpoint: awsACL.values.url,\n\t\t\trequiredFormDataFields: awsACL.values.fields,\n\t\t\timgixEndpoint: awsACL.imgixEndpoint,\n\t\t};\n\t}\n\n\t// TODO: Abstract to a generic `captureScreenshot()` method that is\n\t// used within a Slice-specific method in SliceManager.\n\tasync captureSliceSimulatorScreenshot(\n\t\targs: ScreenshotsManagerCaptureSliceSimulatorScreenshotArgs,\n\t): Promise<ScreenshotsManagerCaptureSliceSimulatorScreenshotReturnType> {\n\t\tassertBrowserContextInitialized(this._browserContext);\n\n\t\tconst sliceMachineConfig = await this.project.getSliceMachineConfig();\n\n\t\tif (!sliceMachineConfig.localSliceSimulatorURL) {\n\t\t\t// TODO: Provide a more helpful error message.\n\t\t\tthrow new Error(\n\t\t\t\t\"A local Slice Simulator URL must be configured in your Slice Machine configuration file.\",\n\t\t\t);\n\t\t}\n\n\t\tconst { model } = await this.slices.readSlice({\n\t\t\tlibraryID: args.libraryID,\n\t\t\tsliceID: args.sliceID,\n\t\t});\n\t\tif (!model) {\n\t\t\tthrow new Error(\n\t\t\t\t`Did not find a Slice in library \"${args.libraryID}\" with ID \"${args.sliceID}\".`,\n\t\t\t);\n\t\t}\n\n\t\tconst viewport = args.viewport || DEFAULT_SCREENSHOT_VIEWPORT;\n\n\t\t// TODO: Change `model.name` to `args.sliceID`?\n\t\t// Making that change would require changing the screenshot\n\t\t// page path in Slice Machine UI.\n\t\tconst url = new URL(\n\t\t\t`./${encodeSliceSimulatorURLPart(args.libraryID)}/${model.name}/${\n\t\t\t\targs.variationID\n\t\t\t}/screenshot`,\n\t\t\targs.sliceMachineUIOrigin,\n\t\t);\n\t\turl.searchParams.set(\"screenWidth\", viewport.width.toString());\n\t\turl.searchParams.set(\"screenHeight\", viewport.height.toString());\n\n\t\tconst isURLAccessible = await checkIsURLAccessible(url.toString());\n\n\t\tif (!isURLAccessible) {\n\t\t\tthrow new Error(\n\t\t\t\t`Slice Simulator screenshot URL is not accessible: ${url}`,\n\t\t\t);\n\t\t}\n\n\t\tconst page = await this._browserContext.newPage();\n\t\tpage.setViewport(viewport);\n\n\t\tawait page.goto(url.toString(), { waitUntil: \"load\" });\n\t\tawait page.waitForSelector(SLICE_SIMULATOR_WAIT_FOR_SELECTOR, {\n\t\t\ttimeout: SLICE_SIMULATOR_WAIT_FOR_TIMEOUT,\n\t\t});\n\n\t\tconst element = await page.$(SLICE_SIMULATOR_SCREENSHOT_SELECTOR);\n\t\tif (!element) {\n\t\t\tconst baseURL = new URL(url.pathname, url.origin);\n\n\t\t\tthrow new Error(\n\t\t\t\t`Slice Simulator did not find ${SLICE_SIMULATOR_WAIT_FOR_SELECTOR} on the page. Verify the URL is correct: ${baseURL}`,\n\t\t\t);\n\t\t}\n\n\t\tconst data = (await element.screenshot({\n\t\t\tencoding: \"binary\",\n\t\t\tclip: {\n\t\t\t\twidth: viewport.width,\n\t\t\t\theight: viewport.height,\n\t\t\t\tx: 0,\n\t\t\t\ty: 0,\n\t\t\t},\n\t\t})) as Buffer;\n\n\t\treturn {\n\t\t\tdata,\n\t\t};\n\t}\n\n\tasync uploadScreenshot(\n\t\targs: ScreenshotsManagerUploadScreenshotArgs,\n\t): Promise<ScreenshotsManagerUploadScreenshotReturnType> {\n\t\tassertS3ACLInitialized(this._s3ACL);\n\n\t\tconst formData = new FormData();\n\n\t\tfor (const requiredFormDataFieldKey in this._s3ACL.requiredFormDataFields) {\n\t\t\tformData.append(\n\t\t\t\trequiredFormDataFieldKey,\n\t\t\t\tthis._s3ACL.requiredFormDataFields[requiredFormDataFieldKey],\n\t\t\t);\n\t\t}\n\n\t\tconst contentDigest = createContentDigest(args.data);\n\t\tconst fileType = await fileTypeFromBuffer(args.data);\n\t\tconst fileName = fileType\n\t\t\t? `${contentDigest}.${fileType.ext}`\n\t\t\t: contentDigest;\n\t\tconst key = args.keyPrefix ? `${args.keyPrefix}/${fileName}` : fileName;\n\n\t\tformData.set(\"key\", key);\n\n\t\tif (fileType) {\n\t\t\tformData.set(\"Content-Type\", fileType.mime);\n\t\t}\n\n\t\tformData.set(\"file\", new Blob([args.data], { type: fileType?.mime }));\n\n\t\tconst res = await fetch(this._s3ACL.uploadEndpoint, {\n\t\t\tmethod: \"POST\",\n\t\t\tbody: formData,\n\t\t});\n\n\t\tif (res.ok) {\n\t\t\tconst url = new URL(key, this._s3ACL.imgixEndpoint);\n\t\t\turl.searchParams.set(\"auto\", \"compress,format\");\n\n\t\t\treturn {\n\t\t\t\turl: url.toString(),\n\t\t\t};\n\t\t} else {\n\t\t\tthrow new Error(\n\t\t\t\t`Unable to upload screenshot with status code: ${res.status}`,\n\t\t\t);\n\t\t}\n\t}\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,26 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
require("h3");
|
|
4
|
+
require("node:buffer");
|
|
4
5
|
const handleRPCRequest = require('./../__node_modules/r19/dist/handleRPCRequest.cjs');
|
|
5
6
|
const index = require('./../__node_modules/msw/lib/index.cjs');
|
|
6
7
|
const createSliceMachineManagerMiddleware = require("../managers/createSliceMachineManagerMiddleware.cjs");
|
|
7
|
-
const streamToString = async (stream) => {
|
|
8
|
-
const chunks = [];
|
|
9
|
-
for await (const chunk of stream) {
|
|
10
|
-
chunks.push(Buffer.from(chunk));
|
|
11
|
-
}
|
|
12
|
-
return Buffer.concat(chunks).toString("utf-8");
|
|
13
|
-
};
|
|
14
8
|
const createSliceMachineManagerMSWHandler = (args) => {
|
|
15
9
|
return index.rest.post(args.url, async (req, res, ctx) => {
|
|
16
10
|
const rpcResponse = await handleRPCRequest.handleRPCRequest({
|
|
17
|
-
body: await req.
|
|
18
|
-
contentTypeHeader: req.headers.get("Content-Type"),
|
|
11
|
+
body: await req.arrayBuffer(),
|
|
19
12
|
procedures: createSliceMachineManagerMiddleware.getSliceMachineManagerProcedures({
|
|
20
13
|
sliceMachineManager: args.sliceMachineManager
|
|
21
14
|
})
|
|
22
15
|
});
|
|
23
|
-
return res(ctx.body(
|
|
16
|
+
return res(ctx.body(rpcResponse.body), ctx.set(rpcResponse.headers), ctx.status(rpcResponse.statusCode || 200));
|
|
24
17
|
});
|
|
25
18
|
};
|
|
26
19
|
exports.createSliceMachineManagerMSWHandler = createSliceMachineManagerMSWHandler;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createSliceMachineManagerMSWHandler.cjs","sources":["../../../src/test/createSliceMachineManagerMSWHandler.ts"],"sourcesContent":["import { handleRPCRequest } from \"r19\";\nimport { rest, DefaultBodyType, MockedRequest, RestHandler } from \"msw\";\
|
|
1
|
+
{"version":3,"file":"createSliceMachineManagerMSWHandler.cjs","sources":["../../../src/test/createSliceMachineManagerMSWHandler.ts"],"sourcesContent":["import { handleRPCRequest } from \"r19\";\nimport { rest, DefaultBodyType, MockedRequest, RestHandler } from \"msw\";\n\nimport { SliceMachineManager } from \"../managers/SliceMachineManager\";\nimport { getSliceMachineManagerProcedures } from \"../managers/createSliceMachineManagerMiddleware\";\n\nexport type CreateSliceMachineManagerMSWHandlerArgs = {\n\tsliceMachineManager: SliceMachineManager;\n\turl: string;\n};\n\nexport const createSliceMachineManagerMSWHandler = (\n\targs: CreateSliceMachineManagerMSWHandlerArgs,\n): RestHandler<MockedRequest<DefaultBodyType>> => {\n\treturn rest.post(args.url, async (req, res, ctx) => {\n\t\tconst rpcResponse = await handleRPCRequest({\n\t\t\tbody: await req.arrayBuffer(),\n\t\t\tprocedures: getSliceMachineManagerProcedures({\n\t\t\t\tsliceMachineManager: args.sliceMachineManager,\n\t\t\t}),\n\t\t});\n\n\t\treturn res(\n\t\t\tctx.body(rpcResponse.body),\n\t\t\tctx.set(rpcResponse.headers),\n\t\t\tctx.status(rpcResponse.statusCode || 200),\n\t\t);\n\t});\n};\n"],"names":["rest","handleRPCRequest","getSliceMachineManagerProcedures"],"mappings":";;;;;;;AAWa,MAAA,sCAAsC,CAClD,SACgD;AAChD,SAAOA,MAAAA,KAAK,KAAK,KAAK,KAAK,OAAO,KAAK,KAAK,QAAO;AAC5C,UAAA,cAAc,MAAMC,kCAAiB;AAAA,MAC1C,MAAM,MAAM,IAAI,YAAa;AAAA,MAC7B,YAAYC,oCAAAA,iCAAiC;AAAA,QAC5C,qBAAqB,KAAK;AAAA,MAAA,CAC1B;AAAA,IAAA,CACD;AAED,WAAO,IACN,IAAI,KAAK,YAAY,IAAI,GACzB,IAAI,IAAI,YAAY,OAAO,GAC3B,IAAI,OAAO,YAAY,cAAc,GAAG,CAAC;AAAA,EAAA,CAE1C;AACF;;"}
|
|
@@ -1,24 +1,17 @@
|
|
|
1
1
|
import "h3";
|
|
2
|
+
import "node:buffer";
|
|
2
3
|
import { handleRPCRequest } from './../__node_modules/r19/dist/handleRPCRequest.js';
|
|
3
4
|
import lib from './../__node_modules/msw/lib/index.js';
|
|
4
5
|
import { getSliceMachineManagerProcedures } from "../managers/createSliceMachineManagerMiddleware.js";
|
|
5
|
-
const streamToString = async (stream) => {
|
|
6
|
-
const chunks = [];
|
|
7
|
-
for await (const chunk of stream) {
|
|
8
|
-
chunks.push(Buffer.from(chunk));
|
|
9
|
-
}
|
|
10
|
-
return Buffer.concat(chunks).toString("utf-8");
|
|
11
|
-
};
|
|
12
6
|
const createSliceMachineManagerMSWHandler = (args) => {
|
|
13
7
|
return lib.rest.post(args.url, async (req, res, ctx) => {
|
|
14
8
|
const rpcResponse = await handleRPCRequest({
|
|
15
|
-
body: await req.
|
|
16
|
-
contentTypeHeader: req.headers.get("Content-Type"),
|
|
9
|
+
body: await req.arrayBuffer(),
|
|
17
10
|
procedures: getSliceMachineManagerProcedures({
|
|
18
11
|
sliceMachineManager: args.sliceMachineManager
|
|
19
12
|
})
|
|
20
13
|
});
|
|
21
|
-
return res(ctx.body(
|
|
14
|
+
return res(ctx.body(rpcResponse.body), ctx.set(rpcResponse.headers), ctx.status(rpcResponse.statusCode || 200));
|
|
22
15
|
});
|
|
23
16
|
};
|
|
24
17
|
export {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createSliceMachineManagerMSWHandler.js","sources":["../../../src/test/createSliceMachineManagerMSWHandler.ts"],"sourcesContent":["import { handleRPCRequest } from \"r19\";\nimport { rest, DefaultBodyType, MockedRequest, RestHandler } from \"msw\";\
|
|
1
|
+
{"version":3,"file":"createSliceMachineManagerMSWHandler.js","sources":["../../../src/test/createSliceMachineManagerMSWHandler.ts"],"sourcesContent":["import { handleRPCRequest } from \"r19\";\nimport { rest, DefaultBodyType, MockedRequest, RestHandler } from \"msw\";\n\nimport { SliceMachineManager } from \"../managers/SliceMachineManager\";\nimport { getSliceMachineManagerProcedures } from \"../managers/createSliceMachineManagerMiddleware\";\n\nexport type CreateSliceMachineManagerMSWHandlerArgs = {\n\tsliceMachineManager: SliceMachineManager;\n\turl: string;\n};\n\nexport const createSliceMachineManagerMSWHandler = (\n\targs: CreateSliceMachineManagerMSWHandlerArgs,\n): RestHandler<MockedRequest<DefaultBodyType>> => {\n\treturn rest.post(args.url, async (req, res, ctx) => {\n\t\tconst rpcResponse = await handleRPCRequest({\n\t\t\tbody: await req.arrayBuffer(),\n\t\t\tprocedures: getSliceMachineManagerProcedures({\n\t\t\t\tsliceMachineManager: args.sliceMachineManager,\n\t\t\t}),\n\t\t});\n\n\t\treturn res(\n\t\t\tctx.body(rpcResponse.body),\n\t\t\tctx.set(rpcResponse.headers),\n\t\t\tctx.status(rpcResponse.statusCode || 200),\n\t\t);\n\t});\n};\n"],"names":["rest"],"mappings":";;;;;AAWa,MAAA,sCAAsC,CAClD,SACgD;AAChD,SAAOA,IAAAA,KAAK,KAAK,KAAK,KAAK,OAAO,KAAK,KAAK,QAAO;AAC5C,UAAA,cAAc,MAAM,iBAAiB;AAAA,MAC1C,MAAM,MAAM,IAAI,YAAa;AAAA,MAC7B,YAAY,iCAAiC;AAAA,QAC5C,qBAAqB,KAAK;AAAA,MAAA,CAC1B;AAAA,IAAA,CACD;AAED,WAAO,IACN,IAAI,KAAK,YAAY,IAAI,GACzB,IAAI,IAAI,YAAY,OAAO,GAC3B,IAAI,OAAO,YAAY,cAAc,GAAG,CAAC;AAAA,EAAA,CAE1C;AACF;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@slicemachine/manager",
|
|
3
|
-
"version": "0.1.1-dev-plugins.
|
|
3
|
+
"version": "0.1.1-dev-plugins.5",
|
|
4
4
|
"description": "Manage all aspects of a Slice Machine project.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"dependencies": {
|
|
61
61
|
"@prismicio/custom-types-client": "^1.0.2",
|
|
62
62
|
"@prismicio/types-internal": "^1.5.3",
|
|
63
|
-
"@slicemachine/plugin-kit": "0.1.8-dev-plugins.
|
|
63
|
+
"@slicemachine/plugin-kit": "0.1.8-dev-plugins.5",
|
|
64
64
|
"@wooorm/starry-night": "^1.4.2",
|
|
65
65
|
"analytics-node": "^6.2.0",
|
|
66
66
|
"cookie": "^0.5.0",
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"node-fetch": "^3.3.0",
|
|
76
76
|
"p-limit": "^4.0.0",
|
|
77
77
|
"prettier": "^2.8.1",
|
|
78
|
-
"r19": "^0.1.
|
|
78
|
+
"r19": "^0.1.4",
|
|
79
79
|
"rc9": "^2.0.0",
|
|
80
80
|
"rehype-stringify": "^9.0.3",
|
|
81
81
|
"remark-gfm": "^3.0.1",
|
|
@@ -124,5 +124,5 @@
|
|
|
124
124
|
"publishConfig": {
|
|
125
125
|
"access": "public"
|
|
126
126
|
},
|
|
127
|
-
"gitHead": "
|
|
127
|
+
"gitHead": "65c73d66cb958236cc0ea17c89b6aed023e93dff"
|
|
128
128
|
}
|
package/src/client/index.ts
CHANGED
|
@@ -15,15 +15,16 @@ import { InternalError } from "../../errors";
|
|
|
15
15
|
|
|
16
16
|
import { BaseManager } from "../BaseManager";
|
|
17
17
|
|
|
18
|
-
const
|
|
19
|
-
const
|
|
18
|
+
const SLICE_SIMULATOR_WAIT_FOR_TIMEOUT = 10_000; // ms
|
|
19
|
+
const SLICE_SIMULATOR_WAIT_FOR_SELECTOR = "#__iframe-ready";
|
|
20
|
+
const SLICE_SIMULATOR_SCREENSHOT_SELECTOR = "#__iframe-renderer";
|
|
20
21
|
|
|
21
22
|
const DEFAULT_SCREENSHOT_VIEWPORT: Viewport = {
|
|
22
23
|
width: 1200,
|
|
23
24
|
height: 800,
|
|
24
25
|
};
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
function assertS3ACLInitialized(
|
|
27
28
|
s3ACL: S3ACL | undefined,
|
|
28
29
|
): asserts s3ACL is NonNullable<typeof s3ACL> {
|
|
29
30
|
if (s3ACL == undefined) {
|
|
@@ -33,7 +34,7 @@ export function assertS3ACLInitialized(
|
|
|
33
34
|
}
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
function assertBrowserContextInitialized(
|
|
37
38
|
browserContext: BrowserContext | undefined,
|
|
38
39
|
): asserts browserContext is NonNullable<typeof browserContext> {
|
|
39
40
|
if (browserContext == undefined) {
|
|
@@ -43,7 +44,26 @@ export function assertBrowserContextInitialized(
|
|
|
43
44
|
}
|
|
44
45
|
}
|
|
45
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Encodes a part of a Slice Simulator URL to ensure it can be added to a URL
|
|
49
|
+
* safely.
|
|
50
|
+
*
|
|
51
|
+
* The encoding logic must match Slice Machine UI's URL encoding practices.
|
|
52
|
+
* Today, that requires the following:
|
|
53
|
+
*
|
|
54
|
+
* - Replace "/" with "--" (e.g. a Slice Library ID of "./slices" should turn into
|
|
55
|
+
* ".--slices")
|
|
56
|
+
*
|
|
57
|
+
* @param urlPart - A part of the URL.
|
|
58
|
+
*
|
|
59
|
+
* @returns `urlPart` encoded for use in a URL.
|
|
60
|
+
*/
|
|
61
|
+
const encodeSliceSimulatorURLPart = (urlPart: string): string => {
|
|
62
|
+
return urlPart.replace(/\//g, "--");
|
|
63
|
+
};
|
|
64
|
+
|
|
46
65
|
type ScreenshotsManagerCaptureSliceSimulatorScreenshotArgs = {
|
|
66
|
+
sliceMachineUIOrigin: string;
|
|
47
67
|
libraryID: string;
|
|
48
68
|
sliceID: string;
|
|
49
69
|
variationID: string;
|
|
@@ -155,37 +175,64 @@ export class ScreenshotsManager extends BaseManager {
|
|
|
155
175
|
);
|
|
156
176
|
}
|
|
157
177
|
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
178
|
+
const { model } = await this.slices.readSlice({
|
|
179
|
+
libraryID: args.libraryID,
|
|
180
|
+
sliceID: args.sliceID,
|
|
181
|
+
});
|
|
182
|
+
if (!model) {
|
|
183
|
+
throw new Error(
|
|
184
|
+
`Did not find a Slice in library "${args.libraryID}" with ID "${args.sliceID}".`,
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const viewport = args.viewport || DEFAULT_SCREENSHOT_VIEWPORT;
|
|
189
|
+
|
|
190
|
+
// TODO: Change `model.name` to `args.sliceID`?
|
|
191
|
+
// Making that change would require changing the screenshot
|
|
192
|
+
// page path in Slice Machine UI.
|
|
193
|
+
const url = new URL(
|
|
194
|
+
`./${encodeSliceSimulatorURLPart(args.libraryID)}/${model.name}/${
|
|
195
|
+
args.variationID
|
|
196
|
+
}/screenshot`,
|
|
197
|
+
args.sliceMachineUIOrigin,
|
|
198
|
+
);
|
|
199
|
+
url.searchParams.set("screenWidth", viewport.width.toString());
|
|
200
|
+
url.searchParams.set("screenHeight", viewport.height.toString());
|
|
162
201
|
|
|
163
202
|
const isURLAccessible = await checkIsURLAccessible(url.toString());
|
|
164
203
|
|
|
165
204
|
if (!isURLAccessible) {
|
|
166
|
-
throw new Error(
|
|
205
|
+
throw new Error(
|
|
206
|
+
`Slice Simulator screenshot URL is not accessible: ${url}`,
|
|
207
|
+
);
|
|
167
208
|
}
|
|
168
209
|
|
|
169
210
|
const page = await this._browserContext.newPage();
|
|
170
|
-
page.setViewport(
|
|
211
|
+
page.setViewport(viewport);
|
|
171
212
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
await page.waitForSelector(SLICE_SIMULATOR_ROOT_SELECTOR, {
|
|
176
|
-
timeout: SLICE_SIMULATOR_LOAD_TIMEOUT,
|
|
213
|
+
await page.goto(url.toString(), { waitUntil: "load" });
|
|
214
|
+
await page.waitForSelector(SLICE_SIMULATOR_WAIT_FOR_SELECTOR, {
|
|
215
|
+
timeout: SLICE_SIMULATOR_WAIT_FOR_TIMEOUT,
|
|
177
216
|
});
|
|
178
217
|
|
|
179
|
-
const element = await page.$(
|
|
218
|
+
const element = await page.$(SLICE_SIMULATOR_SCREENSHOT_SELECTOR);
|
|
180
219
|
if (!element) {
|
|
181
220
|
const baseURL = new URL(url.pathname, url.origin);
|
|
182
221
|
|
|
183
222
|
throw new Error(
|
|
184
|
-
`Slice Simulator
|
|
223
|
+
`Slice Simulator did not find ${SLICE_SIMULATOR_WAIT_FOR_SELECTOR} on the page. Verify the URL is correct: ${baseURL}`,
|
|
185
224
|
);
|
|
186
225
|
}
|
|
187
226
|
|
|
188
|
-
const data = (await element.screenshot({
|
|
227
|
+
const data = (await element.screenshot({
|
|
228
|
+
encoding: "binary",
|
|
229
|
+
clip: {
|
|
230
|
+
width: viewport.width,
|
|
231
|
+
height: viewport.height,
|
|
232
|
+
x: 0,
|
|
233
|
+
y: 0,
|
|
234
|
+
},
|
|
235
|
+
})) as Buffer;
|
|
189
236
|
|
|
190
237
|
return {
|
|
191
238
|
data,
|
|
@@ -1,20 +1,9 @@
|
|
|
1
1
|
import { handleRPCRequest } from "r19";
|
|
2
2
|
import { rest, DefaultBodyType, MockedRequest, RestHandler } from "msw";
|
|
3
|
-
import { Readable } from "node:stream";
|
|
4
3
|
|
|
5
4
|
import { SliceMachineManager } from "../managers/SliceMachineManager";
|
|
6
5
|
import { getSliceMachineManagerProcedures } from "../managers/createSliceMachineManagerMiddleware";
|
|
7
6
|
|
|
8
|
-
const streamToString = async (stream: Readable) => {
|
|
9
|
-
const chunks: Buffer[] = [];
|
|
10
|
-
|
|
11
|
-
for await (const chunk of stream) {
|
|
12
|
-
chunks.push(Buffer.from(chunk));
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
return Buffer.concat(chunks).toString("utf-8");
|
|
16
|
-
};
|
|
17
|
-
|
|
18
7
|
export type CreateSliceMachineManagerMSWHandlerArgs = {
|
|
19
8
|
sliceMachineManager: SliceMachineManager;
|
|
20
9
|
url: string;
|
|
@@ -25,15 +14,14 @@ export const createSliceMachineManagerMSWHandler = (
|
|
|
25
14
|
): RestHandler<MockedRequest<DefaultBodyType>> => {
|
|
26
15
|
return rest.post(args.url, async (req, res, ctx) => {
|
|
27
16
|
const rpcResponse = await handleRPCRequest({
|
|
28
|
-
body: await req.
|
|
29
|
-
contentTypeHeader: req.headers.get("Content-Type"),
|
|
17
|
+
body: await req.arrayBuffer(),
|
|
30
18
|
procedures: getSliceMachineManagerProcedures({
|
|
31
19
|
sliceMachineManager: args.sliceMachineManager,
|
|
32
20
|
}),
|
|
33
21
|
});
|
|
34
22
|
|
|
35
23
|
return res(
|
|
36
|
-
ctx.body(
|
|
24
|
+
ctx.body(rpcResponse.body),
|
|
37
25
|
ctx.set(rpcResponse.headers),
|
|
38
26
|
ctx.status(rpcResponse.statusCode || 200),
|
|
39
27
|
);
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
const utils = require("./utils.cjs");
|
|
3
|
-
const multipart = require("./types/multipart.cjs");
|
|
4
|
-
const urlencoded = require("./types/urlencoded.cjs");
|
|
5
|
-
const { parseContentType } = utils;
|
|
6
|
-
function getInstance(cfg) {
|
|
7
|
-
const headers = cfg.headers;
|
|
8
|
-
const conType = parseContentType(headers["content-type"]);
|
|
9
|
-
if (!conType)
|
|
10
|
-
throw new Error("Malformed content type");
|
|
11
|
-
for (const type of TYPES) {
|
|
12
|
-
const matched = type.detect(conType);
|
|
13
|
-
if (!matched)
|
|
14
|
-
continue;
|
|
15
|
-
const instanceCfg = {
|
|
16
|
-
limits: cfg.limits,
|
|
17
|
-
headers,
|
|
18
|
-
conType,
|
|
19
|
-
highWaterMark: void 0,
|
|
20
|
-
fileHwm: void 0,
|
|
21
|
-
defCharset: void 0,
|
|
22
|
-
defParamCharset: void 0,
|
|
23
|
-
preservePath: false
|
|
24
|
-
};
|
|
25
|
-
if (cfg.highWaterMark)
|
|
26
|
-
instanceCfg.highWaterMark = cfg.highWaterMark;
|
|
27
|
-
if (cfg.fileHwm)
|
|
28
|
-
instanceCfg.fileHwm = cfg.fileHwm;
|
|
29
|
-
instanceCfg.defCharset = cfg.defCharset;
|
|
30
|
-
instanceCfg.defParamCharset = cfg.defParamCharset;
|
|
31
|
-
instanceCfg.preservePath = cfg.preservePath;
|
|
32
|
-
return new type(instanceCfg);
|
|
33
|
-
}
|
|
34
|
-
throw new Error(`Unsupported content type: ${headers["content-type"]}`);
|
|
35
|
-
}
|
|
36
|
-
const TYPES = [
|
|
37
|
-
multipart,
|
|
38
|
-
urlencoded
|
|
39
|
-
].filter(function(typemod) {
|
|
40
|
-
return typeof typemod.detect === "function";
|
|
41
|
-
});
|
|
42
|
-
var lib = (cfg) => {
|
|
43
|
-
if (typeof cfg !== "object" || cfg === null)
|
|
44
|
-
cfg = {};
|
|
45
|
-
if (typeof cfg.headers !== "object" || cfg.headers === null || typeof cfg.headers["content-type"] !== "string") {
|
|
46
|
-
throw new Error("Missing Content-Type");
|
|
47
|
-
}
|
|
48
|
-
return getInstance(cfg);
|
|
49
|
-
};
|
|
50
|
-
module.exports = lib;
|
|
51
|
-
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../../../../../../node_modules/busboy/lib/index.js"],"sourcesContent":["'use strict';\n\nconst { parseContentType } = require('./utils.js');\n\nfunction getInstance(cfg) {\n const headers = cfg.headers;\n const conType = parseContentType(headers['content-type']);\n if (!conType)\n throw new Error('Malformed content type');\n\n for (const type of TYPES) {\n const matched = type.detect(conType);\n if (!matched)\n continue;\n\n const instanceCfg = {\n limits: cfg.limits,\n headers,\n conType,\n highWaterMark: undefined,\n fileHwm: undefined,\n defCharset: undefined,\n defParamCharset: undefined,\n preservePath: false,\n };\n if (cfg.highWaterMark)\n instanceCfg.highWaterMark = cfg.highWaterMark;\n if (cfg.fileHwm)\n instanceCfg.fileHwm = cfg.fileHwm;\n instanceCfg.defCharset = cfg.defCharset;\n instanceCfg.defParamCharset = cfg.defParamCharset;\n instanceCfg.preservePath = cfg.preservePath;\n return new type(instanceCfg);\n }\n\n throw new Error(`Unsupported content type: ${headers['content-type']}`);\n}\n\n// Note: types are explicitly listed here for easier bundling\n// See: https://github.com/mscdex/busboy/issues/121\nconst TYPES = [\n require('./types/multipart'),\n require('./types/urlencoded'),\n].filter(function(typemod) { return typeof typemod.detect === 'function'; });\n\nmodule.exports = (cfg) => {\n if (typeof cfg !== 'object' || cfg === null)\n cfg = {};\n\n if (typeof cfg.headers !== 'object'\n || cfg.headers === null\n || typeof cfg.headers['content-type'] !== 'string') {\n throw new Error('Missing Content-Type');\n }\n\n return getInstance(cfg);\n};\n"],"names":["require$$0","require$$1","require$$2"],"mappings":";;;;AAEA,MAAM,EAAE,iBAAkB,IAAGA;AAE7B,SAAS,YAAY,KAAK;AACxB,QAAM,UAAU,IAAI;AACpB,QAAM,UAAU,iBAAiB,QAAQ,cAAc,CAAC;AACxD,MAAI,CAAC;AACH,UAAM,IAAI,MAAM,wBAAwB;AAE1C,aAAW,QAAQ,OAAO;AACxB,UAAM,UAAU,KAAK,OAAO,OAAO;AACnC,QAAI,CAAC;AACH;AAEF,UAAM,cAAc;AAAA,MAClB,QAAQ,IAAI;AAAA,MACZ;AAAA,MACA;AAAA,MACA,eAAe;AAAA,MACf,SAAS;AAAA,MACT,YAAY;AAAA,MACZ,iBAAiB;AAAA,MACjB,cAAc;AAAA,IACpB;AACI,QAAI,IAAI;AACN,kBAAY,gBAAgB,IAAI;AAClC,QAAI,IAAI;AACN,kBAAY,UAAU,IAAI;AAC5B,gBAAY,aAAa,IAAI;AAC7B,gBAAY,kBAAkB,IAAI;AAClC,gBAAY,eAAe,IAAI;AAC/B,WAAO,IAAI,KAAK,WAAW;AAAA,EAC5B;AAED,QAAM,IAAI,MAAM,6BAA6B,QAAQ,cAAc,GAAG;AACxE;AAIA,MAAM,QAAQ;AAAA,EACZC;AAAAA,EACAC;AACF,EAAE,OAAO,SAAS,SAAS;AAAE,SAAO,OAAO,QAAQ,WAAW;AAAW,CAAE;IAE3E,MAAiB,CAAC,QAAQ;AACxB,MAAI,OAAO,QAAQ,YAAY,QAAQ;AACrC,UAAM,CAAA;AAER,MAAI,OAAO,IAAI,YAAY,YACpB,IAAI,YAAY,QAChB,OAAO,IAAI,QAAQ,cAAc,MAAM,UAAU;AACtD,UAAM,IAAI,MAAM,sBAAsB;AAAA,EACvC;AAED,SAAO,YAAY,GAAG;AACxB;;"}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import utils from "./utils.js";
|
|
2
|
-
import multipart from "./types/multipart.js";
|
|
3
|
-
import urlencoded from "./types/urlencoded.js";
|
|
4
|
-
const { parseContentType } = utils;
|
|
5
|
-
function getInstance(cfg) {
|
|
6
|
-
const headers = cfg.headers;
|
|
7
|
-
const conType = parseContentType(headers["content-type"]);
|
|
8
|
-
if (!conType)
|
|
9
|
-
throw new Error("Malformed content type");
|
|
10
|
-
for (const type of TYPES) {
|
|
11
|
-
const matched = type.detect(conType);
|
|
12
|
-
if (!matched)
|
|
13
|
-
continue;
|
|
14
|
-
const instanceCfg = {
|
|
15
|
-
limits: cfg.limits,
|
|
16
|
-
headers,
|
|
17
|
-
conType,
|
|
18
|
-
highWaterMark: void 0,
|
|
19
|
-
fileHwm: void 0,
|
|
20
|
-
defCharset: void 0,
|
|
21
|
-
defParamCharset: void 0,
|
|
22
|
-
preservePath: false
|
|
23
|
-
};
|
|
24
|
-
if (cfg.highWaterMark)
|
|
25
|
-
instanceCfg.highWaterMark = cfg.highWaterMark;
|
|
26
|
-
if (cfg.fileHwm)
|
|
27
|
-
instanceCfg.fileHwm = cfg.fileHwm;
|
|
28
|
-
instanceCfg.defCharset = cfg.defCharset;
|
|
29
|
-
instanceCfg.defParamCharset = cfg.defParamCharset;
|
|
30
|
-
instanceCfg.preservePath = cfg.preservePath;
|
|
31
|
-
return new type(instanceCfg);
|
|
32
|
-
}
|
|
33
|
-
throw new Error(`Unsupported content type: ${headers["content-type"]}`);
|
|
34
|
-
}
|
|
35
|
-
const TYPES = [
|
|
36
|
-
multipart,
|
|
37
|
-
urlencoded
|
|
38
|
-
].filter(function(typemod) {
|
|
39
|
-
return typeof typemod.detect === "function";
|
|
40
|
-
});
|
|
41
|
-
var lib = (cfg) => {
|
|
42
|
-
if (typeof cfg !== "object" || cfg === null)
|
|
43
|
-
cfg = {};
|
|
44
|
-
if (typeof cfg.headers !== "object" || cfg.headers === null || typeof cfg.headers["content-type"] !== "string") {
|
|
45
|
-
throw new Error("Missing Content-Type");
|
|
46
|
-
}
|
|
47
|
-
return getInstance(cfg);
|
|
48
|
-
};
|
|
49
|
-
export {
|
|
50
|
-
lib as default
|
|
51
|
-
};
|
|
52
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../../../../../node_modules/busboy/lib/index.js"],"sourcesContent":["'use strict';\n\nconst { parseContentType } = require('./utils.js');\n\nfunction getInstance(cfg) {\n const headers = cfg.headers;\n const conType = parseContentType(headers['content-type']);\n if (!conType)\n throw new Error('Malformed content type');\n\n for (const type of TYPES) {\n const matched = type.detect(conType);\n if (!matched)\n continue;\n\n const instanceCfg = {\n limits: cfg.limits,\n headers,\n conType,\n highWaterMark: undefined,\n fileHwm: undefined,\n defCharset: undefined,\n defParamCharset: undefined,\n preservePath: false,\n };\n if (cfg.highWaterMark)\n instanceCfg.highWaterMark = cfg.highWaterMark;\n if (cfg.fileHwm)\n instanceCfg.fileHwm = cfg.fileHwm;\n instanceCfg.defCharset = cfg.defCharset;\n instanceCfg.defParamCharset = cfg.defParamCharset;\n instanceCfg.preservePath = cfg.preservePath;\n return new type(instanceCfg);\n }\n\n throw new Error(`Unsupported content type: ${headers['content-type']}`);\n}\n\n// Note: types are explicitly listed here for easier bundling\n// See: https://github.com/mscdex/busboy/issues/121\nconst TYPES = [\n require('./types/multipart'),\n require('./types/urlencoded'),\n].filter(function(typemod) { return typeof typemod.detect === 'function'; });\n\nmodule.exports = (cfg) => {\n if (typeof cfg !== 'object' || cfg === null)\n cfg = {};\n\n if (typeof cfg.headers !== 'object'\n || cfg.headers === null\n || typeof cfg.headers['content-type'] !== 'string') {\n throw new Error('Missing Content-Type');\n }\n\n return getInstance(cfg);\n};\n"],"names":["require$$0","require$$1","require$$2"],"mappings":";;;AAEA,MAAM,EAAE,iBAAkB,IAAGA;AAE7B,SAAS,YAAY,KAAK;AACxB,QAAM,UAAU,IAAI;AACpB,QAAM,UAAU,iBAAiB,QAAQ,cAAc,CAAC;AACxD,MAAI,CAAC;AACH,UAAM,IAAI,MAAM,wBAAwB;AAE1C,aAAW,QAAQ,OAAO;AACxB,UAAM,UAAU,KAAK,OAAO,OAAO;AACnC,QAAI,CAAC;AACH;AAEF,UAAM,cAAc;AAAA,MAClB,QAAQ,IAAI;AAAA,MACZ;AAAA,MACA;AAAA,MACA,eAAe;AAAA,MACf,SAAS;AAAA,MACT,YAAY;AAAA,MACZ,iBAAiB;AAAA,MACjB,cAAc;AAAA,IACpB;AACI,QAAI,IAAI;AACN,kBAAY,gBAAgB,IAAI;AAClC,QAAI,IAAI;AACN,kBAAY,UAAU,IAAI;AAC5B,gBAAY,aAAa,IAAI;AAC7B,gBAAY,kBAAkB,IAAI;AAClC,gBAAY,eAAe,IAAI;AAC/B,WAAO,IAAI,KAAK,WAAW;AAAA,EAC5B;AAED,QAAM,IAAI,MAAM,6BAA6B,QAAQ,cAAc,GAAG;AACxE;AAIA,MAAM,QAAQ;AAAA,EACZC;AAAAA,EACAC;AACF,EAAE,OAAO,SAAS,SAAS;AAAE,SAAO,OAAO,QAAQ,WAAW;AAAW,CAAE;IAE3E,MAAiB,CAAC,QAAQ;AACxB,MAAI,OAAO,QAAQ,YAAY,QAAQ;AACrC,UAAM,CAAA;AAER,MAAI,OAAO,IAAI,YAAY,YACpB,IAAI,YAAY,QAChB,OAAO,IAAI,QAAQ,cAAc,MAAM,UAAU;AACtD,UAAM,IAAI,MAAM,sBAAsB;AAAA,EACvC;AAED,SAAO,YAAY,GAAG;AACxB;"}
|