@decocms/blocks 7.1.2 → 7.2.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.
@@ -2,3 +2,29 @@ export { isBelowFold, LazySection, type LazySectionProps } from "./LazySection";
2
2
  export { LiveControls } from "./LiveControls";
3
3
  export { SectionErrorBoundary } from "./SectionErrorFallback";
4
4
  export { default as RenderSection } from "./RenderSection";
5
+
6
+ // Commerce UI primitives (moved from apps-start's commerce/components/).
7
+ export {
8
+ default as Image,
9
+ registerImageCdnDomain,
10
+ getImageCdnDomain,
11
+ getOptimizedMediaUrl,
12
+ getSrcSet,
13
+ FACTORS,
14
+ type ImageProps,
15
+ type FitOptions,
16
+ } from "./Image";
17
+ export { Picture, Source, type PictureProps, type SourceProps } from "./Picture";
18
+ export {
19
+ ProductJsonLd,
20
+ PLPJsonLd,
21
+ BreadcrumbJsonLd,
22
+ seoMetaTags,
23
+ type ProductJsonLdProps,
24
+ type PLPJsonLdProps,
25
+ type BreadcrumbJsonLdProps,
26
+ type SeoMetaProps,
27
+ type JsonLdProduct,
28
+ type JsonLdProductListingPage,
29
+ type JsonLdBreadcrumbList,
30
+ } from "./JsonLd";
@@ -1,9 +1,10 @@
1
- import { beforeEach, describe, expect, it } from "vitest";
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
2
  import type { MatcherContext } from "../cms/resolve";
3
3
  import { evaluateMatcher } from "../cms/resolve";
4
4
  import { registerBuiltinMatchers } from "./builtins";
5
5
 
6
6
  const LOCATION_KEY = "website/matchers/location.ts";
7
+ const DATE_KEY = "website/matchers/date.ts";
7
8
 
8
9
  beforeEach(() => {
9
10
  registerBuiltinMatchers();
@@ -249,3 +250,69 @@ describe("locationMatcher — data source fallbacks", () => {
249
250
  expect(match({ includeLocations: [{ regionCode: "RJ" }] }, ctx)).toBe(false);
250
251
  });
251
252
  });
253
+
254
+ function matchDate(rule: Record<string, unknown>): boolean {
255
+ return evaluateMatcher({ ...rule, __resolveType: DATE_KEY }, {});
256
+ }
257
+
258
+ describe("dateMatcher — parity with deco-cx/apps website/matchers/date.ts", () => {
259
+ beforeEach(() => {
260
+ vi.useFakeTimers();
261
+ vi.setSystemTime(new Date("2026-07-07T12:00:00Z"));
262
+ });
263
+
264
+ afterEach(() => {
265
+ vi.useRealTimers();
266
+ });
267
+
268
+ it("matches when neither start nor end is given (vacuously true)", () => {
269
+ expect(matchDate({})).toBe(true);
270
+ });
271
+
272
+ it("matches when now is after start and no end is given", () => {
273
+ expect(matchDate({ start: "2026-07-01T00:00:00Z" })).toBe(true);
274
+ });
275
+
276
+ it("does not match when now is before start", () => {
277
+ expect(matchDate({ start: "2026-08-01T00:00:00Z" })).toBe(false);
278
+ });
279
+
280
+ it("matches when now is before end and no start is given", () => {
281
+ expect(matchDate({ end: "2026-08-01T00:00:00Z" })).toBe(true);
282
+ });
283
+
284
+ it("does not match when now is after end", () => {
285
+ expect(matchDate({ end: "2026-06-01T00:00:00Z" })).toBe(false);
286
+ });
287
+
288
+ it("matches when now is strictly between start and end", () => {
289
+ expect(
290
+ matchDate({ start: "2026-07-01T00:00:00Z", end: "2026-07-31T00:00:00Z" }),
291
+ ).toBe(true);
292
+ });
293
+
294
+ it("does not match outside a [start, end] window in either direction", () => {
295
+ expect(
296
+ matchDate({ start: "2026-08-01T00:00:00Z", end: "2026-09-01T00:00:00Z" }),
297
+ ).toBe(false);
298
+ expect(
299
+ matchDate({ start: "2026-01-01T00:00:00Z", end: "2026-02-01T00:00:00Z" }),
300
+ ).toBe(false);
301
+ });
302
+
303
+ it("does not match at the exact start boundary instant (strict >, not >=)", () => {
304
+ expect(matchDate({ start: "2026-07-07T12:00:00Z" })).toBe(false);
305
+ });
306
+
307
+ it("does not match at the exact end boundary instant (strict <, not <=)", () => {
308
+ expect(matchDate({ end: "2026-07-07T12:00:00Z" })).toBe(false);
309
+ });
310
+
311
+ it("treats an invalid start date as non-matching (NaN comparison is always false)", () => {
312
+ expect(matchDate({ start: "not-a-date" })).toBe(false);
313
+ });
314
+
315
+ it("treats an invalid end date as non-matching", () => {
316
+ expect(matchDate({ end: "not-a-date" })).toBe(false);
317
+ });
318
+ });
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * These augment the matchers already handled inline in resolve.ts
5
5
  * (always, never, device, random, utm) with the additional matchers
6
- * that deco supported: cookie, cron, host, pathname, queryString,
6
+ * that deco supported: cookie, cron, date, host, pathname, queryString,
7
7
  * location, userAgent, environment, multi, negate.
8
8
  *
9
9
  * Register these at startup:
@@ -61,6 +61,28 @@ function cronMatcher(rule: Record<string, unknown>, _ctx: MatcherContext): boole
61
61
  return true;
62
62
  }
63
63
 
64
+ // -------------------------------------------------------------------------
65
+ // Date matcher — parity with deco-cx/apps website/matchers/date.ts
66
+ //
67
+ // Unlike cronMatcher's inclusive [start, end] window (>=, <=), date.ts uses
68
+ // strict inequalities: it matches only strictly *between* start and end, so
69
+ // a request landing on the exact boundary instant does not match. Invalid
70
+ // date strings produce NaN comparisons, which are always false — same
71
+ // "reject silently" behavior as the original (no explicit isNaN check
72
+ // needed to match upstream semantics).
73
+ // -------------------------------------------------------------------------
74
+
75
+ function dateMatcher(rule: Record<string, unknown>, _ctx: MatcherContext): boolean {
76
+ const start = rule.start as string | undefined;
77
+ const end = rule.end as string | undefined;
78
+
79
+ const now = new Date();
80
+ const afterStart = start ? now > new Date(start) : true;
81
+ const beforeEnd = end ? now < new Date(end) : true;
82
+
83
+ return afterStart && beforeEnd;
84
+ }
85
+
64
86
  // -------------------------------------------------------------------------
65
87
  // Host matcher
66
88
  // -------------------------------------------------------------------------
@@ -425,7 +447,7 @@ function negateMatcher(rule: Record<string, unknown>, ctx: MatcherContext): bool
425
447
  export function registerBuiltinMatchers(): void {
426
448
  registerMatcher("website/matchers/cookie.ts", cookieMatcher);
427
449
  registerMatcher("website/matchers/cron.ts", cronMatcher);
428
- registerMatcher("website/matchers/date.ts", cronMatcher);
450
+ registerMatcher("website/matchers/date.ts", dateMatcher);
429
451
  registerMatcher("website/matchers/host.ts", hostMatcher);
430
452
  registerMatcher("website/matchers/pathname.ts", pathnameMatcher);
431
453
  registerMatcher("website/matchers/queryString.ts", queryStringMatcher);