@speedkit/cli 2.27.0 → 2.29.0

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.
Files changed (40) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +1 -1
  3. package/dist/helpers/get-parsed-config.d.ts +4 -0
  4. package/dist/helpers/get-parsed-config.js +4 -0
  5. package/dist/helpers/is-directory.d.ts +4 -0
  6. package/dist/helpers/is-directory.js +7 -1
  7. package/dist/helpers/is-file.d.ts +4 -0
  8. package/dist/helpers/is-file.js +7 -1
  9. package/dist/helpers/logging-helper.d.ts +6 -3
  10. package/dist/helpers/logging-helper.js +3 -0
  11. package/dist/services/customer-config/customer-config-service.js +0 -3
  12. package/dist/services/customer-config/templates/config_documentHandler.js.hbs +0 -4
  13. package/dist/services/deploy/interface/deploy-handler-interface.d.ts +4 -4
  14. package/dist/services/document-handler-runtime/document-handler-server.d.ts +2 -1
  15. package/dist/services/document-handler-runtime/document-handler-server.js +9 -8
  16. package/dist/services/document-handler-runtime/server/document-handler-response.d.ts +10 -0
  17. package/dist/services/document-handler-runtime/server/document-handler-response.js +19 -0
  18. package/dist/services/document-handler-runtime/server/request.d.ts +14 -0
  19. package/dist/services/document-handler-runtime/server/request.js +19 -0
  20. package/dist/services/onboarding/browser/baqend-response.d.ts +2 -2
  21. package/dist/services/onboarding/browser/baqend-response.js +10 -1
  22. package/dist/services/onboarding/dashboard/request-diff-service.js +4 -2
  23. package/dist/services/onboarding/fetch-events/speed-kit-asset-request.d.ts +4 -18
  24. package/dist/services/onboarding/fetch-events/speed-kit-asset-request.js +4 -153
  25. package/dist/services/onboarding/file-events/file-watcher.js +2 -1
  26. package/dist/services/onboarding/onboarding-model.d.ts +6 -1
  27. package/dist/services/onboarding/onboarding-model.js +8 -2
  28. package/dist/services/onboarding/onboarding-service-factory.d.ts +1 -0
  29. package/dist/services/onboarding/onboarding-service-factory.js +17 -8
  30. package/dist/services/onboarding/onboarding-service.js +1 -1
  31. package/dist/services/onboarding/request-cache/cache.js +9 -1
  32. package/dist/services/onboarding/virtual-orestes-app/crawler.d.ts +10 -0
  33. package/dist/services/onboarding/virtual-orestes-app/crawler.js +58 -0
  34. package/dist/services/onboarding/virtual-orestes-app/index.d.ts +26 -0
  35. package/dist/services/onboarding/virtual-orestes-app/index.js +190 -0
  36. package/oclif.manifest.json +1 -1
  37. package/package.json +1 -1
  38. package/dist/services/customer-config/templates/deploymentDetection.es6.hbs +0 -303
  39. package/dist/services/prewarm-service.d.ts +0 -5
  40. package/dist/services/prewarm-service.js +0 -64
