@exdst-sitecore-content-sdk/astro 0.0.16 → 0.0.19

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 (110) hide show
  1. package/README.md +3 -3
  2. package/package.json +41 -42
  3. package/src/client/index.ts +1 -1
  4. package/src/client/sitecore-astro-client.test.ts +41 -20
  5. package/src/client/sitecore-astro-client.ts +74 -57
  6. package/src/components/AstroImage.astro +2 -2
  7. package/src/components/AstroImage.astro.test.ts +542 -0
  8. package/src/components/Date.astro +5 -1
  9. package/src/components/Date.astro.test.ts +197 -0
  10. package/src/components/DefaultEmptyFieldEditingComponentImage.astro +4 -0
  11. package/src/components/DefaultEmptyFieldEditingComponentText.astro +4 -0
  12. package/src/components/EditingScripts.astro +2 -2
  13. package/src/components/EditingScripts.astro.test.ts +267 -0
  14. package/src/components/ErrorBoundary.astro +8 -9
  15. package/src/components/ErrorBoundary.astro.test.ts +252 -0
  16. package/src/components/ErrorComponent.astro +16 -0
  17. package/src/components/ErrorComponent.astro.test.ts +31 -0
  18. package/src/components/FieldMetadata.astro +1 -1
  19. package/src/components/FieldMetadata.astro.test.ts +40 -0
  20. package/src/components/File.astro +5 -1
  21. package/src/components/File.astro.test.ts +68 -0
  22. package/src/components/HiddenRendering.astro.test.ts +36 -0
  23. package/src/components/Image.astro +18 -4
  24. package/src/components/Image.astro.test.ts +438 -0
  25. package/src/components/Link.astro +13 -1
  26. package/src/components/Link.astro.test.ts +261 -0
  27. package/src/components/MissingComponent.astro.test.ts +21 -0
  28. package/src/components/Placeholder/Placeholder.astro +18 -23
  29. package/src/components/Placeholder/Placeholder.astro.test.ts +1088 -0
  30. package/src/components/Placeholder/PlaceholderMetadata.astro +24 -18
  31. package/src/components/Placeholder/PlaceholderMetadata.astro.test.ts +228 -0
  32. package/src/components/Placeholder/PlaceholderUtils.astro +21 -40
  33. package/src/components/Placeholder/PlaceholderUtils.astro.test.ts +149 -0
  34. package/src/components/Placeholder/models.ts +26 -17
  35. package/src/components/Placeholder/placeholder-utils.test.ts +153 -6
  36. package/src/components/Placeholder/placeholder-utils.ts +33 -11
  37. package/src/components/RichText.astro +9 -1
  38. package/src/components/RichText.astro.test.ts +205 -0
  39. package/src/components/Text.astro +15 -3
  40. package/src/components/Text.astro.test.ts +273 -0
  41. package/src/config/define-config.test.ts +5 -5
  42. package/src/config/define-config.ts +22 -42
  43. package/src/config-cli/define-cli-config.test.ts +5 -12
  44. package/src/config-cli/define-cli-config.ts +4 -8
  45. package/src/context.ts +42 -11
  46. package/src/debug.ts +13 -0
  47. package/src/editing/editing-config-middleware.test.ts +5 -7
  48. package/src/editing/editing-config-middleware.ts +11 -7
  49. package/src/editing/editing-render-middleware.test.ts +366 -24
  50. package/src/editing/editing-render-middleware.ts +34 -12
  51. package/src/editing/index.ts +2 -0
  52. package/src/editing/render-middleware.test.ts +1 -1
  53. package/src/editing/render-middleware.ts +1 -1
  54. package/src/editing/types.ts +39 -0
  55. package/src/editing/utils.test.ts +364 -4
  56. package/src/editing/utils.ts +82 -24
  57. package/src/enhancers/WithEmptyFieldEditingComponent.astro +1 -1
  58. package/src/enhancers/WithEmptyFieldEditingComponent.astro.test.ts +380 -0
  59. package/src/enhancers/WithFieldMetadata.astro.test.ts +113 -0
  60. package/src/index.ts +10 -7
  61. package/src/middleware/index.ts +4 -12
  62. package/src/middleware/middleware.test.ts +13 -0
  63. package/src/middleware/middleware.ts +12 -3
  64. package/src/middleware/multisite-middleware.test.ts +45 -50
  65. package/src/middleware/multisite-middleware.ts +33 -6
  66. package/src/middleware/robots-middleware.test.ts +20 -4
  67. package/src/middleware/robots-middleware.ts +10 -3
  68. package/src/middleware/sitemap-middleware.test.ts +35 -3
  69. package/src/middleware/sitemap-middleware.ts +7 -6
  70. package/src/services/component-props-service.ts +7 -6
  71. package/src/sharedTypes/component-props.ts +15 -4
  72. package/src/site/index.ts +1 -1
  73. package/src/tests/astro-helpers.ts +61 -0
  74. package/src/tests/test-components/CustomErrorComponent.astro +3 -0
  75. package/src/tests/test-components/CustomHiddenRendering.astro +10 -0
  76. package/src/tests/test-components/CustomMissingComponent.astro +9 -0
  77. package/src/tests/test-components/DownloadCallout.astro +12 -0
  78. package/src/tests/test-components/EmptyFieldEditingComponent.astro +5 -0
  79. package/src/tests/test-components/ErrorBoundaryWithError.astro +10 -0
  80. package/src/tests/test-components/Home.astro +12 -0
  81. package/src/tests/test-components/SxaRichText.astro +23 -0
  82. package/src/tests/test-components/SxaRichTextDefault.astro +7 -0
  83. package/src/tests/test-components/SxaRichTextWithTitle.astro +8 -0
  84. package/src/tests/test-components/TestComponent.astro +9 -0
  85. package/src/tests/test-components/TestComponentWithError.astro +4 -0
  86. package/src/tests/test-components/TestComponentWithField.astro +17 -0
  87. package/src/tests/test-components/TestHeader.astro +8 -0
  88. package/src/tests/test-components/TestLogo.astro +5 -0
  89. package/src/tests/test-components/TestParentWrapperComponent.astro +5 -0
  90. package/src/tests/test-components/TestWrapperComponent.astro +5 -0
  91. package/src/tests/test-data/metadata-data.ts +86 -0
  92. package/src/tests/test-data/normal-mode-data.ts +466 -0
  93. package/src/tests/vitest.setup.ts +4 -0
  94. package/src/tools/generate-map.ts +4 -3
  95. package/src/tools/index.ts +2 -4
  96. package/src/tools/templating/components.test.ts +100 -87
  97. package/src/tools/templating/components.ts +2 -1
  98. package/src/tools/templating/default-component.ts +3 -8
  99. package/src/utils/utils.ts +20 -2
  100. /package/src/{test-data → tests}/helpers.ts +0 -0
  101. /package/src/{test-data → tests}/personalizeData.ts +0 -0
  102. /package/src/{test-data/components → tests/test-components/map-components}/Bar.astro +0 -0
  103. /package/src/{test-data/components → tests/test-components/map-components}/Baz.astro +0 -0
  104. /package/src/{test-data/components → tests/test-components/map-components}/Foo.astro +0 -0
  105. /package/src/{test-data/components → tests/test-components/map-components}/Hero.variant.astro +0 -0
  106. /package/src/{test-data/components → tests/test-components/map-components}/NotComponent.bsx +0 -0
  107. /package/src/{test-data/components → tests/test-components/map-components}/Qux.astro +0 -0
  108. /package/src/{test-data/components → tests/test-components/map-components}/folded/Folded.astro +0 -0
  109. /package/src/{test-data/components → tests/test-components/map-components}/folded/random-file-2.docx +0 -0
  110. /package/src/{test-data/components → tests/test-components/map-components}/random-file.txt +0 -0
