@replohq/sdk 0.6.1 → 0.8.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 (46) hide show
  1. package/_vendor/replo-utils/lib/url.d.ts +5 -0
  2. package/_vendor/schemas/generated/consent.d.ts +18 -1
  3. package/_vendor/schemas/routing/locale.d.ts +45 -0
  4. package/_vendor/schemas/routing/locale.mjs +95 -0
  5. package/_vendor/schemas/routing/localeNegotiation.d.ts +87 -0
  6. package/_vendor/schemas/routing/localeNegotiation.mjs +141 -0
  7. package/_vendor/schemas/routing/rules.mjs +1 -1
  8. package/analytics/analytics-provider.js +2 -0
  9. package/analytics/analytics-provider.js.map +2 -2
  10. package/analytics/get-analytics-sinks.d.ts +1 -0
  11. package/analytics/get-analytics-sinks.js +4 -1
  12. package/analytics/get-analytics-sinks.js.map +2 -2
  13. package/analytics/replo-pixel-script.d.ts +4 -0
  14. package/analytics/replo-pixel-script.js +10 -2
  15. package/analytics/replo-pixel-script.js.map +2 -2
  16. package/analytics/utils/analytics-utils.js +5 -6
  17. package/analytics/utils/analytics-utils.js.map +2 -2
  18. package/consent/consent-platform.d.ts +52 -0
  19. package/consent/consent-platform.js +223 -0
  20. package/consent/consent-platform.js.map +7 -0
  21. package/consent/consent-store.d.ts +4 -3
  22. package/consent/consent-store.js +28 -10
  23. package/consent/consent-store.js.map +2 -2
  24. package/consent/inject-script-descriptors.d.ts +3 -1
  25. package/consent/inject-script-descriptors.js +17 -1
  26. package/consent/inject-script-descriptors.js.map +2 -2
  27. package/consent/replo-scripts.d.ts +9 -2
  28. package/consent/replo-scripts.js +66 -7
  29. package/consent/replo-scripts.js.map +2 -2
  30. package/consent/script-snippets.d.ts +1 -1
  31. package/consent/script-snippets.js.map +2 -2
  32. package/consent/types.d.ts +13 -2
  33. package/consent/window-api.d.ts +4 -1
  34. package/consent/window-api.js +3 -0
  35. package/consent/window-api.js.map +2 -2
  36. package/lib/buildMetadata.js +3 -3
  37. package/package.json +14 -4
  38. package/routing/evaluate.d.ts +3 -1
  39. package/routing/evaluate.js +150 -6
  40. package/routing/evaluate.js.map +3 -3
  41. package/routing/geo.d.ts +9 -0
  42. package/routing/geo.js +17 -0
  43. package/routing/geo.js.map +7 -0
  44. package/routing/locale.d.ts +13 -0
  45. package/routing/locale.js +13 -0
  46. package/routing/locale.js.map +7 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../routing/evaluate.ts"],