@@ -1,303 +0,0 @@
1
- // Salesforce specific:
2
-
3
- const { run: executeRefresh } = require('./speedKit.CronJob');
4
- const fetch = require('node-fetch');
5
-
6
- const minutesToMilliseconds = (minutes) => {
7
- return minutes * 60000;
8
- }
9
-
10
- const LOCALES_BY_LAYOUT = [
11
- // TODO: adjust/replace this exemplary list
12
- ExampleLayoutA: ["de_DE", "en_DE"],
13
- ExampleLayoutB: ["en_US"],
14
- ];
15
- const ASSET_SUB_PATHS = [
16
- // These assets are expected to be found via the following base URL:
17
- // https://<ORIGIN>/on/demandware.static/Sites-<LAYOUT>-Site/-/<LOCALE>/v<VERSION>/...
18
-
19
- // TODO: adjust/replace this exemplary list
20
-
21
- // JS:
22
- "js/main.js",
23
- "internal/jscript/dwanalytics-(.*).js",
24
-
25
- // CSS:
26
- "css/style.css",
27
- ];
28
-
29
- function getAssetList() {
30
- return Object.entries(LOCALES_BY_LAYOUT).flatMap(([layout, locales]) =>
31
- locales.flatMap((locale) =>
32
- ASSET_SUB_PATHS.map(
33
- (subPath) =>
34
- {{#if staging.host}}
35
- `^https://(?:{{production.host}}|{{staging.host}})/on/demandware.static/Sites-${layout}-Site/-/${locale}/v(\\d{13})/${subPath}`,
36
- {{else}}
37
- `^https://{{production.host}}/on/demandware.static/Sites-${layout}-Site/-/${locale}/v(\\d{13})/${subPath}`,
38
- {{/if}}
39
- ),
40
- );
41
- }
42
-
43
- const deploymentConfig = {
44
- slack: "<1Password deployment token>",
45
- origins: [
46
- // Production:
47
- "https://{{production.host}}",
48
- {{#if staging.host}}
49
-
50
- // Staging:
51
- "https://{{staging.host}}",
52
- {{/if}}
53
-
54
- // TODO: also check for other origins like e.g. https://*.my.salesforce.com
55
- ],
56
- jobType: "deployment", // "instant" | "deployment"
57
-
58
- // configure how long the origin deployment takes in milliseconds:
59
- deploymentDelay: minutesToMilliseconds(15),
60
-
61
- // throttling the HTML refresh:
62
- revalidationDelay: minutesToMilliseconds(0),
63
-
64
- compareVersionStrings: false, // Compares version strings of the new and previous assets and only triggers deployment, when the current is newer. The version must be the first group in the regex
65
- compareContentHashes: true,
66
-
67
- assetList: [
68
- ...getAssetList(),
69
-
70
- // TODO: add additional non-demandware asset urls as needed
71
- ],
72
- }
73
-
74
- function getQuery(assetRegex, origin) {
75
- const query = getBaseQuery(origin);
76
- query.url = { "$regex": assetRegex };
77
- return query;
78
- }
79
-
80
- function getBaseQuery(...origins) {
81
- return {
82
- "contentType": { "$in": ["script", "style"] },
83
- "variation": "DEFAULT",
84
- "origin": { "$in": origins },
85
- };
86
- }
87
-
88
- const DEPLOYMENT_REFRESH_JOB_ID_TEMPLATE = '/db/jobs.Definition/deployment-refresh'
89
- const DEPLOYMENT_CHECKER_JOB_ID = '/db/jobs.Definition/deployment-check'
90
- const MINUTE = 60 * 1000;
91
-
92
- exports.get = async function(db, req, res) {
93
- if (!db.User.me || db.User.me.key !== '1') {
94
- throw new Abort("Unauthorized");
95
- }
96
-
97
- const assetsByOrigin = {};
98
-
99
- for (const origin of deploymentConfig.origins) {
100
- assetsByOrigin[origin] = await Promise.all(deploymentConfig.assetList.map(async asset => {
101
- const { url } = await findAsset(db, asset, origin) || { url: null };
102
- return url;
103
- }))
104
- }
105
-
106
- res.json(assetsByOrigin);
107
- }
108
-
109
-
110
- exports.post = async function(db, req, res) {
111
- if (!db.User.me || db.User.me.key !== '1') {
112
- throw new Abort("Unauthorized");
113
- }
114
-
115
- const deploymentJobs = await Promise.all(deploymentConfig.origins.map(
116
- origin => createDeploymentJob(db, deploymentConfig, origin)));
117
-
118
- const deploymentChecker = await (new db['jobs.Definition']({
119
- id: DEPLOYMENT_CHECKER_JOB_ID,
120
- module: 'deploymentDetection',
121
- cronpattern: '42 */10 * * * *',
122
- startsAt: new Date(),
123
- }).save().catch(e => e.message));
124
-
125
- res.json({ deploymentChecker, deploymentJobs });
126
- }
127
-
128
- function getDeploymentJobId(origin) {
129
- const originWithoutSlashes = origin.replace(/https:/g, '').replace(/\//g, '');
130
- return `${DEPLOYMENT_REFRESH_JOB_ID_TEMPLATE}-${originWithoutSlashes}`;
131
- }
132
-
133
- function getDeploymentJobName(origin) {
134
- const originWithoutSlashes = origin.replace(/https:/g, '').replace(/\//g, '');
135
- return `Deployment ${originWithoutSlashes}`
136
- }
137
-
138
- function createDeploymentJob(db, deploymentConfig, origin) {
139
- return (new db['jobs.Definition']({
140
- id: getDeploymentJobId(origin),
141
- module: 'speedKit.CronJob',
142
- cronpattern: '*/60 */24 */30 */12 *',
143
- startsAt: new Date(),
144
- expiresAt: new Date(1970,0, 1),
145
- data: {
146
- "name": getDeploymentJobName(origin),
147
- "jobType": deploymentConfig.jobType || "deployment",
148
- "filter": {
149
- "contentTypes": [ "document" ],
150
- "origin": [ origin ],
151
- },
152
- "scheduleIn": deploymentConfig.deploymentDelay,
153
- "revalidationDelay": deploymentConfig.revalidationDelay,
154
- },
155
- }).save({ force: true }).catch(e => e.message));
156
- }
157
-
158
- exports.run = async function triggerRefreshAfterDeployment(db, status) {
159
- const interval = new Date(Date.now() - 10 * MINUTE - 10);
160
- const origins = await findNewAsset(db, interval, deploymentConfig.origins);
161
- const app = db.connection.host;
162
-
163
- if (origins.size === 0) {
164
- status.data = {refreshing: false, reason: "no new asset" };
165
- return;
166
- }
167
-
168
- const data = status.data = {};
169
- for (const origin of origins) {
170
- try {
171
- const changedAsset = await getChangedAssets(db, origin);
172
- if (!changedAsset) {
173
- data[origin] = {
174
- refreshing: false, origin, reason: "asset hash not changed",
175
- };
176
- continue;
177
- }
178
-
179
- const reason = `Asset changed: ${changedAsset}`;
180
- fetch(deploymentConfig.slack, {
181
- username: 'Deployment Detection',
182
- method: 'post',
183
- body: JSON.stringify({text: `Deployment on ${app} on ${origin} detected. Starting HTML ${deploymentConfig.jobType || "deployment"} Refresh. ${reason}`}),
184
- }).catch(db.log.warn);
185
-
186
- await runDeploymentJob(db, origin, reason);
187
-
188
- data[origin] = {
189
- refreshing: true, origin, latestAsset: changedAsset,
190
- };
191
- } catch (e) {
192
- fetch(deploymentConfig.slack, {
193
- username: 'Deployment Detection',
194
- method: 'post',
195
- body: JSON.stringify({text: `Deployment detection on ${app} and ${origin} failed. ${e.message}`}),
196
- }).catch(db.log.warn);
197
- data[origin] = {
198
- refreshing: false, origin, reason: e.stack,
199
- };
200
- }
201
- }
202
- return data;
203
- };
204
-
205
- async function getChangedAssets(db, origin) {
206
- for (const urlRegex of deploymentConfig.assetList) {
207
- const interval = new Date(Date.now() - 10 * MINUTE - 10);
208
- const current = await findAsset(db, urlRegex, origin, interval);
209
- const previous = await findAsset(db, urlRegex, origin, null, interval);
210
-
211
- if (!current) {
212
- continue;
213
- }
214
-
215
- // trigger deployment since we see a new matching asset
216
- if (!previous) {
217
- return current.url;
218
- }
219
-
220
- if (deploymentConfig.compareContentHashes) {
221
- if (current.contentHash === previous.contentHash) {
222
- continue;
223
- }
224
-
225
- if (await hasCachedAsset(db, current, urlRegex, interval)) {
226
- db.log.info('contentHash already known', {current, previous});
227
- continue;
228
- }
229
- }
230
-
231
- if (deploymentConfig.compareVersionStrings) {
232
- const prevVersion = previous.url.match(urlRegex)[1];
233
- const currentVersion = current.url.match(urlRegex)[1];
234
- if (currentVersion < prevVersion) {
235
- db.log.info('content version has not increased', {current, previous});
236
- continue;
237
- }
238
- }
239
-
240
- return current.url;
241
- }
242
-
243
- return false;
244
- }
245
-
246
- async function findAsset(db, urlRegex, origin, from, to) {
247
- return await db["speedKit.Asset"].find()
248
- .where(getQuery(urlRegex, origin))
249
- .le('createdAt', to || new Date(Date.now() + MINUTE))
250
- .gt('createdAt', from || new Date(1970, 0, 1))
251
- .descending('createdAt')
252
- .singleResult();
253
- }
254
-
255
- async function findNewAsset(db, from, origins) {
256
- const result = await db["speedKit.Asset"].find()
257
- .where(getBaseQuery(...origins))
258
- .gt('createdAt', from || new Date(1970, 0, 1))
259
- .resultList();
260
-
261
- return new Set(result.map(asset => asset.origin));
262
- }
263
-
264
- async function hasAssetChanged(asset1, asset2) {
265
- const res1 = await fetch(asset1.url);
266
- const text1 = await res1.text();
267
-
268
- const res2 = await fetch(asset2.url);
269
- const text2 = await res2.text();
270
-
271
- return text1 !== text2;
272
- }
273
-
274
- /**
275
- * Check whether we have seen the hash before
276
- */
277
- async function hasCachedAsset(db, asset, urlRegex, to) {
278
- return (await db["speedKit.Asset"].find()
279
- .where(getQuery(urlRegex, asset.origin))
280
- .equal('contentType', asset.contentType)
281
- .equal('variation', asset.variation)
282
- .equal('contentHash', asset.contentHash)
283
- .le('createdAt', to || new Date(Date.now() + MINUTE))
284
- .count()
285
- ) > 0;
286
- }
287
-
288
- async function runDeploymentJob(db, origin, reason) {
289
- const status = new db['jobs.Status']();
290
- const jobId = getDeploymentJobId(origin);
291
- const cronjob = await db['jobs.Definition'].load(jobId);
292
- // TODO: quite hacky right now we may need a way to pass additional parameters to the run job code
293
- cronjob.data.reason = reason;
294
- status.cronjob = cronjob;
295
-
296
- try {
297
- await executeRefresh(db, status, cronjob);
298
- } catch(error) {
299
- status.status = 'failed';
300
- status.error = error;
301
- await status.save();
302
- }
303
- }
@@ -1,5 +0,0 @@
1
- export default class PrewarmService {
2
- static fetchURLByIp(ip: string, url: string, origin: string, variation: string, app: string, token: string): Promise<any>;
3
- static fetchURL(url: string, origin: string, variation: string, app: string, token: string, sortParameters: boolean): Promise<any>;
4
- static chunk(array: any[], length: number): any[];
5
- }
@@ -1,64 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const tslib_1 = require("tslib");
4
- const normalize_1 = require("../helpers/normalize");
5
- const node_fetch_1 = tslib_1.__importDefault(require("node-fetch"));
6
- const node_https_1 = tslib_1.__importDefault(require("node:https"));
7
- const agent = new node_https_1.default.Agent({
8
- rejectUnauthorized: false,
9
- keepAlive: true,
10
- });
11
- // eslint-disable-next-line unicorn/no-static-only-class
12
- class PrewarmService {
13
- // eslint-disable-next-line max-params
14
- static async fetchURLByIp(ip, url, origin, variation, app, token) {
15
- if (variation && variation !== "DEFAULT" && variation !== "DESKTOP") {
16
- url =
17
- url +
18
- (url.includes("?") ? "&" : "?") +
19
- `bqvariation=${variation.toLowerCase()}`;
20
- }
21
- url += (url.includes("?") ? "&" : "?") + `bqpass=1`;
22
- return (0, node_fetch_1.default)(`https://${ip}/v1/asset/${url}`, {
23
- agent,
24
- method: "HEAD",
25
- headers: {
26
- Host: `${app}.app.baqend.com`,
27
- Origin: origin,
28
- authorization: `BAT ${token}`,
29
- "Accept-Encoding": "gzip, deflate, br",
30
- },
31
- timeout: 10_000,
32
- });
33
- }
34
- // eslint-disable-next-line max-params
35
- static async fetchURL(url, origin, variation, app, token, sortParameters) {
36
- let normalizedUrl = (0, normalize_1.normalize)(url, sortParameters);
37
- if (variation && variation !== "DEFAULT" && variation !== "DESKTOP") {
38
- normalizedUrl +=
39
- (normalizedUrl.includes("?") ? "&" : "?") +
40
- `bqvariation=${variation.toLowerCase()}`;
41
- }
42
- normalizedUrl += (normalizedUrl.includes("?") ? "&" : "?") + `bqpass=1`;
43
- return (0, node_fetch_1.default)(`https://${app}.app.baqend.com/v1/asset/${normalizedUrl}`, {
44
- agent,
45
- method: "HEAD",
46
- headers: {
47
- Origin: origin,
48
- authorization: `BAT ${token}`,
49
- "Accept-Encoding": "gzip, deflate, br",
50
- },
51
- timeout: 10_000,
52
- });
53
- }
54
- static chunk(array, length) {
55
- const chunks = [];
56
- const n = array.length;
57
- let index = 0;
58
- while (index < n) {
59
- chunks.push(array.slice(index, (index += length)));
60
- }
61
- return chunks;
62
- }
63
- }
64
- exports.default = PrewarmService;