@@ -7,13 +7,14 @@ import {
7
7
  QUERY_PARAM_EDITING_SECRET,
8
8
  DesignLibraryMode,
9
9
  PREVIEW_KEY,
10
- } from '@sitecore-content-sdk/core/editing';
11
- import { DEFAULT_VARIANT } from '@sitecore-content-sdk/core/personalize';
12
- import { SITE_KEY } from '@sitecore-content-sdk/core/site';
10
+ } from '@sitecore-content-sdk/content/editing';
11
+ import { DEFAULT_VARIANT } from '@sitecore-content-sdk/content/personalize';
12
+ import { SITE_KEY } from '@sitecore-content-sdk/content/site';
13
13
  import { NativeDataFetcher } from '@sitecore-content-sdk/core';
14
14
  import {
15
15
  getEditingSecretFromRequest,
16
16
  mapEditingParams,
17
+ getAllowedQueryParams,
17
18
  cleanupPreviewCookies,
18
19
  getPreviewCookies,
19
20
  getRequiredEditingParamsList,
@@ -28,7 +29,7 @@ import {
28
29
  QUERY_PARAM_VERCEL_PROTECTION_BYPASS,
29
30
  QUERY_PARAM_VERCEL_SET_BYPASS_COOKIE,
30
31
  } from './constants';
31
- import { mockRequest } from '../test-data/helpers';
32
+ import { mockRequest } from '../tests/helpers';
32
33
 
33
34
  chai.use(sinonChai);
34
35
 
@@ -186,6 +187,352 @@ describe('editing/utils', () => {
186
187
  });
187
188
  });