4
- "sourcesContent": ["import type { NextRequest } from \"next/server\";\nimport type { RedirectRule } from \"schemas/routing/rules\";\n\nimport { NextResponse } from \"next/server\";\n\nimport { applyParams, matchSource } from \"schemas/routing/match\";\n\nexport function evaluateRouting({\n request,\n rules,\n}: {\n request: NextRequest;\n rules: readonly RedirectRule[];\n}): NextResponse {\n for (const rule of rules) {\n const response = evaluateRedirect({ request, rule });\n if (response) {\n return response;\n }\n }\n return NextResponse.next();\n}\n\nfunction evaluateRedirect({\n request,\n rule,\n}: {\n request: NextRequest;\n rule: RedirectRule;\n}): NextResponse | null {\n const params = matchSource({\n source: rule.source,\n pathname: request.nextUrl.pathname,\n });\n if (!params) {\n return null;\n }\n const url = new URL(\n applyParams({ value: rule.destination, params }),\n request.nextUrl.origin,\n );\n if (url.origin === request.nextUrl.origin && !url.search) {\n url.search = request.nextUrl.search;\n }\n // A destination that lands back on the current request would loop. The rule\n // still consumes the request \u2014 later rules were authored as alternatives to\n // it, not fallbacks for it \u2014 so the page renders normally instead.\n const redirectsToCurrentRequest =\n url.origin === request.nextUrl.origin &&\n normalizePath(url.pathname) === normalizePath(request.nextUrl.pathname) &&\n url.search === request.nextUrl.search;\n if (redirectsToCurrentRequest) {\n return NextResponse.next();\n }\n return NextResponse.redirect(url, rule.status);\n}\n\n/** Trailing slashes carry no meaning, so `/blogs` and `/blogs/` are the same. */\nfunction normalizePath(pathname: string): string {\n const trimmed = pathname.replace(/\\/+$/, \"\");\n return trimmed === \"\" ? \"/\" : trimmed;\n}\n"],
5
- "mappings": "AAGA,SAAS,oBAAoB;AAE7B,SAAS,aAAa,mBAAmB;AAElC,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AACF,GAGiB;AACf,aAAW,QAAQ,OAAO;AACxB,UAAM,WAAW,iBAAiB,EAAE,SAAS,KAAK,CAAC;AACnD,QAAI,UAAU;AACZ,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO,aAAa,KAAK;AAC3B;AAEA,SAAS,iBAAiB;AAAA,EACxB;AAAA,EACA;AACF,GAGwB;AACtB,QAAM,SAAS,YAAY;AAAA,IACzB,QAAQ,KAAK;AAAA,IACb,UAAU,QAAQ,QAAQ;AAAA,EAC5B,CAAC;AACD,MAAI,CAAC,QAAQ;AACX,WAAO;AAAA,EACT;AACA,QAAM,MAAM,IAAI;AAAA,IACd,YAAY,EAAE,OAAO,KAAK,aAAa,OAAO,CAAC;AAAA,IAC/C,QAAQ,QAAQ;AAAA,EAClB;AACA,MAAI,IAAI,WAAW,QAAQ,QAAQ,UAAU,CAAC,IAAI,QAAQ;AACxD,QAAI,SAAS,QAAQ,QAAQ;AAAA,EAC/B;AAIA,QAAM,4BACJ,IAAI,WAAW,QAAQ,QAAQ,UAC/B,cAAc,IAAI,QAAQ,MAAM,cAAc,QAAQ,QAAQ,QAAQ,KACtE,IAAI,WAAW,QAAQ,QAAQ;AACjC,MAAI,2BAA2B;AAC7B,WAAO,aAAa,KAAK;AAAA,EAC3B;AACA,SAAO,aAAa,SAAS,KAAK,KAAK,MAAM;AAC/C;AAGA,SAAS,cAAc,UAA0B;AAC/C,QAAM,UAAU,SAAS,QAAQ,QAAQ,EAAE;AAC3C,SAAO,YAAY,KAAK,MAAM;AAChC;",
6
- "names": []
4
+ "sourcesContent": ["import type { NextRequest } from \"next/server\";\nimport type { LocaleRoutingConfig } from \"schemas/routing/locale\";\nimport type {\n LocaleScope,\n NegotiatedLocale,\n PathLocale,\n} from \"schemas/routing/localeNegotiation\";\nimport type { RedirectRule } from \"schemas/routing/rules\";\n\nimport { NextResponse } from \"next/server\";\n\nimport { REPLO_LOCALE_COOKIE } from \"schemas/routing/locale\";\nimport {\n getLocaleFromPath,\n negotiateLocale,\n resolveLocaleScope,\n withLocalePrefix,\n} from \"schemas/routing/localeNegotiation\";\nimport { applyParams, matchSource } from \"schemas/routing/match\";\n\nimport { ipCountry } from \"./geo\";\n\nexport function evaluateRouting({\n request,\n rules,\n localeRouting,\n}: {\n request: NextRequest;\n rules: readonly RedirectRule[];\n localeRouting?: LocaleRoutingConfig;\n}): NextResponse {\n const localeContext = localeRouting\n ? getLocaleContext({ request, config: localeRouting })\n : null;\n for (const rule of rules) {\n const response = evaluateRedirect({ request, rule, localeContext });\n if (response) {\n return response;\n }\n }\n return evaluateLocalePhase({ request, localeContext });\n}\n\ninterface LocaleContext {\n config: LocaleRoutingConfig;\n scope: LocaleScope;\n /** A single-locale domain serves clean URLs whatever the config says. */\n urlMode: LocaleRoutingConfig[\"urlMode\"];\n pathLocale: PathLocale | null;\n negotiated: NegotiatedLocale;\n}\n\nfunction getLocaleContext({\n request,\n config,\n}: {\n request: NextRequest;\n config: LocaleRoutingConfig;\n}): LocaleContext {\n const scope = resolveLocaleScope({\n host: request.headers.get(\"host\"),\n config,\n });\n // The prefix is read against every configured locale, not just the scope's:\n // an explicitly requested out-of-scope locale is served rather than fought,\n // it just never wins negotiation or gets advertised in a cookie.\n const pathLocale = getLocaleFromPath({\n pathname: request.nextUrl.pathname,\n locales: config.locales,\n });\n const negotiated = negotiateLocale({\n scope,\n detection: config.detection,\n cookieLocale: request.cookies.get(REPLO_LOCALE_COOKIE)?.value ?? null,\n countryCode: ipCountry(request),\n acceptLanguageHeader: request.headers.get(\"accept-language\"),\n });\n return {\n config,\n scope,\n urlMode: scope.locales.length === 1 ? \"hidden\" : config.urlMode,\n pathLocale,\n negotiated,\n };\n}\n\nfunction evaluateRedirect({\n request,\n rule,\n localeContext,\n}: {\n request: NextRequest;\n rule: RedirectRule;\n localeContext: LocaleContext | null;\n}): NextResponse | null {\n // Raw first, so a locale-prefixed source targets exactly that locale; then\n // the locale-stripped path, so a rule authored against the logical path\n // covers every locale.\n const params =\n matchSource({\n source: rule.source,\n pathname: request.nextUrl.pathname,\n }) ??\n (localeContext?.pathLocale\n ? matchSource({\n source: rule.source,\n pathname: localeContext.pathLocale.logicalPath,\n })\n : null);\n if (!params) {\n return null;\n }\n const url = new URL(\n applyParams({ value: rule.destination, params }),\n request.nextUrl.origin,\n );\n if (url.origin === request.nextUrl.origin && !url.search) {\n url.search = request.nextUrl.search;\n }\n if (localeContext && url.origin === request.nextUrl.origin) {\n applyLocaleToDestination({ url, localeContext });\n }\n // A destination that lands back on the current request would loop. The rule\n // still consumes the request \u2014 later rules were authored as alternatives to\n // it, not fallbacks for it \u2014 so the page renders normally instead.\n const redirectsToCurrentRequest =\n url.origin === request.nextUrl.origin &&\n normalizePath(url.pathname) === normalizePath(request.nextUrl.pathname) &&\n url.search === request.nextUrl.search;\n if (redirectsToCurrentRequest) {\n return NextResponse.next();\n }\n const response = NextResponse.redirect(url, rule.status);\n // Hidden-mode destinations stay clean, so the locale an explicit prefix\n // carried would be lost across the hop \u2014 keep it in the cookie instead.\n if (\n localeContext?.pathLocale &&\n localeContext.urlMode === \"hidden\" &&\n url.origin === request.nextUrl.origin &&\n localeContext.scope.locales.includes(localeContext.pathLocale.locale)\n ) {\n setLocaleCookie({ response, locale: localeContext.pathLocale.locale });\n }\n return response;\n}\n\n/**\n * In prefixed mode every page lives under a locale, so an unprefixed\n * same-site destination gets one \u2014 the request's own locale when the URL\n * carried one, the negotiated locale otherwise \u2014 collapsing what would be a\n * second redirect hop. A destination that names a locale is left alone:\n * explicit always wins.\n */\nfunction applyLocaleToDestination({\n url,\n localeContext,\n}: {\n url: URL;\n localeContext: LocaleContext;\n}) {\n if (localeContext.urlMode !== \"prefixed\") {\n return;\n }\n if (isLocaleExemptPath(url.pathname)) {\n return;\n }\n const destinationLocale = getLocaleFromPath({\n pathname: url.pathname,\n locales: localeContext.config.locales,\n });\n if (destinationLocale) {\n // A non-canonically-cased prefix (`/fr-ca/x`) would otherwise 308 back to\n // the canonical spelling, re-match the rule, and loop forever \u2014 the loop\n // guard compares pathnames case-sensitively.\n if (destinationLocale.pathSegment !== destinationLocale.locale) {\n url.pathname = withLocalePrefix({\n locale: destinationLocale.locale,\n path: destinationLocale.logicalPath,\n });\n }\n return;\n }\n url.pathname = withLocalePrefix({\n locale: localeContext.pathLocale?.locale ?? localeContext.negotiated.locale,\n path: url.pathname,\n });\n}\n\nfunction evaluateLocalePhase({\n request,\n localeContext,\n}: {\n request: NextRequest;\n localeContext: LocaleContext | null;\n}): NextResponse {\n if (!localeContext) {\n return NextResponse.next();\n }\n const { pathname } = request.nextUrl;\n if (isLocaleExemptPath(pathname)) {\n return NextResponse.next();\n }\n const { urlMode, pathLocale, negotiated, scope } = localeContext;\n\n if (urlMode === \"prefixed\") {\n if (!pathLocale) {\n const url = request.nextUrl.clone();\n url.pathname = withLocalePrefix({\n locale: negotiated.locale,\n path: pathname,\n });\n // Temporary: the same URL legitimately serves different locales to\n // different visitors, so this must never cache as permanent.\n return NextResponse.redirect(url, 307);\n }\n if (pathLocale.pathSegment !== pathLocale.locale) {\n const url = request.nextUrl.clone();\n url.pathname = withLocalePrefix({\n locale: pathLocale.locale,\n path: pathLocale.logicalPath,\n });\n return NextResponse.redirect(url, 308);\n }\n const response = NextResponse.next();\n // A prefix that differs from what negotiation would pick is an explicit\n // choice \u2014 keep it sticky. A matching prefix proves nothing: the 307\n // above manufactures exactly that navigation, and persisting the guess\n // would freeze the first detection result for a year.\n if (\n pathLocale.locale !== negotiated.locale &&\n request.cookies.get(REPLO_LOCALE_COOKIE)?.value !== pathLocale.locale &&\n scope.locales.includes(pathLocale.locale)\n ) {\n setLocaleCookie({ response, locale: pathLocale.locale });\n }\n return response;\n }\n\n // Hidden mode: a locale-prefixed URL is the language-switcher mechanism \u2014\n // adopt the locale as a cookie and settle on the clean URL.\n if (pathLocale) {\n const url = request.nextUrl.clone();\n url.pathname = pathLocale.logicalPath;\n const response = NextResponse.redirect(url, 307);\n if (scope.locales.includes(pathLocale.locale)) {\n setLocaleCookie({ response, locale: pathLocale.locale });\n }\n return response;\n }\n const url = request.nextUrl.clone();\n url.pathname = withLocalePrefix({\n locale: negotiated.locale,\n path: pathname,\n });\n return NextResponse.rewrite(url);\n}\n\n// Requests for files and API routes have no locale variant; prefixing or\n// rewriting them would just 404.\nfunction isLocaleExemptPath(pathname: string): boolean {\n if (pathname === \"/api\" || pathname.startsWith(\"/api/\")) {\n return true;\n }\n const lastSegment = pathname.slice(pathname.lastIndexOf(\"/\") + 1);\n return lastSegment.includes(\".\");\n}\n\nfunction setLocaleCookie({\n response,\n locale,\n}: {\n response: NextResponse;\n locale: string;\n}) {\n response.cookies.set(REPLO_LOCALE_COOKIE, locale, {\n path: \"/\",\n maxAge: 31_536_000,\n sameSite: \"lax\",\n });\n}\n\n/** Trailing slashes carry no meaning, so `/blogs` and `/blogs/` are the same. */\nfunction normalizePath(pathname: string): string {\n const trimmed = pathname.replace(/\\/+$/, \"\");\n return trimmed === \"\" ? \"/\" : trimmed;\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["url"]
7
7
  }
@@ -0,0 +1,9 @@
1
+ import type { NextRequest } from "next/server";
2
+ /**
3
+ * The visitor's ISO 3166-1 alpha-2 country, or null when unknown. The
4
+ * override cookie wins so geo behavior is testable without a VPN — in dev
5
+ * sandboxes, where the Cloudflare header never exists, and on the published
6
+ * site alike. At worst the override lets a visitor pick which locale they
7
+ * see, which the locale cookie already allows deliberately.
8
+ */
9
+ export declare function ipCountry(request: NextRequest): string | null;
package/routing/geo.js ADDED
@@ -0,0 +1,17 @@
1
+ import { REPLO_COUNTRY_OVERRIDE_COOKIE } from "../_vendor/schemas/routing/locale.mjs";
2
+ const COUNTRY_CODE = /^[A-Za-z]{2}$/;
3
+ function ipCountry(request) {
4
+ const override = request.cookies.get(REPLO_COUNTRY_OVERRIDE_COOKIE)?.value.trim();
5
+ if (override && COUNTRY_CODE.test(override)) {
6
+ return override.toUpperCase();
7
+ }
8
+ const header = request.headers.get("cf-ipcountry")?.trim().toUpperCase();
9
+ if (!header || !COUNTRY_CODE.test(header) || header === "XX") {
10
+ return null;
11
+ }
12
+ return header;
13
+ }
14
+ export {
15
+ ipCountry
16
+ };
17
+ //# sourceMappingURL=geo.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../routing/geo.ts"],
4
+ "sourcesContent": ["import type { NextRequest } from \"next/server\";\n\nimport { REPLO_COUNTRY_OVERRIDE_COOKIE } from \"schemas/routing/locale\";\n\n// Cloudflare's header uses `XX` for \"unknown\" (`T1` for Tor fails the\n// letters-only check on its own).\nconst COUNTRY_CODE = /^[A-Za-z]{2}$/;\n\n/**\n * The visitor's ISO 3166-1 alpha-2 country, or null when unknown. The\n * override cookie wins so geo behavior is testable without a VPN \u2014 in dev\n * sandboxes, where the Cloudflare header never exists, and on the published\n * site alike. At worst the override lets a visitor pick which locale they\n * see, which the locale cookie already allows deliberately.\n */\nexport function ipCountry(request: NextRequest): string | null {\n const override = request.cookies\n .get(REPLO_COUNTRY_OVERRIDE_COOKIE)\n ?.value.trim();\n if (override && COUNTRY_CODE.test(override)) {\n return override.toUpperCase();\n }\n const header = request.headers.get(\"cf-ipcountry\")?.trim().toUpperCase();\n if (!header || !COUNTRY_CODE.test(header) || header === \"XX\") {\n return null;\n }\n return header;\n}\n"],
5
+ "mappings": "AAEA,SAAS,qCAAqC;AAI9C,MAAM,eAAe;AASd,SAAS,UAAU,SAAqC;AAC7D,QAAM,WAAW,QAAQ,QACtB,IAAI,6BAA6B,GAChC,MAAM,KAAK;AACf,MAAI,YAAY,aAAa,KAAK,QAAQ,GAAG;AAC3C,WAAO,SAAS,YAAY;AAAA,EAC9B;AACA,QAAM,SAAS,QAAQ,QAAQ,IAAI,cAAc,GAAG,KAAK,EAAE,YAAY;AACvE,MAAI,CAAC,UAAU,CAAC,aAAa,KAAK,MAAM,KAAK,WAAW,MAAM;AAC5D,WAAO;AAAA,EACT;AACA,SAAO;AACT;",
6
+ "names": []
7
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Site-facing entrypoint for locale routing authoring and helpers.
3
+ *
4
+ * The schema, negotiation, and URL helpers live in `schemas/routing` so the
5
+ * Website Builder can import them without depending on a published package,
6
+ * whose release cycle shouldn't gate editor-only changes. Sites can't import
7
+ * `schemas` directly — it is workspace-only and reaches generated sites
8
+ * through the SDK's vendoring step — so `middleware.ts` and language
9
+ * switchers import from here.
10
+ */
11
+ export type { LocaleDetection, LocaleRoutingConfig, LocaleRoutingConfigInput, LocaleUrlMode, } from "../_vendor/schemas/routing/locale";
12
+ export { REPLO_COUNTRY_OVERRIDE_COOKIE, REPLO_LOCALE_COOKIE, reploLocaleRouting, } from "../_vendor/schemas/routing/locale";
13
+ export { localeUrl } from "../_vendor/schemas/routing/localeNegotiation";
@@ -0,0 +1,13 @@
1
+ import {
2
+ REPLO_COUNTRY_OVERRIDE_COOKIE,
3
+ REPLO_LOCALE_COOKIE,
4
+ reploLocaleRouting
5
+ } from "../_vendor/schemas/routing/locale.mjs";
6
+ import { localeUrl } from "../_vendor/schemas/routing/localeNegotiation.mjs";
7
+ export {
8
+ REPLO_COUNTRY_OVERRIDE_COOKIE,
9
+ REPLO_LOCALE_COOKIE,
10
+ localeUrl,
11
+ reploLocaleRouting
12
+ };
13
+ //# sourceMappingURL=locale.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../routing/locale.ts"],
4
+ "sourcesContent": ["/**\n * Site-facing entrypoint for locale routing authoring and helpers.\n *\n * The schema, negotiation, and URL helpers live in `schemas/routing` so the\n * Website Builder can import them without depending on a published package,\n * whose release cycle shouldn't gate editor-only changes. Sites can't import\n * `schemas` directly \u2014 it is workspace-only and reaches generated sites\n * through the SDK's vendoring step \u2014 so `middleware.ts` and language\n * switchers import from here.\n */\nexport type {\n LocaleDetection,\n LocaleRoutingConfig,\n LocaleRoutingConfigInput,\n LocaleUrlMode,\n} from \"schemas/routing/locale\"; /* eslint-disable-line barrel-files/avoid-barrel-files, replo/no-export-from -- site-facing entrypoint intentionally re-exports the authoring API. */\nexport {\n REPLO_COUNTRY_OVERRIDE_COOKIE,\n REPLO_LOCALE_COOKIE,\n reploLocaleRouting,\n} from \"schemas/routing/locale\"; /* eslint-disable-line replo/no-export-from -- site-facing entrypoint intentionally re-exports the authoring API. */\nexport { localeUrl } from \"schemas/routing/localeNegotiation\"; /* eslint-disable-line replo/no-export-from -- site-facing entrypoint intentionally re-exports the authoring API. */\n"],
5
+ "mappings": "AAgBA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,iBAAiB;",
6
+ "names": []
7
+ }