188
189
 
190
+ describe('getAllowedQueryParams', () => {
191
+ it('should return empty results when allowedParams is undefined', () => {
192
+ const queryParams = { param1: 'value1', param2: 'value2' };
193
+
194
+ const result = getAllowedQueryParams(queryParams);
195
+
196
+ expect(result).to.deep.equal({
197
+ missingAllowedParams: [],
198
+ allowedQueryParams: {},
199
+ });
200
+ });
201
+
202
+ it('should return empty results when allowedParams is an empty array', () => {
203
+ const queryParams = { param1: 'value1', param2: 'value2' };
204
+
205
+ const result = getAllowedQueryParams(queryParams, []);
206
+
207
+ expect(result).to.deep.equal({
208
+ missingAllowedParams: [],
209
+ allowedQueryParams: {},
210
+ });
211
+ });
212
+
213
+ it('should extract allowed parameters when they exist in queryParams', () => {
214
+ const queryParams = {
215
+ param1: 'value1',
216
+ param2: 'value2',
217
+ param3: 'value3',
218
+ };
219
+ const allowedParams = [{ name: 'param1' }, { name: 'param3' }];
220
+
221
+ const result = getAllowedQueryParams(queryParams, allowedParams);
222
+
223
+ expect(result).to.deep.equal({
224
+ missingAllowedParams: [],
225
+ allowedQueryParams: {
226
+ param1: 'value1',
227
+ param3: 'value3',
228
+ },
229
+ });
230
+ });
231
+
232
+ it('should handle missing required parameters', () => {
233
+ const queryParams = { param1: 'value1' };
234
+ const allowedParams = [
235
+ { name: 'param1', required: true },
236
+ { name: 'param2', required: true },
237
+ { name: 'param3', required: true },
238
+ ];
239
+
240
+ const result = getAllowedQueryParams(queryParams, allowedParams);
241
+
242
+ expect(result).to.deep.equal({
243
+ missingAllowedParams: ['param2', 'param3'],
244
+ allowedQueryParams: {
245
+ param1: 'value1',
246
+ },
247
+ });
248
+ });
249
+
250
+ it('should not include missing optional parameters in missingAllowedParams', () => {
251
+ const queryParams = { param1: 'value1' };
252
+ const allowedParams = [
253
+ { name: 'param1' },
254
+ { name: 'param2' },
255
+ { name: 'param3', required: false },
256
+ ];
257
+
258
+ const result = getAllowedQueryParams(queryParams, allowedParams);
259
+
260
+ expect(result).to.deep.equal({
261
+ missingAllowedParams: [],
262
+ allowedQueryParams: {
263
+ param1: 'value1',
264
+ },
265
+ });
266
+ });
267
+
268
+ it('should handle mixed required and optional parameters', () => {
269
+ const queryParams = {
270
+ requiredParam1: 'value1',
271
+ optionalParam1: 'value2',
272
+ };
273
+ const allowedParams = [
274
+ { name: 'requiredParam1', required: true },
275
+ { name: 'requiredParam2', required: true },
276
+ { name: 'optionalParam1' },
277
+ { name: 'optionalParam2' },
278
+ ];
279
+
280
+ const result = getAllowedQueryParams(queryParams, allowedParams);
281
+
282
+ expect(result).to.deep.equal({
283
+ missingAllowedParams: ['requiredParam2'],
284
+ allowedQueryParams: {
285
+ requiredParam1: 'value1',
286
+ optionalParam1: 'value2',
287
+ },
288
+ });
289
+ });
290
+
291
+ it('should call resolver function with query parameter keys', () => {
292
+ const queryParams = {
293
+ param1: 'value1',
294
+ param2: 'value2',
295
+ param3: 'value3',
296
+ };
297
+ const resolver = sandbox
298
+ .stub()
299
+ .returns([{ name: 'param1', required: true }, { name: 'param2' }]);
300
+
301
+ const result = getAllowedQueryParams(queryParams, resolver);
302
+
303
+ expect(resolver).to.have.been.calledOnce;
304
+ expect(resolver).to.have.been.calledWith(['param1', 'param2', 'param3']);
305
+ expect(result).to.deep.equal({
306
+ missingAllowedParams: [],
307
+ allowedQueryParams: {
308
+ param1: 'value1',
309
+ param2: 'value2',
310
+ },
311
+ });
312
+ });
313
+
314
+ it('should handle resolver function returning empty array', () => {
315
+ const queryParams = { param1: 'value1' };
316
+ const resolver = sandbox.stub().returns([]);
317
+
318
+ const result = getAllowedQueryParams(queryParams, resolver);
319
+
320
+ expect(resolver).to.have.been.calledOnce;
321
+ expect(result).to.deep.equal({
322
+ missingAllowedParams: [],
323
+ allowedQueryParams: {},
324
+ });
325
+ });
326
+
327
+ it('should handle resolver function with missing required parameters', () => {
328
+ const queryParams = { param1: 'value1' };
329
+ const resolver = sandbox.stub().returns([
330
+ { name: 'param1', required: true },
331
+ { name: 'param2', required: true },
332
+ ]);
333
+
334
+ const result = getAllowedQueryParams(queryParams, resolver);
335
+
336
+ expect(result).to.deep.equal({
337
+ missingAllowedParams: ['param2'],
338
+ allowedQueryParams: {
339
+ param1: 'value1',
340
+ },
341
+ });
342
+ });
343
+
344
+ it('should handle undefined query parameter values correctly', () => {
345
+ const queryParams = {
346
+ param1: 'value1',
347
+ param2: undefined,
348
+ param3: 'value3',
349
+ };
350
+ const allowedParams = [
351
+ { name: 'param1' },
352
+ { name: 'param2', required: true },
353
+ { name: 'param3' },
354
+ ];
355
+
356
+ const result = getAllowedQueryParams(queryParams, allowedParams);
357
+
358
+ expect(result).to.deep.equal({
359
+ missingAllowedParams: ['param2'],
360
+ allowedQueryParams: {
361
+ param1: 'value1',
362
+ param3: 'value3',
363
+ },
364
+ });
365
+ });
366
+
367
+ it('should handle various data types in query parameter values', () => {
368
+ const queryParams = {
369
+ stringParam: 'string-value',
370
+ numberParam: 123,
371
+ booleanParam: true,
372
+ arrayParam: ['val1', 'val2'],
373
+ objectParam: { nested: 'value' },
374
+ };
375
+ const allowedParams = [
376
+ { name: 'stringParam' },
377
+ { name: 'numberParam' },
378
+ { name: 'booleanParam' },
379
+ { name: 'arrayParam' },
380
+ { name: 'objectParam' },
381
+ ];
382
+
383
+ const result = getAllowedQueryParams(queryParams, allowedParams);
384
+
385
+ expect(result).to.deep.equal({
386
+ missingAllowedParams: [],
387
+ allowedQueryParams: {
388
+ stringParam: 'string-value',
389
+ numberParam: 123,
390
+ booleanParam: true,
391
+ arrayParam: ['val1', 'val2'],
392
+ objectParam: { nested: 'value' },
393
+ },
394
+ });
395
+ });
396
+
397
+ it('should handle empty queryParams object', () => {
398
+ const queryParams = {};
399
+ const allowedParams = [{ name: 'param1', required: true }, { name: 'param2' }];
400
+
401
+ const result = getAllowedQueryParams(queryParams, allowedParams);
402
+
403
+ expect(result).to.deep.equal({
404
+ missingAllowedParams: ['param1'],
405
+ allowedQueryParams: {},
406
+ });
407
+ });
408
+
409
+ it('should handle null and false values as valid (not undefined)', () => {
410
+ const queryParams = {
411
+ nullParam: null,
412
+ falseParam: false,
413
+ zeroParam: 0,
414
+ emptyStringParam: '',
415
+ };
416
+ const allowedParams = [
417
+ { name: 'nullParam' },
418
+ { name: 'falseParam' },
419
+ { name: 'zeroParam' },
420
+ { name: 'emptyStringParam' },
421
+ ];
422
+
423
+ const result = getAllowedQueryParams(queryParams, allowedParams);
424
+
425
+ expect(result).to.deep.equal({
426
+ missingAllowedParams: [],
427
+ allowedQueryParams: {
428
+ nullParam: null,
429
+ falseParam: false,
430
+ zeroParam: 0,
431
+ emptyStringParam: '',
432
+ },
433
+ });
434
+ });
435
+
436
+ it('should handle array with string parameters (optional by default)', () => {
437
+ const queryParams = {
438
+ param1: 'value1',
439
+ param2: 'value2',
440
+ param3: 'value3',
441
+ };
442
+ const allowedParams = ['param1', 'param2', 'missingParam'];
443
+
444
+ const result = getAllowedQueryParams(queryParams, allowedParams);
445
+
446
+ expect(result).to.deep.equal({
447
+ missingAllowedParams: [],
448
+ allowedQueryParams: {
449
+ param1: 'value1',
450
+ param2: 'value2',
451
+ },
452
+ });
453
+ });
454
+
455
+ it('should handle mixed array with strings and objects', () => {
456
+ const queryParams = {
457
+ stringParam1: 'value1',
458
+ stringParam2: 'value2',
459
+ requiredParam: 'required-value',
460
+ optionalParam: 'optional-value',
461
+ };
462
+ const allowedParams = [
463
+ 'stringParam1',
464
+ 'stringParam2',
465
+ 'missingStringParam',
466
+ { name: 'requiredParam', required: true },
467
+ { name: 'optionalParam', required: false },
468
+ { name: 'missingRequiredParam', required: true },
469
+ ];
470
+
471
+ const result = getAllowedQueryParams(queryParams, allowedParams);
472
+
473
+ expect(result).to.deep.equal({
474
+ missingAllowedParams: ['missingRequiredParam'],
475
+ allowedQueryParams: {
476
+ stringParam1: 'value1',
477
+ stringParam2: 'value2',
478
+ requiredParam: 'required-value',
479
+ optionalParam: 'optional-value',
480
+ },
481
+ });
482
+ });
483
+
484
+ it('should handle resolver function returning strings', () => {
485
+ const queryParams = {
486
+ prefixedParam1: 'value1',
487
+ prefixedParam2: 'value2',
488
+ otherParam: 'value3',
489
+ };
490
+ const resolver = sandbox.stub().returns(['prefixedParam1', 'prefixedParam2']);
491
+
492
+ const result = getAllowedQueryParams(queryParams, resolver);
493
+
494
+ expect(resolver).to.have.been.calledOnce;
495
+ expect(result).to.deep.equal({
496
+ missingAllowedParams: [],
497
+ allowedQueryParams: {
498
+ prefixedParam1: 'value1',
499
+ prefixedParam2: 'value2',
500
+ },
501
+ });
502
+ });
503
+
504
+ it('should handle resolver function returning mixed strings and objects', () => {
505
+ const queryParams = {
506
+ stringParam1: 'value1',
507
+ stringParam2: 'value2',
508
+ requiredParam: 'required-value',
509
+ optionalParam: 'optional-value',
510
+ };
511
+ const resolver = sandbox
512
+ .stub()
513
+ .returns([
514
+ 'stringParam1',
515
+ 'stringParam2',
516
+ { name: 'requiredParam', required: true },
517
+ { name: 'optionalParam', required: false },
518
+ { name: 'missingRequiredParam', required: true },
519
+ ]);
520
+
521
+ const result = getAllowedQueryParams(queryParams, resolver);
522
+
523
+ expect(resolver).to.have.been.calledOnce;
524
+ expect(result).to.deep.equal({
525
+ missingAllowedParams: ['missingRequiredParam'],
526
+ allowedQueryParams: {
527
+ stringParam1: 'value1',
528
+ stringParam2: 'value2',
529
+ requiredParam: 'required-value',
530
+ optionalParam: 'optional-value',
531
+ },
532
+ });
533
+ });
534
+ });
535
+
189
536
  describe('cleanupPreviewCookies', () => {
190
537
  it('should return null when cookies is null', () => {
191
538
  const result = cleanupPreviewCookies(null);
@@ -793,6 +1140,19 @@ describe('editing/utils', () => {
793
1140
 
794
1141
  expect(result).to.equal('http://localhost:3000');
795
1142
  });
1143
+
1144
+ it('should use x-forwarded-host header when present', () => {
1145
+ const req = mockRequest({
1146
+ headers: {
1147
+ 'x-forwarded-host': 'proxy.example.com',
1148
+ host: 'internal-host.local',
1149
+ },
1150
+ });
1151
+
1152
+ const result = resolveServerUrl(req);
1153
+
1154
+ expect(result).to.equal('http://proxy.example.com');
1155
+ });
796
1156
  });
797
1157
 
798
1158
  describe('getCSPHeader', () => {
@@ -6,9 +6,9 @@ import {
6
6
  LayoutKind,
7
7
  PREVIEW_KEY,
8
8
  QUERY_PARAM_EDITING_SECRET,
9
- } from '@sitecore-content-sdk/core/editing';
10
- import { DEFAULT_VARIANT } from '@sitecore-content-sdk/core/personalize';
11
- import { SITE_KEY } from '@sitecore-content-sdk/core/site';
9
+ } from '@sitecore-content-sdk/content/editing';
10
+ import { DEFAULT_VARIANT } from '@sitecore-content-sdk/content/personalize';
11
+ import { SITE_KEY } from '@sitecore-content-sdk/content/site';
12
12
  import {
13
13
  EDITING_PASS_THROUGH_HEADERS,
14
14
  QUERY_PARAM_VERCEL_PROTECTION_BYPASS,
@@ -16,7 +16,8 @@ import {
16
16
  } from './constants';
17
17
  import { IncomingHttpHeaders } from 'http';
18
18
  import { NativeDataFetcher } from '@sitecore-content-sdk/core';
19
- import { getAllowedOriginsFromEnv } from '@sitecore-content-sdk/core/utils';
19
+ import { getAllowedOriginsFromEnv } from '@sitecore-content-sdk/core/tools';
20
+ import { AllowedQueryParams, GetAllowedQueryParamsResult } from './types';
20
21
 
21
22
  /**
22
23
  * Gets editing secret value from request
@@ -66,8 +67,49 @@ export const mapEditingParams = (query: {
66
67
  return params;
67
68
  };
68
69
 
70
+ /**
71
+ * Parses the query parameters based on the provided allowed parameters or a resolver function, to extract additional parameters that should be allowed.
72
+ * @param {{ [key: string]: string | undefined }} queryParams Object of query parameters from incoming URL.
73
+ * @param {AllowedQueryParams} allowedParams Allowed parameters to map.
74
+ * @returns Object containing the list of missing required parameters and the allowed query parameters that were extracted.
75
+ * @internal
76
+ */
77
+ export const getAllowedQueryParams = (
78
+ queryParams: { [key: string]: unknown },
79
+ allowedParams?: AllowedQueryParams
80
+ ): GetAllowedQueryParamsResult => {
81
+ const allowedQueryParamsList =
82
+ typeof allowedParams === 'function'
83
+ ? allowedParams(Object.keys(queryParams))
84
+ : Array.isArray(allowedParams)
85
+ ? allowedParams
86
+ : [];
87
+
88
+ if (!allowedQueryParamsList.length) return { missingAllowedParams: [], allowedQueryParams: {} };
89
+
90
+ return allowedQueryParamsList.reduce(
91
+ (acc, param) => {
92
+ const name = typeof param === 'string' ? param : param.name;
93
+ const required = typeof param === 'string' ? false : param.required;
94
+
95
+ const value = queryParams[name];
96
+ if (value !== undefined) {
97
+ acc.allowedQueryParams[name] = value;
98
+
99
+ return acc;
100
+ }
101
+
102
+ if (required) acc.missingAllowedParams.push(name);
103
+
104
+ return acc;
105
+ },
106
+ { missingAllowedParams: [], allowedQueryParams: {} } as GetAllowedQueryParamsResult
107
+ );
108
+ };
109
+
69
110
  /**
70
111
  * Preview cookies enum
112
+ * @public
71
113
  */
72
114
  export enum PreviewCookies {
73
115
  PREVIEW_DATA = '_preview_data',
@@ -127,6 +169,7 @@ export const getRequiredEditingParamsList = (mode: EditingRenderQueryParams['mod
127
169
  * Gets query parameters that should be passed along to subsequent requests (e.g. for deployment protection bypass)
128
170
  * @param {object} query URLSearchParams object from incoming URL
129
171
  * @returns object of approved query parameters
172
+ * @internal
130
173
  */
131
174
  export const getQueryParamsForPropagation = (
132
175
  query: Partial<{ [key: string]: string | string[] }>
@@ -149,6 +192,7 @@ export const getQueryParamsForPropagation = (
149
192
  * Get headers that should be passed along to subsequent requests
150
193
  * @param {IncomingHttpHeaders | Headers} headers Incoming HTTP Headers
151
194
  * @returns Object of approved headers
195
+ * @internal
152
196
  */
153
197
  export const getHeadersForPropagation = (
154
198
  headers: IncomingHttpHeaders | Headers
@@ -229,6 +273,7 @@ export const getEditingRequestHtml = async (
229
273
  * @param {object} data preview data to check
230
274
  * @returns true if the data is EditingPreviewData
231
275
  * @see EditingPreviewData
276
+ * @public
232
277
  */
233
278
  export const isDesignLibraryPreviewData = (
234
279
  data: unknown
@@ -257,24 +302,23 @@ export const isDesignLibraryPreviewData = (
257
302
  * @param {Request} req
258
303
  */
259
304
  export const resolveServerUrl = (req: Request) => {
260
- const internalHostUrl =
261
- import.meta.env?.SITECORE_INTERNAL_EDITING_HOST_URL ||
262
- process.env.SITECORE_INTERNAL_EDITING_HOST_URL;
305
+ const internalHostUrl = process.env.SITECORE_INTERNAL_EDITING_HOST_URL;
263
306
  if (internalHostUrl) {
264
307
  return internalHostUrl;
265
308
  }
266
309
 
267
310
  // in xmc deployment we always use localhost:3000
268
- if (import.meta.env?.SITECORE || process.env.SITECORE) {
311
+ if (process.env.SITECORE) {
269
312
  return 'http://localhost:3000';
270
313
  }
271
314
 
272
315
  // to preserve auth headers, use https if we're in our 3 main hosting options
273
- const useHttps =
274
- (import.meta.env?.VERCEL || process.env.VERCEL ||
275
- import.meta.env?.NETLIFY || process.env.NETLIFY) !== undefined;
316
+ const useHttps = (process.env.VERCEL || process.env.NETLIFY) !== undefined;
317
+
318
+ const host = req.headers.get('x-forwarded-host') || req.headers.get('host');
319
+
276
320
  // use https for requests with auth but also support unsecured http rendering hosts
277
- return `${useHttps ? 'https' : 'http'}://${req.headers.get('host') ?? undefined}`;
321
+ return `${useHttps ? 'https' : 'http'}://${host || undefined}`;
278
322
  };
279
323
 
280
324
  /**
@@ -293,20 +337,34 @@ export const getCSPHeader = () => {
293
337
  * @returns object with query params
294
338
  */
295
339
  export const getEditingRenderQueryParams = (query: URLSearchParams): EditingRenderQueryParams => {
296
- const params = Object.fromEntries(query.entries());
340
+ const params: Record<string, string | string[]> = {};
341
+ query.forEach((_, key) => {
342
+ if (!(key in params)) {
343
+ const values = query.getAll(key);
344
+ params[key] = values.length === 1 ? values[0] : values;
345
+ }
346
+ });
347
+
348
+ const find = (key: string): string | undefined => {
349
+ const lowerKey = key.toLowerCase();
350
+ const match = Object.keys(params).find((k) => k.toLowerCase() === lowerKey);
351
+ if (!match) return undefined;
352
+ const value = params[match];
353
+ return Array.isArray(value) ? value[0] : value;
354
+ };
297
355
 
298
356
  return {
299
357
  ...params,
300
- secret: params.secret ?? '',
301
- sc_lang: params.sc_lang ?? '',
302
- sc_itemid: params.sc_itemid ?? '',
303
- sc_site: params.sc_site ?? '',
304
- route: params.route ?? '',
305
- mode: params.mode as EditingRenderQueryParams['mode'],
306
- sc_layoutKind: params.sc_layoutkind as LayoutKind,
307
- sc_variant: params.sc_variant ?? undefined,
308
- sc_version: params.sc_version ?? undefined,
309
- sc_renderingId: params.sc_renderingid ?? undefined,
310
- dataSourceId: params.datasourceid ?? undefined,
358
+ secret: find('secret') ?? '',
359
+ sc_lang: find('sc_lang') ?? '',
360
+ sc_itemid: find('sc_itemid') ?? '',
361
+ sc_site: find('sc_site') ?? '',
362
+ route: find('route') ?? '',
363
+ mode: find('mode') as EditingRenderQueryParams['mode'],
364
+ sc_layoutKind: find('sc_layoutKind') as LayoutKind,
365
+ sc_variant: find('sc_variant') ?? undefined,
366
+ sc_version: find('sc_version') ?? undefined,
367
+ sc_renderingId: find('sc_renderingId') ?? undefined,
368
+ dataSourceId: find('dataSourceId') ?? undefined,
311
369
  };
312
370
  };
@@ -8,7 +8,7 @@ import {
8
8
  Field,
9
9
  isFieldValueEmpty,
10
10
  FieldMetadata,
11
- } from '@sitecore-content-sdk/core/layout';
11
+ } from '@sitecore-content-sdk/content/layout';
12
12
  import { AstroContentSdkComponent } from '../sharedTypes/component-props';
13
13
 
14
14
  interface FieldComponentProps {