@copilotkit/web-inspector 2.0.0-rc.10 → 2.0.0-rc.11

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.
@@ -1,4 +1,4 @@
1
- let semver = require("semver");
1
+ const require_semver = require('./semver.cjs');
2
2
 
3
3
  //#region src/lib/hud-config.ts
4
4
  const HUD_FEATURE_IDS = ["threads", "learning"];
@@ -37,7 +37,7 @@ function parseRule(value) {
37
37
  "framework",
38
38
  "sdkVersion",
39
39
  "features"
40
- ]) || typeof value.framework !== "string" || !frameworkPattern.test(value.framework) || !text(value.sdkVersion, 256) || (0, semver.validRange)(value.sdkVersion) === null || !record(value.features) || !Object.keys(value.features).length) return null;
40
+ ]) || typeof value.framework !== "string" || !frameworkPattern.test(value.framework) || !text(value.sdkVersion, 256) || !require_semver.isValidRange(value.sdkVersion) || !record(value.features) || !Object.keys(value.features).length) return null;
41
41
  const features = {};
42
42
  for (const [id, entry] of Object.entries(value.features)) {
43
43
  if (!featureIdPattern.test(id)) return null;
@@ -68,8 +68,8 @@ function parseHudFeed(value) {
68
68
  /** Return the first rule for a development client with a known stable SDK. */
69
69
  function matchHudRule(feed, context) {
70
70
  const { development, framework, sdkVersion } = context;
71
- if (!development || !framework || !sdkVersion || (0, semver.valid)(sdkVersion) === null || (0, semver.prerelease)(sdkVersion) !== null) return null;
72
- return feed.rules.find((rule) => rule.framework === framework && (0, semver.satisfies)(sdkVersion, rule.sdkVersion)) ?? null;
71
+ if (!development || !framework || !sdkVersion || !require_semver.isStableVersion(sdkVersion)) return null;
72
+ return feed.rules.find((rule) => rule.framework === framework && require_semver.satisfies(sdkVersion, rule.sdkVersion)) ?? null;
73
73
  }
74
74
  /** Overlay the matching rule on the built-in content, field by field. */
75
75
  function resolveHudContent(feed, context, options) {
@@ -1 +1 @@
1
- {"version":3,"file":"hud-config.cjs","names":[],"sources":["../../src/lib/hud-config.ts"],"sourcesContent":["import { prerelease, satisfies, valid, validRange } from \"semver\";\nimport type { NotificationContext } from \"./notifications.js\";\n\n/** Versioned CDN HUD contract. Keep the Intelligence copy and fixtures aligned. */\nexport type HudFramework = \"react\" | \"vue\" | \"angular\";\nexport type HudFeatureId = \"threads\" | \"learning\";\nexport interface HudFeatureContent {\n label: string;\n description: string;\n destination: string;\n}\nexport type HudContent = Record<HudFeatureId, HudFeatureContent>;\nexport type HudFeatureOverride = Partial<HudFeatureContent>;\nexport interface HudRule {\n /** Frameworks unknown to this SDK are kept but never match. */\n framework: string;\n sdkVersion: string;\n /** Keyed by feature ID; IDs unknown to this SDK are kept but never applied. */\n features: Record<string, HudFeatureOverride>;\n}\nexport interface HudFeed {\n schemaVersion: 1;\n rules: HudRule[];\n}\nexport type HudContext = Pick<\n NotificationContext,\n \"development\" | \"framework\" | \"sdkVersion\"\n>;\nexport interface HudResolveOptions {\n defaults: HudContent;\n /** Destination keys this SDK can open; anything else keeps the default. */\n supportedDestinations: readonly string[];\n}\n\nexport const HUD_FEATURE_IDS: readonly HudFeatureId[] = [\"threads\", \"learning\"];\nexport const HUD_LABEL_MAX_LENGTH = 32;\nexport const HUD_DESCRIPTION_MAX_LENGTH = 140;\nconst frameworkPattern = /^[a-z][a-z0-9-]{0,31}$/;\nconst featureIdPattern = /^[a-z][a-z0-9-]{0,31}$/;\nconst destinationPattern = /^[a-z][a-z0-9-]{0,63}$/;\n// eslint-disable-next-line no-control-regex\nconst controlCharacters = /[\\u0000-\\u001f\\u007f-\\u009f\\u2028\\u2029]/;\n\nfunction record(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\nfunction onlyKeys(value: Record<string, unknown>, keys: string[]): boolean {\n return Object.keys(value).every((key) => keys.includes(key));\n}\nfunction text(value: unknown, max: number): value is string {\n return (\n typeof value === \"string\" &&\n value.trim().length > 0 &&\n value.length <= max &&\n !controlCharacters.test(value)\n );\n}\nfunction parseFeature(value: unknown): HudFeatureOverride | null {\n if (\n !record(value) ||\n !Object.keys(value).length ||\n !onlyKeys(value, [\"label\", \"description\", \"destination\"])\n )\n return null;\n if (value.label !== undefined && !text(value.label, HUD_LABEL_MAX_LENGTH))\n return null;\n if (\n value.description !== undefined &&\n !text(value.description, HUD_DESCRIPTION_MAX_LENGTH)\n )\n return null;\n if (\n value.destination !== undefined &&\n (typeof value.destination !== \"string\" ||\n !destinationPattern.test(value.destination))\n )\n return null;\n return {\n ...(value.label === undefined ? {} : { label: value.label }),\n ...(value.description === undefined\n ? {}\n : { description: value.description }),\n ...(value.destination === undefined\n ? {}\n : { destination: value.destination }),\n };\n}\nfunction parseRule(value: unknown): HudRule | null {\n if (\n !record(value) ||\n !onlyKeys(value, [\"framework\", \"sdkVersion\", \"features\"]) ||\n typeof value.framework !== \"string\" ||\n !frameworkPattern.test(value.framework) ||\n !text(value.sdkVersion, 256) ||\n validRange(value.sdkVersion) === null ||\n !record(value.features) ||\n !Object.keys(value.features).length\n )\n return null;\n const features: Record<string, HudFeatureOverride> = {};\n for (const [id, entry] of Object.entries(value.features)) {\n if (!featureIdPattern.test(id)) return null;\n const feature = parseFeature(entry);\n if (!feature) return null;\n features[id] = feature;\n }\n return {\n framework: value.framework,\n sdkVersion: value.sdkVersion,\n features,\n };\n}\n\n/** Parse the public envelope. Any invalid entry rejects the whole feed. */\nexport function parseHudFeed(value: unknown): HudFeed | null {\n if (\n !record(value) ||\n !onlyKeys(value, [\"schemaVersion\", \"rules\"]) ||\n value.schemaVersion !== 1 ||\n !Array.isArray(value.rules) ||\n value.rules.length > 100\n )\n return null;\n const rules: HudRule[] = [];\n for (const entry of value.rules) {\n const rule = parseRule(entry);\n if (!rule) return null;\n rules.push(rule);\n }\n return { schemaVersion: 1, rules };\n}\n\n/** Return the first rule for a development client with a known stable SDK. */\nexport function matchHudRule(\n feed: HudFeed,\n context: HudContext,\n): HudRule | null {\n const { development, framework, sdkVersion } = context;\n if (\n !development ||\n !framework ||\n !sdkVersion ||\n valid(sdkVersion) === null ||\n prerelease(sdkVersion) !== null\n )\n return null;\n return (\n feed.rules.find(\n (rule) =>\n rule.framework === framework && satisfies(sdkVersion, rule.sdkVersion),\n ) ?? null\n );\n}\n\n/** Overlay the matching rule on the built-in content, field by field. */\nexport function resolveHudContent(\n feed: HudFeed | null,\n context: HudContext,\n options: HudResolveOptions,\n): HudContent {\n const rule = feed ? matchHudRule(feed, context) : null;\n const content = {} as HudContent;\n for (const id of HUD_FEATURE_IDS) {\n const fallback = options.defaults[id];\n const override = rule?.features[id];\n content[id] = {\n label: override?.label ?? fallback.label,\n description: override?.description ?? fallback.description,\n destination:\n override?.destination !== undefined &&\n options.supportedDestinations.includes(override.destination)\n ? override.destination\n : fallback.destination,\n };\n }\n return content;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"hud-config.cjs","names":["isValidRange","isStableVersion","satisfies"],"sources":["../../src/lib/hud-config.ts"],"sourcesContent":["import { isStableVersion, isValidRange, satisfies } from \"./semver.js\";\nimport type { NotificationContext } from \"./notifications.js\";\n\n/** Versioned CDN HUD contract. Keep the Intelligence copy and fixtures aligned. */\nexport type HudFramework = \"react\" | \"vue\" | \"angular\";\nexport type HudFeatureId = \"threads\" | \"learning\";\nexport interface HudFeatureContent {\n label: string;\n description: string;\n destination: string;\n}\nexport type HudContent = Record<HudFeatureId, HudFeatureContent>;\nexport type HudFeatureOverride = Partial<HudFeatureContent>;\nexport interface HudRule {\n /** Frameworks unknown to this SDK are kept but never match. */\n framework: string;\n sdkVersion: string;\n /** Keyed by feature ID; IDs unknown to this SDK are kept but never applied. */\n features: Record<string, HudFeatureOverride>;\n}\nexport interface HudFeed {\n schemaVersion: 1;\n rules: HudRule[];\n}\nexport type HudContext = Pick<\n NotificationContext,\n \"development\" | \"framework\" | \"sdkVersion\"\n>;\nexport interface HudResolveOptions {\n defaults: HudContent;\n /** Destination keys this SDK can open; anything else keeps the default. */\n supportedDestinations: readonly string[];\n}\n\nexport const HUD_FEATURE_IDS: readonly HudFeatureId[] = [\"threads\", \"learning\"];\nexport const HUD_LABEL_MAX_LENGTH = 32;\nexport const HUD_DESCRIPTION_MAX_LENGTH = 140;\nconst frameworkPattern = /^[a-z][a-z0-9-]{0,31}$/;\nconst featureIdPattern = /^[a-z][a-z0-9-]{0,31}$/;\nconst destinationPattern = /^[a-z][a-z0-9-]{0,63}$/;\n// eslint-disable-next-line no-control-regex\nconst controlCharacters = /[\\u0000-\\u001f\\u007f-\\u009f\\u2028\\u2029]/;\n\nfunction record(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\nfunction onlyKeys(value: Record<string, unknown>, keys: string[]): boolean {\n return Object.keys(value).every((key) => keys.includes(key));\n}\nfunction text(value: unknown, max: number): value is string {\n return (\n typeof value === \"string\" &&\n value.trim().length > 0 &&\n value.length <= max &&\n !controlCharacters.test(value)\n );\n}\nfunction parseFeature(value: unknown): HudFeatureOverride | null {\n if (\n !record(value) ||\n !Object.keys(value).length ||\n !onlyKeys(value, [\"label\", \"description\", \"destination\"])\n )\n return null;\n if (value.label !== undefined && !text(value.label, HUD_LABEL_MAX_LENGTH))\n return null;\n if (\n value.description !== undefined &&\n !text(value.description, HUD_DESCRIPTION_MAX_LENGTH)\n )\n return null;\n if (\n value.destination !== undefined &&\n (typeof value.destination !== \"string\" ||\n !destinationPattern.test(value.destination))\n )\n return null;\n return {\n ...(value.label === undefined ? {} : { label: value.label }),\n ...(value.description === undefined\n ? {}\n : { description: value.description }),\n ...(value.destination === undefined\n ? {}\n : { destination: value.destination }),\n };\n}\nfunction parseRule(value: unknown): HudRule | null {\n if (\n !record(value) ||\n !onlyKeys(value, [\"framework\", \"sdkVersion\", \"features\"]) ||\n typeof value.framework !== \"string\" ||\n !frameworkPattern.test(value.framework) ||\n !text(value.sdkVersion, 256) ||\n !isValidRange(value.sdkVersion) ||\n !record(value.features) ||\n !Object.keys(value.features).length\n )\n return null;\n const features: Record<string, HudFeatureOverride> = {};\n for (const [id, entry] of Object.entries(value.features)) {\n if (!featureIdPattern.test(id)) return null;\n const feature = parseFeature(entry);\n if (!feature) return null;\n features[id] = feature;\n }\n return {\n framework: value.framework,\n sdkVersion: value.sdkVersion,\n features,\n };\n}\n\n/** Parse the public envelope. Any invalid entry rejects the whole feed. */\nexport function parseHudFeed(value: unknown): HudFeed | null {\n if (\n !record(value) ||\n !onlyKeys(value, [\"schemaVersion\", \"rules\"]) ||\n value.schemaVersion !== 1 ||\n !Array.isArray(value.rules) ||\n value.rules.length > 100\n )\n return null;\n const rules: HudRule[] = [];\n for (const entry of value.rules) {\n const rule = parseRule(entry);\n if (!rule) return null;\n rules.push(rule);\n }\n return { schemaVersion: 1, rules };\n}\n\n/** Return the first rule for a development client with a known stable SDK. */\nexport function matchHudRule(\n feed: HudFeed,\n context: HudContext,\n): HudRule | null {\n const { development, framework, sdkVersion } = context;\n if (!development || !framework || !sdkVersion || !isStableVersion(sdkVersion))\n return null;\n return (\n feed.rules.find(\n (rule) =>\n rule.framework === framework && satisfies(sdkVersion, rule.sdkVersion),\n ) ?? null\n );\n}\n\n/** Overlay the matching rule on the built-in content, field by field. */\nexport function resolveHudContent(\n feed: HudFeed | null,\n context: HudContext,\n options: HudResolveOptions,\n): HudContent {\n const rule = feed ? matchHudRule(feed, context) : null;\n const content = {} as HudContent;\n for (const id of HUD_FEATURE_IDS) {\n const fallback = options.defaults[id];\n const override = rule?.features[id];\n content[id] = {\n label: override?.label ?? fallback.label,\n description: override?.description ?? fallback.description,\n destination:\n override?.destination !== undefined &&\n options.supportedDestinations.includes(override.destination)\n ? override.destination\n : fallback.destination,\n };\n }\n return content;\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { prerelease, satisfies, valid, validRange } from "semver";
1
+ import { isStableVersion, isValidRange, satisfies } from "./semver.mjs";
2
2
 
3
3
  //#region src/lib/hud-config.ts
4
4
  const HUD_FEATURE_IDS = ["threads", "learning"];
@@ -37,7 +37,7 @@ function parseRule(value) {
37
37
  "framework",
38
38
  "sdkVersion",
39
39
  "features"
40
- ]) || typeof value.framework !== "string" || !frameworkPattern.test(value.framework) || !text(value.sdkVersion, 256) || validRange(value.sdkVersion) === null || !record(value.features) || !Object.keys(value.features).length) return null;
40
+ ]) || typeof value.framework !== "string" || !frameworkPattern.test(value.framework) || !text(value.sdkVersion, 256) || !isValidRange(value.sdkVersion) || !record(value.features) || !Object.keys(value.features).length) return null;
41
41
  const features = {};
42
42
  for (const [id, entry] of Object.entries(value.features)) {
43
43
  if (!featureIdPattern.test(id)) return null;
@@ -68,7 +68,7 @@ function parseHudFeed(value) {
68
68
  /** Return the first rule for a development client with a known stable SDK. */
69
69
  function matchHudRule(feed, context) {
70
70
  const { development, framework, sdkVersion } = context;
71
- if (!development || !framework || !sdkVersion || valid(sdkVersion) === null || prerelease(sdkVersion) !== null) return null;
71
+ if (!development || !framework || !sdkVersion || !isStableVersion(sdkVersion)) return null;
72
72
  return feed.rules.find((rule) => rule.framework === framework && satisfies(sdkVersion, rule.sdkVersion)) ?? null;
73
73
  }
74
74
  /** Overlay the matching rule on the built-in content, field by field. */
@@ -1 +1 @@
1
- {"version":3,"file":"hud-config.mjs","names":[],"sources":["../../src/lib/hud-config.ts"],"sourcesContent":["import { prerelease, satisfies, valid, validRange } from \"semver\";\nimport type { NotificationContext } from \"./notifications.js\";\n\n/** Versioned CDN HUD contract. Keep the Intelligence copy and fixtures aligned. */\nexport type HudFramework = \"react\" | \"vue\" | \"angular\";\nexport type HudFeatureId = \"threads\" | \"learning\";\nexport interface HudFeatureContent {\n label: string;\n description: string;\n destination: string;\n}\nexport type HudContent = Record<HudFeatureId, HudFeatureContent>;\nexport type HudFeatureOverride = Partial<HudFeatureContent>;\nexport interface HudRule {\n /** Frameworks unknown to this SDK are kept but never match. */\n framework: string;\n sdkVersion: string;\n /** Keyed by feature ID; IDs unknown to this SDK are kept but never applied. */\n features: Record<string, HudFeatureOverride>;\n}\nexport interface HudFeed {\n schemaVersion: 1;\n rules: HudRule[];\n}\nexport type HudContext = Pick<\n NotificationContext,\n \"development\" | \"framework\" | \"sdkVersion\"\n>;\nexport interface HudResolveOptions {\n defaults: HudContent;\n /** Destination keys this SDK can open; anything else keeps the default. */\n supportedDestinations: readonly string[];\n}\n\nexport const HUD_FEATURE_IDS: readonly HudFeatureId[] = [\"threads\", \"learning\"];\nexport const HUD_LABEL_MAX_LENGTH = 32;\nexport const HUD_DESCRIPTION_MAX_LENGTH = 140;\nconst frameworkPattern = /^[a-z][a-z0-9-]{0,31}$/;\nconst featureIdPattern = /^[a-z][a-z0-9-]{0,31}$/;\nconst destinationPattern = /^[a-z][a-z0-9-]{0,63}$/;\n// eslint-disable-next-line no-control-regex\nconst controlCharacters = /[\\u0000-\\u001f\\u007f-\\u009f\\u2028\\u2029]/;\n\nfunction record(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\nfunction onlyKeys(value: Record<string, unknown>, keys: string[]): boolean {\n return Object.keys(value).every((key) => keys.includes(key));\n}\nfunction text(value: unknown, max: number): value is string {\n return (\n typeof value === \"string\" &&\n value.trim().length > 0 &&\n value.length <= max &&\n !controlCharacters.test(value)\n );\n}\nfunction parseFeature(value: unknown): HudFeatureOverride | null {\n if (\n !record(value) ||\n !Object.keys(value).length ||\n !onlyKeys(value, [\"label\", \"description\", \"destination\"])\n )\n return null;\n if (value.label !== undefined && !text(value.label, HUD_LABEL_MAX_LENGTH))\n return null;\n if (\n value.description !== undefined &&\n !text(value.description, HUD_DESCRIPTION_MAX_LENGTH)\n )\n return null;\n if (\n value.destination !== undefined &&\n (typeof value.destination !== \"string\" ||\n !destinationPattern.test(value.destination))\n )\n return null;\n return {\n ...(value.label === undefined ? {} : { label: value.label }),\n ...(value.description === undefined\n ? {}\n : { description: value.description }),\n ...(value.destination === undefined\n ? {}\n : { destination: value.destination }),\n };\n}\nfunction parseRule(value: unknown): HudRule | null {\n if (\n !record(value) ||\n !onlyKeys(value, [\"framework\", \"sdkVersion\", \"features\"]) ||\n typeof value.framework !== \"string\" ||\n !frameworkPattern.test(value.framework) ||\n !text(value.sdkVersion, 256) ||\n validRange(value.sdkVersion) === null ||\n !record(value.features) ||\n !Object.keys(value.features).length\n )\n return null;\n const features: Record<string, HudFeatureOverride> = {};\n for (const [id, entry] of Object.entries(value.features)) {\n if (!featureIdPattern.test(id)) return null;\n const feature = parseFeature(entry);\n if (!feature) return null;\n features[id] = feature;\n }\n return {\n framework: value.framework,\n sdkVersion: value.sdkVersion,\n features,\n };\n}\n\n/** Parse the public envelope. Any invalid entry rejects the whole feed. */\nexport function parseHudFeed(value: unknown): HudFeed | null {\n if (\n !record(value) ||\n !onlyKeys(value, [\"schemaVersion\", \"rules\"]) ||\n value.schemaVersion !== 1 ||\n !Array.isArray(value.rules) ||\n value.rules.length > 100\n )\n return null;\n const rules: HudRule[] = [];\n for (const entry of value.rules) {\n const rule = parseRule(entry);\n if (!rule) return null;\n rules.push(rule);\n }\n return { schemaVersion: 1, rules };\n}\n\n/** Return the first rule for a development client with a known stable SDK. */\nexport function matchHudRule(\n feed: HudFeed,\n context: HudContext,\n): HudRule | null {\n const { development, framework, sdkVersion } = context;\n if (\n !development ||\n !framework ||\n !sdkVersion ||\n valid(sdkVersion) === null ||\n prerelease(sdkVersion) !== null\n )\n return null;\n return (\n feed.rules.find(\n (rule) =>\n rule.framework === framework && satisfies(sdkVersion, rule.sdkVersion),\n ) ?? null\n );\n}\n\n/** Overlay the matching rule on the built-in content, field by field. */\nexport function resolveHudContent(\n feed: HudFeed | null,\n context: HudContext,\n options: HudResolveOptions,\n): HudContent {\n const rule = feed ? matchHudRule(feed, context) : null;\n const content = {} as HudContent;\n for (const id of HUD_FEATURE_IDS) {\n const fallback = options.defaults[id];\n const override = rule?.features[id];\n content[id] = {\n label: override?.label ?? fallback.label,\n description: override?.description ?? fallback.description,\n destination:\n override?.destination !== undefined &&\n options.supportedDestinations.includes(override.destination)\n ? override.destination\n : fallback.destination,\n };\n }\n return content;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"hud-config.mjs","names":[],"sources":["../../src/lib/hud-config.ts"],"sourcesContent":["import { isStableVersion, isValidRange, satisfies } from \"./semver.js\";\nimport type { NotificationContext } from \"./notifications.js\";\n\n/** Versioned CDN HUD contract. Keep the Intelligence copy and fixtures aligned. */\nexport type HudFramework = \"react\" | \"vue\" | \"angular\";\nexport type HudFeatureId = \"threads\" | \"learning\";\nexport interface HudFeatureContent {\n label: string;\n description: string;\n destination: string;\n}\nexport type HudContent = Record<HudFeatureId, HudFeatureContent>;\nexport type HudFeatureOverride = Partial<HudFeatureContent>;\nexport interface HudRule {\n /** Frameworks unknown to this SDK are kept but never match. */\n framework: string;\n sdkVersion: string;\n /** Keyed by feature ID; IDs unknown to this SDK are kept but never applied. */\n features: Record<string, HudFeatureOverride>;\n}\nexport interface HudFeed {\n schemaVersion: 1;\n rules: HudRule[];\n}\nexport type HudContext = Pick<\n NotificationContext,\n \"development\" | \"framework\" | \"sdkVersion\"\n>;\nexport interface HudResolveOptions {\n defaults: HudContent;\n /** Destination keys this SDK can open; anything else keeps the default. */\n supportedDestinations: readonly string[];\n}\n\nexport const HUD_FEATURE_IDS: readonly HudFeatureId[] = [\"threads\", \"learning\"];\nexport const HUD_LABEL_MAX_LENGTH = 32;\nexport const HUD_DESCRIPTION_MAX_LENGTH = 140;\nconst frameworkPattern = /^[a-z][a-z0-9-]{0,31}$/;\nconst featureIdPattern = /^[a-z][a-z0-9-]{0,31}$/;\nconst destinationPattern = /^[a-z][a-z0-9-]{0,63}$/;\n// eslint-disable-next-line no-control-regex\nconst controlCharacters = /[\\u0000-\\u001f\\u007f-\\u009f\\u2028\\u2029]/;\n\nfunction record(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\nfunction onlyKeys(value: Record<string, unknown>, keys: string[]): boolean {\n return Object.keys(value).every((key) => keys.includes(key));\n}\nfunction text(value: unknown, max: number): value is string {\n return (\n typeof value === \"string\" &&\n value.trim().length > 0 &&\n value.length <= max &&\n !controlCharacters.test(value)\n );\n}\nfunction parseFeature(value: unknown): HudFeatureOverride | null {\n if (\n !record(value) ||\n !Object.keys(value).length ||\n !onlyKeys(value, [\"label\", \"description\", \"destination\"])\n )\n return null;\n if (value.label !== undefined && !text(value.label, HUD_LABEL_MAX_LENGTH))\n return null;\n if (\n value.description !== undefined &&\n !text(value.description, HUD_DESCRIPTION_MAX_LENGTH)\n )\n return null;\n if (\n value.destination !== undefined &&\n (typeof value.destination !== \"string\" ||\n !destinationPattern.test(value.destination))\n )\n return null;\n return {\n ...(value.label === undefined ? {} : { label: value.label }),\n ...(value.description === undefined\n ? {}\n : { description: value.description }),\n ...(value.destination === undefined\n ? {}\n : { destination: value.destination }),\n };\n}\nfunction parseRule(value: unknown): HudRule | null {\n if (\n !record(value) ||\n !onlyKeys(value, [\"framework\", \"sdkVersion\", \"features\"]) ||\n typeof value.framework !== \"string\" ||\n !frameworkPattern.test(value.framework) ||\n !text(value.sdkVersion, 256) ||\n !isValidRange(value.sdkVersion) ||\n !record(value.features) ||\n !Object.keys(value.features).length\n )\n return null;\n const features: Record<string, HudFeatureOverride> = {};\n for (const [id, entry] of Object.entries(value.features)) {\n if (!featureIdPattern.test(id)) return null;\n const feature = parseFeature(entry);\n if (!feature) return null;\n features[id] = feature;\n }\n return {\n framework: value.framework,\n sdkVersion: value.sdkVersion,\n features,\n };\n}\n\n/** Parse the public envelope. Any invalid entry rejects the whole feed. */\nexport function parseHudFeed(value: unknown): HudFeed | null {\n if (\n !record(value) ||\n !onlyKeys(value, [\"schemaVersion\", \"rules\"]) ||\n value.schemaVersion !== 1 ||\n !Array.isArray(value.rules) ||\n value.rules.length > 100\n )\n return null;\n const rules: HudRule[] = [];\n for (const entry of value.rules) {\n const rule = parseRule(entry);\n if (!rule) return null;\n rules.push(rule);\n }\n return { schemaVersion: 1, rules };\n}\n\n/** Return the first rule for a development client with a known stable SDK. */\nexport function matchHudRule(\n feed: HudFeed,\n context: HudContext,\n): HudRule | null {\n const { development, framework, sdkVersion } = context;\n if (!development || !framework || !sdkVersion || !isStableVersion(sdkVersion))\n return null;\n return (\n feed.rules.find(\n (rule) =>\n rule.framework === framework && satisfies(sdkVersion, rule.sdkVersion),\n ) ?? null\n );\n}\n\n/** Overlay the matching rule on the built-in content, field by field. */\nexport function resolveHudContent(\n feed: HudFeed | null,\n context: HudContext,\n options: HudResolveOptions,\n): HudContent {\n const rule = feed ? matchHudRule(feed, context) : null;\n const content = {} as HudContent;\n for (const id of HUD_FEATURE_IDS) {\n const fallback = options.defaults[id];\n const override = rule?.features[id];\n content[id] = {\n label: override?.label ?? fallback.label,\n description: override?.description ?? fallback.description,\n destination:\n override?.destination !== undefined &&\n options.supportedDestinations.includes(override.destination)\n ? override.destination\n : fallback.destination,\n };\n }\n return content;\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- let semver = require("semver");
1
+ const require_semver = require('./semver.cjs');
2
2
 
3
3
  //#region src/lib/notifications.ts
4
4
  function emptyNotificationState() {
@@ -46,7 +46,7 @@ function validNotificationConditions(value) {
46
46
  if (value.sdkVersion !== void 0 && value.framework === void 0) return false;
47
47
  return Object.entries(value).every(([key, expected]) => {
48
48
  if (!text(expected)) return false;
49
- if (key === "sdkVersion") return (0, semver.validRange)(expected) !== null;
49
+ if (key === "sdkVersion") return require_semver.isValidRange(expected);
50
50
  if (key === "plan") return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(expected);
51
51
  return Object.prototype.hasOwnProperty.call(conditionValues, key) && (conditionValues[key]?.includes(expected) ?? false);
52
52
  });
@@ -144,7 +144,7 @@ function matchNotification(notice, context) {
144
144
  matches: false,
145
145
  reasons: ["Development only"]
146
146
  };
147
- if (!context.sdkVersion || !(0, semver.valid)(context.sdkVersion) || (0, semver.prerelease)(context.sdkVersion) !== null) return {
147
+ if (!context.sdkVersion || !require_semver.isStableVersion(context.sdkVersion)) return {
148
148
  matches: false,
149
149
  reasons: ["Known stable SDK required"]
150
150
  };
@@ -152,7 +152,7 @@ function matchNotification(notice, context) {
152
152
  const matches = notice.audiences.some((conditions) => {
153
153
  return Object.entries(conditions).every(([field, expected]) => {
154
154
  const actual = context[field];
155
- const matches = field === "sdkVersion" ? actual !== void 0 && (0, semver.valid)(actual) !== null && (0, semver.prerelease)(actual) === null && (0, semver.satisfies)(actual, expected) : actual === expected;
155
+ const matches = field === "sdkVersion" ? actual !== void 0 && require_semver.isStableVersion(actual) && require_semver.satisfies(actual, expected) : actual === expected;
156
156
  if (!matches) reasons.push(`${field} ${actual === void 0 ? "is unknown" : `requires ${expected} (got ${actual})`}`);
157
157
  return matches;
158
158
  });
@@ -1 +1 @@
1
- {"version":3,"file":"notifications.cjs","names":[],"sources":["../../src/lib/notifications.ts"],"sourcesContent":["import { satisfies, valid, validRange, prerelease } from \"semver\";\n\n/** Versioned CDN notification contract. Keep the Intelligence copy and fixtures aligned. */\nexport type NotificationFramework = \"react\" | \"vue\" | \"angular\";\nexport type NotificationPriority = \"Low\" | \"Normal\" | \"High\" | \"Urgent\";\nexport interface NotificationConditions {\n framework?: NotificationFramework;\n sdkVersion?: string;\n intelligence?: \"enabled\" | \"disabled\";\n deployment?: \"managed\" | \"self-hosted\";\n plan?: string;\n}\nexport interface CohortNotification {\n id: string;\n title: string;\n body: string;\n publishedAt: string;\n audiences: NotificationConditions[];\n priority: NotificationPriority;\n priorityOverride?: number;\n}\nexport interface NotificationFeed {\n schemaVersion: 1;\n notifications: CohortNotification[];\n}\nexport interface NotificationContext extends Omit<\n NotificationConditions,\n \"sdkVersion\"\n> {\n development: boolean;\n sdkVersion?: string;\n}\nexport interface NotificationState {\n schemaVersion: 1;\n eligibleIds: string[];\n readIds: string[];\n suppressedIds: string[];\n activeId: string | null;\n}\nexport function emptyNotificationState(): NotificationState {\n return {\n schemaVersion: 1,\n eligibleIds: [],\n readIds: [],\n suppressedIds: [],\n activeId: null,\n };\n}\nconst priorities: Record<NotificationPriority, number> = {\n Low: 10,\n Normal: 20,\n High: 30,\n Urgent: 40,\n};\nconst conditionValues: Record<string, readonly string[]> = {\n framework: [\"react\", \"vue\", \"angular\"],\n intelligence: [\"enabled\", \"disabled\"],\n deployment: [\"managed\", \"self-hosted\"],\n};\nfunction record(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\nfunction onlyKeys(value: Record<string, unknown>, keys: string[]): boolean {\n return Object.keys(value).every((key) => keys.includes(key));\n}\nfunction text(value: unknown, max = 256): value is string {\n return (\n typeof value === \"string\" && value.trim().length > 0 && value.length <= max\n );\n}\nexport function isNotificationId(value: unknown): value is string {\n return (\n typeof value === \"string\" &&\n /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(\n value,\n )\n );\n}\nfunction ids(value: unknown): value is string[] {\n return (\n Array.isArray(value) &&\n value.every(isNotificationId) &&\n new Set(value).size === value.length\n );\n}\n/** Validate authored conditions; unsupported predicates cannot broaden the audience. */\nexport function validNotificationConditions(\n value: unknown,\n): value is NotificationConditions {\n if (!record(value)) return false;\n if (value.sdkVersion !== undefined && value.framework === undefined)\n return false;\n return Object.entries(value).every(([key, expected]) => {\n if (!text(expected)) return false;\n if (key === \"sdkVersion\") return validRange(expected) !== null;\n if (key === \"plan\") return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(expected);\n return (\n Object.prototype.hasOwnProperty.call(conditionValues, key) &&\n (conditionValues[key]?.includes(expected) ?? false)\n );\n });\n}\n/** Parse the public envelope. Any invalid entry rejects the whole feed. */\nexport function parseNotificationFeed(value: unknown): NotificationFeed | null {\n if (\n !record(value) ||\n !onlyKeys(value, [\"schemaVersion\", \"notifications\"]) ||\n value.schemaVersion !== 1\n )\n return null;\n if (!Array.isArray(value.notifications) || value.notifications.length > 100)\n return null;\n const notifications: CohortNotification[] = [];\n for (const notice of value.notifications) {\n if (\n !record(notice) ||\n !onlyKeys(notice, [\n \"id\",\n \"title\",\n \"body\",\n \"publishedAt\",\n \"audiences\",\n \"priority\",\n \"priorityOverride\",\n ])\n )\n return null;\n if (\n !isNotificationId(notice.id) ||\n !text(notice.title) ||\n !text(notice.body, 100_000) ||\n !text(notice.publishedAt)\n )\n return null;\n if (\n !/^\\d{4}-\\d\\d-\\d\\dT\\d\\d:\\d\\d:\\d\\d\\.\\d{3}Z$/.test(notice.publishedAt) ||\n !Number.isFinite(Date.parse(notice.publishedAt)) ||\n new Date(notice.publishedAt).toISOString() !== notice.publishedAt\n )\n return null;\n if (\n !Array.isArray(notice.audiences) ||\n !notice.audiences.length ||\n notice.audiences.length > 100 ||\n !notice.audiences.every(validNotificationConditions)\n )\n return null;\n if (\n !text(notice.priority) ||\n !Object.prototype.hasOwnProperty.call(priorities, notice.priority)\n )\n return null;\n if (\n notice.priorityOverride !== undefined &&\n (typeof notice.priorityOverride !== \"number\" ||\n !Number.isSafeInteger(notice.priorityOverride) ||\n notice.priorityOverride < 0)\n )\n return null;\n if (notifications.some((n) => n.id === notice.id)) return null;\n notifications.push({\n id: notice.id,\n title: notice.title,\n body: notice.body,\n publishedAt: notice.publishedAt,\n audiences: notice.audiences,\n priority: notice.priority as NotificationPriority,\n ...(notice.priorityOverride === undefined\n ? {}\n : { priorityOverride: notice.priorityOverride }),\n });\n }\n return { schemaVersion: 1, notifications };\n}\n/** Ignore malformed or incompatible browser state. */\nexport function parseNotificationState(\n value: unknown,\n): NotificationState | null {\n if (\n !record(value) ||\n value.schemaVersion !== 1 ||\n !ids(value.eligibleIds) ||\n !ids(value.readIds) ||\n !ids(value.suppressedIds) ||\n (value.activeId !== null && !isNotificationId(value.activeId))\n )\n return null;\n return {\n schemaVersion: 1,\n eligibleIds: value.eligibleIds,\n readIds: value.readIds,\n suppressedIds: value.suppressedIds,\n activeId: value.activeId,\n };\n}\n/** Internal priority never becomes a developer-facing badge. */\nexport function notificationPriority(notice: CohortNotification): number {\n return notice.priorityOverride ?? priorities[notice.priority];\n}\n/** Stable ordering used by authoring reports and first-time selection. */\nexport function compareNotifications(\n a: CohortNotification,\n b: CohortNotification,\n): number {\n return (\n notificationPriority(b) - notificationPriority(a) ||\n Date.parse(b.publishedAt) - Date.parse(a.publishedAt) ||\n (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)\n );\n}\n/** Refresh eligibility without promoting the previously delivered backlog. */\nexport function reconcileNotifications(\n state: NotificationState,\n feed: NotificationFeed,\n context: NotificationContext,\n): NotificationState {\n const eligible = feed.notifications.filter(\n (n) => matchNotification(n, context).matches,\n );\n const quiet = (n: CohortNotification) =>\n state.readIds.includes(n.id) || state.suppressedIds.includes(n.id);\n const active =\n eligible.find((n) => n.id === state.activeId && !quiet(n)) ??\n feed.notifications.find(\n (n) =>\n n.id === state.activeId &&\n !quiet(n) &&\n n.audiences.some((conditions) => {\n const entries = Object.entries(conditions);\n if (\n !entries.some(\n ([key]) =>\n context[key as keyof NotificationConditions] === undefined,\n )\n )\n return false;\n const known = Object.fromEntries(\n entries.filter(\n ([key]) =>\n context[key as keyof NotificationConditions] !== undefined,\n ),\n );\n return matchNotification({ ...n, audiences: [known] }, context)\n .matches;\n }),\n );\n const newcomer = eligible\n .filter((n) => !quiet(n) && !state.eligibleIds.includes(n.id))\n .sort(compareNotifications)[0];\n const winner =\n active &&\n (!newcomer ||\n notificationPriority(active) >= notificationPriority(newcomer))\n ? active\n : newcomer;\n return {\n ...state,\n eligibleIds: eligible.map((n) => n.id),\n activeId: winner?.id ?? null,\n };\n}\n/** Reading or closing the highlight quiets its delivered backlog, not future IDs. */\nexport function acknowledgeNotification(\n state: NotificationState,\n noticeId: string,\n): NotificationState {\n if (!state.eligibleIds.includes(noticeId)) return state;\n const highlighted = state.activeId === noticeId;\n return {\n ...state,\n readIds: [...new Set([...state.readIds, noticeId])],\n suppressedIds: [\n ...new Set([\n ...state.suppressedIds,\n ...(highlighted ? state.eligibleIds : [noticeId]),\n ]),\n ],\n activeId: highlighted ? null : state.activeId,\n };\n}\n/** Explain targeting using the same rules in CI and the Inspector. */\nexport function matchNotification(\n notice: CohortNotification,\n context: NotificationContext,\n): { matches: boolean; reasons: string[] } {\n if (!context.development)\n return { matches: false, reasons: [\"Development only\"] };\n if (\n !context.sdkVersion ||\n !valid(context.sdkVersion) ||\n prerelease(context.sdkVersion) !== null\n )\n return {\n matches: false,\n reasons: [\"Known stable SDK required\"],\n };\n const reasons: string[] = [];\n const matches = notice.audiences.some((conditions) => {\n return Object.entries(conditions).every(([field, expected]) => {\n const actual = context[field as keyof NotificationConditions];\n const matches =\n field === \"sdkVersion\"\n ? actual !== undefined &&\n valid(actual) !== null &&\n prerelease(actual) === null &&\n satisfies(actual, expected)\n : actual === expected;\n if (!matches)\n reasons.push(\n `${field} ${actual === undefined ? \"is unknown\" : `requires ${expected} (got ${actual})`}`,\n );\n return matches;\n });\n });\n return {\n matches,\n reasons: matches ? [] : reasons,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"notifications.cjs","names":["isValidRange","isStableVersion","satisfies"],"sources":["../../src/lib/notifications.ts"],"sourcesContent":["import { isStableVersion, isValidRange, satisfies } from \"./semver.js\";\n\n/** Versioned CDN notification contract. Keep the Intelligence copy and fixtures aligned. */\nexport type NotificationFramework = \"react\" | \"vue\" | \"angular\";\nexport type NotificationPriority = \"Low\" | \"Normal\" | \"High\" | \"Urgent\";\nexport interface NotificationConditions {\n framework?: NotificationFramework;\n sdkVersion?: string;\n intelligence?: \"enabled\" | \"disabled\";\n deployment?: \"managed\" | \"self-hosted\";\n plan?: string;\n}\nexport interface CohortNotification {\n id: string;\n title: string;\n body: string;\n publishedAt: string;\n audiences: NotificationConditions[];\n priority: NotificationPriority;\n priorityOverride?: number;\n}\nexport interface NotificationFeed {\n schemaVersion: 1;\n notifications: CohortNotification[];\n}\nexport interface NotificationContext extends Omit<\n NotificationConditions,\n \"sdkVersion\"\n> {\n development: boolean;\n sdkVersion?: string;\n}\nexport interface NotificationState {\n schemaVersion: 1;\n eligibleIds: string[];\n readIds: string[];\n suppressedIds: string[];\n activeId: string | null;\n}\nexport function emptyNotificationState(): NotificationState {\n return {\n schemaVersion: 1,\n eligibleIds: [],\n readIds: [],\n suppressedIds: [],\n activeId: null,\n };\n}\nconst priorities: Record<NotificationPriority, number> = {\n Low: 10,\n Normal: 20,\n High: 30,\n Urgent: 40,\n};\nconst conditionValues: Record<string, readonly string[]> = {\n framework: [\"react\", \"vue\", \"angular\"],\n intelligence: [\"enabled\", \"disabled\"],\n deployment: [\"managed\", \"self-hosted\"],\n};\nfunction record(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\nfunction onlyKeys(value: Record<string, unknown>, keys: string[]): boolean {\n return Object.keys(value).every((key) => keys.includes(key));\n}\nfunction text(value: unknown, max = 256): value is string {\n return (\n typeof value === \"string\" && value.trim().length > 0 && value.length <= max\n );\n}\nexport function isNotificationId(value: unknown): value is string {\n return (\n typeof value === \"string\" &&\n /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(\n value,\n )\n );\n}\nfunction ids(value: unknown): value is string[] {\n return (\n Array.isArray(value) &&\n value.every(isNotificationId) &&\n new Set(value).size === value.length\n );\n}\n/** Validate authored conditions; unsupported predicates cannot broaden the audience. */\nexport function validNotificationConditions(\n value: unknown,\n): value is NotificationConditions {\n if (!record(value)) return false;\n if (value.sdkVersion !== undefined && value.framework === undefined)\n return false;\n return Object.entries(value).every(([key, expected]) => {\n if (!text(expected)) return false;\n if (key === \"sdkVersion\") return isValidRange(expected);\n if (key === \"plan\") return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(expected);\n return (\n Object.prototype.hasOwnProperty.call(conditionValues, key) &&\n (conditionValues[key]?.includes(expected) ?? false)\n );\n });\n}\n/** Parse the public envelope. Any invalid entry rejects the whole feed. */\nexport function parseNotificationFeed(value: unknown): NotificationFeed | null {\n if (\n !record(value) ||\n !onlyKeys(value, [\"schemaVersion\", \"notifications\"]) ||\n value.schemaVersion !== 1\n )\n return null;\n if (!Array.isArray(value.notifications) || value.notifications.length > 100)\n return null;\n const notifications: CohortNotification[] = [];\n for (const notice of value.notifications) {\n if (\n !record(notice) ||\n !onlyKeys(notice, [\n \"id\",\n \"title\",\n \"body\",\n \"publishedAt\",\n \"audiences\",\n \"priority\",\n \"priorityOverride\",\n ])\n )\n return null;\n if (\n !isNotificationId(notice.id) ||\n !text(notice.title) ||\n !text(notice.body, 100_000) ||\n !text(notice.publishedAt)\n )\n return null;\n if (\n !/^\\d{4}-\\d\\d-\\d\\dT\\d\\d:\\d\\d:\\d\\d\\.\\d{3}Z$/.test(notice.publishedAt) ||\n !Number.isFinite(Date.parse(notice.publishedAt)) ||\n new Date(notice.publishedAt).toISOString() !== notice.publishedAt\n )\n return null;\n if (\n !Array.isArray(notice.audiences) ||\n !notice.audiences.length ||\n notice.audiences.length > 100 ||\n !notice.audiences.every(validNotificationConditions)\n )\n return null;\n if (\n !text(notice.priority) ||\n !Object.prototype.hasOwnProperty.call(priorities, notice.priority)\n )\n return null;\n if (\n notice.priorityOverride !== undefined &&\n (typeof notice.priorityOverride !== \"number\" ||\n !Number.isSafeInteger(notice.priorityOverride) ||\n notice.priorityOverride < 0)\n )\n return null;\n if (notifications.some((n) => n.id === notice.id)) return null;\n notifications.push({\n id: notice.id,\n title: notice.title,\n body: notice.body,\n publishedAt: notice.publishedAt,\n audiences: notice.audiences,\n priority: notice.priority as NotificationPriority,\n ...(notice.priorityOverride === undefined\n ? {}\n : { priorityOverride: notice.priorityOverride }),\n });\n }\n return { schemaVersion: 1, notifications };\n}\n/** Ignore malformed or incompatible browser state. */\nexport function parseNotificationState(\n value: unknown,\n): NotificationState | null {\n if (\n !record(value) ||\n value.schemaVersion !== 1 ||\n !ids(value.eligibleIds) ||\n !ids(value.readIds) ||\n !ids(value.suppressedIds) ||\n (value.activeId !== null && !isNotificationId(value.activeId))\n )\n return null;\n return {\n schemaVersion: 1,\n eligibleIds: value.eligibleIds,\n readIds: value.readIds,\n suppressedIds: value.suppressedIds,\n activeId: value.activeId,\n };\n}\n/** Internal priority never becomes a developer-facing badge. */\nexport function notificationPriority(notice: CohortNotification): number {\n return notice.priorityOverride ?? priorities[notice.priority];\n}\n/** Stable ordering used by authoring reports and first-time selection. */\nexport function compareNotifications(\n a: CohortNotification,\n b: CohortNotification,\n): number {\n return (\n notificationPriority(b) - notificationPriority(a) ||\n Date.parse(b.publishedAt) - Date.parse(a.publishedAt) ||\n (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)\n );\n}\n/** Refresh eligibility without promoting the previously delivered backlog. */\nexport function reconcileNotifications(\n state: NotificationState,\n feed: NotificationFeed,\n context: NotificationContext,\n): NotificationState {\n const eligible = feed.notifications.filter(\n (n) => matchNotification(n, context).matches,\n );\n const quiet = (n: CohortNotification) =>\n state.readIds.includes(n.id) || state.suppressedIds.includes(n.id);\n const active =\n eligible.find((n) => n.id === state.activeId && !quiet(n)) ??\n feed.notifications.find(\n (n) =>\n n.id === state.activeId &&\n !quiet(n) &&\n n.audiences.some((conditions) => {\n const entries = Object.entries(conditions);\n if (\n !entries.some(\n ([key]) =>\n context[key as keyof NotificationConditions] === undefined,\n )\n )\n return false;\n const known = Object.fromEntries(\n entries.filter(\n ([key]) =>\n context[key as keyof NotificationConditions] !== undefined,\n ),\n );\n return matchNotification({ ...n, audiences: [known] }, context)\n .matches;\n }),\n );\n const newcomer = eligible\n .filter((n) => !quiet(n) && !state.eligibleIds.includes(n.id))\n .sort(compareNotifications)[0];\n const winner =\n active &&\n (!newcomer ||\n notificationPriority(active) >= notificationPriority(newcomer))\n ? active\n : newcomer;\n return {\n ...state,\n eligibleIds: eligible.map((n) => n.id),\n activeId: winner?.id ?? null,\n };\n}\n/** Reading or closing the highlight quiets its delivered backlog, not future IDs. */\nexport function acknowledgeNotification(\n state: NotificationState,\n noticeId: string,\n): NotificationState {\n if (!state.eligibleIds.includes(noticeId)) return state;\n const highlighted = state.activeId === noticeId;\n return {\n ...state,\n readIds: [...new Set([...state.readIds, noticeId])],\n suppressedIds: [\n ...new Set([\n ...state.suppressedIds,\n ...(highlighted ? state.eligibleIds : [noticeId]),\n ]),\n ],\n activeId: highlighted ? null : state.activeId,\n };\n}\n/** Explain targeting using the same rules in CI and the Inspector. */\nexport function matchNotification(\n notice: CohortNotification,\n context: NotificationContext,\n): { matches: boolean; reasons: string[] } {\n if (!context.development)\n return { matches: false, reasons: [\"Development only\"] };\n if (!context.sdkVersion || !isStableVersion(context.sdkVersion))\n return {\n matches: false,\n reasons: [\"Known stable SDK required\"],\n };\n const reasons: string[] = [];\n const matches = notice.audiences.some((conditions) => {\n return Object.entries(conditions).every(([field, expected]) => {\n const actual = context[field as keyof NotificationConditions];\n const matches =\n field === \"sdkVersion\"\n ? actual !== undefined &&\n isStableVersion(actual) &&\n satisfies(actual, expected)\n : actual === expected;\n if (!matches)\n reasons.push(\n `${field} ${actual === undefined ? \"is unknown\" : `requires ${expected} (got ${actual})`}`,\n );\n return matches;\n });\n });\n return {\n matches,\n reasons: matches ? [] : reasons,\n };\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { prerelease, satisfies, valid, validRange } from "semver";
1
+ import { isStableVersion, isValidRange, satisfies } from "./semver.mjs";
2
2
 
3
3
  //#region src/lib/notifications.ts
4
4
  function emptyNotificationState() {
@@ -46,7 +46,7 @@ function validNotificationConditions(value) {
46
46
  if (value.sdkVersion !== void 0 && value.framework === void 0) return false;
47
47
  return Object.entries(value).every(([key, expected]) => {
48
48
  if (!text(expected)) return false;
49
- if (key === "sdkVersion") return validRange(expected) !== null;
49
+ if (key === "sdkVersion") return isValidRange(expected);
50
50
  if (key === "plan") return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(expected);
51
51
  return Object.prototype.hasOwnProperty.call(conditionValues, key) && (conditionValues[key]?.includes(expected) ?? false);
52
52
  });
@@ -144,7 +144,7 @@ function matchNotification(notice, context) {
144
144
  matches: false,
145
145
  reasons: ["Development only"]
146
146
  };
147
- if (!context.sdkVersion || !valid(context.sdkVersion) || prerelease(context.sdkVersion) !== null) return {
147
+ if (!context.sdkVersion || !isStableVersion(context.sdkVersion)) return {
148
148
  matches: false,
149
149
  reasons: ["Known stable SDK required"]
150
150
  };
@@ -152,7 +152,7 @@ function matchNotification(notice, context) {
152
152
  const matches = notice.audiences.some((conditions) => {
153
153
  return Object.entries(conditions).every(([field, expected]) => {
154
154
  const actual = context[field];
155
- const matches = field === "sdkVersion" ? actual !== void 0 && valid(actual) !== null && prerelease(actual) === null && satisfies(actual, expected) : actual === expected;
155
+ const matches = field === "sdkVersion" ? actual !== void 0 && isStableVersion(actual) && satisfies(actual, expected) : actual === expected;
156
156
  if (!matches) reasons.push(`${field} ${actual === void 0 ? "is unknown" : `requires ${expected} (got ${actual})`}`);
157
157
  return matches;
158
158
  });
@@ -1 +1 @@
1
- {"version":3,"file":"notifications.mjs","names":[],"sources":["../../src/lib/notifications.ts"],"sourcesContent":["import { satisfies, valid, validRange, prerelease } from \"semver\";\n\n/** Versioned CDN notification contract. Keep the Intelligence copy and fixtures aligned. */\nexport type NotificationFramework = \"react\" | \"vue\" | \"angular\";\nexport type NotificationPriority = \"Low\" | \"Normal\" | \"High\" | \"Urgent\";\nexport interface NotificationConditions {\n framework?: NotificationFramework;\n sdkVersion?: string;\n intelligence?: \"enabled\" | \"disabled\";\n deployment?: \"managed\" | \"self-hosted\";\n plan?: string;\n}\nexport interface CohortNotification {\n id: string;\n title: string;\n body: string;\n publishedAt: string;\n audiences: NotificationConditions[];\n priority: NotificationPriority;\n priorityOverride?: number;\n}\nexport interface NotificationFeed {\n schemaVersion: 1;\n notifications: CohortNotification[];\n}\nexport interface NotificationContext extends Omit<\n NotificationConditions,\n \"sdkVersion\"\n> {\n development: boolean;\n sdkVersion?: string;\n}\nexport interface NotificationState {\n schemaVersion: 1;\n eligibleIds: string[];\n readIds: string[];\n suppressedIds: string[];\n activeId: string | null;\n}\nexport function emptyNotificationState(): NotificationState {\n return {\n schemaVersion: 1,\n eligibleIds: [],\n readIds: [],\n suppressedIds: [],\n activeId: null,\n };\n}\nconst priorities: Record<NotificationPriority, number> = {\n Low: 10,\n Normal: 20,\n High: 30,\n Urgent: 40,\n};\nconst conditionValues: Record<string, readonly string[]> = {\n framework: [\"react\", \"vue\", \"angular\"],\n intelligence: [\"enabled\", \"disabled\"],\n deployment: [\"managed\", \"self-hosted\"],\n};\nfunction record(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\nfunction onlyKeys(value: Record<string, unknown>, keys: string[]): boolean {\n return Object.keys(value).every((key) => keys.includes(key));\n}\nfunction text(value: unknown, max = 256): value is string {\n return (\n typeof value === \"string\" && value.trim().length > 0 && value.length <= max\n );\n}\nexport function isNotificationId(value: unknown): value is string {\n return (\n typeof value === \"string\" &&\n /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(\n value,\n )\n );\n}\nfunction ids(value: unknown): value is string[] {\n return (\n Array.isArray(value) &&\n value.every(isNotificationId) &&\n new Set(value).size === value.length\n );\n}\n/** Validate authored conditions; unsupported predicates cannot broaden the audience. */\nexport function validNotificationConditions(\n value: unknown,\n): value is NotificationConditions {\n if (!record(value)) return false;\n if (value.sdkVersion !== undefined && value.framework === undefined)\n return false;\n return Object.entries(value).every(([key, expected]) => {\n if (!text(expected)) return false;\n if (key === \"sdkVersion\") return validRange(expected) !== null;\n if (key === \"plan\") return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(expected);\n return (\n Object.prototype.hasOwnProperty.call(conditionValues, key) &&\n (conditionValues[key]?.includes(expected) ?? false)\n );\n });\n}\n/** Parse the public envelope. Any invalid entry rejects the whole feed. */\nexport function parseNotificationFeed(value: unknown): NotificationFeed | null {\n if (\n !record(value) ||\n !onlyKeys(value, [\"schemaVersion\", \"notifications\"]) ||\n value.schemaVersion !== 1\n )\n return null;\n if (!Array.isArray(value.notifications) || value.notifications.length > 100)\n return null;\n const notifications: CohortNotification[] = [];\n for (const notice of value.notifications) {\n if (\n !record(notice) ||\n !onlyKeys(notice, [\n \"id\",\n \"title\",\n \"body\",\n \"publishedAt\",\n \"audiences\",\n \"priority\",\n \"priorityOverride\",\n ])\n )\n return null;\n if (\n !isNotificationId(notice.id) ||\n !text(notice.title) ||\n !text(notice.body, 100_000) ||\n !text(notice.publishedAt)\n )\n return null;\n if (\n !/^\\d{4}-\\d\\d-\\d\\dT\\d\\d:\\d\\d:\\d\\d\\.\\d{3}Z$/.test(notice.publishedAt) ||\n !Number.isFinite(Date.parse(notice.publishedAt)) ||\n new Date(notice.publishedAt).toISOString() !== notice.publishedAt\n )\n return null;\n if (\n !Array.isArray(notice.audiences) ||\n !notice.audiences.length ||\n notice.audiences.length > 100 ||\n !notice.audiences.every(validNotificationConditions)\n )\n return null;\n if (\n !text(notice.priority) ||\n !Object.prototype.hasOwnProperty.call(priorities, notice.priority)\n )\n return null;\n if (\n notice.priorityOverride !== undefined &&\n (typeof notice.priorityOverride !== \"number\" ||\n !Number.isSafeInteger(notice.priorityOverride) ||\n notice.priorityOverride < 0)\n )\n return null;\n if (notifications.some((n) => n.id === notice.id)) return null;\n notifications.push({\n id: notice.id,\n title: notice.title,\n body: notice.body,\n publishedAt: notice.publishedAt,\n audiences: notice.audiences,\n priority: notice.priority as NotificationPriority,\n ...(notice.priorityOverride === undefined\n ? {}\n : { priorityOverride: notice.priorityOverride }),\n });\n }\n return { schemaVersion: 1, notifications };\n}\n/** Ignore malformed or incompatible browser state. */\nexport function parseNotificationState(\n value: unknown,\n): NotificationState | null {\n if (\n !record(value) ||\n value.schemaVersion !== 1 ||\n !ids(value.eligibleIds) ||\n !ids(value.readIds) ||\n !ids(value.suppressedIds) ||\n (value.activeId !== null && !isNotificationId(value.activeId))\n )\n return null;\n return {\n schemaVersion: 1,\n eligibleIds: value.eligibleIds,\n readIds: value.readIds,\n suppressedIds: value.suppressedIds,\n activeId: value.activeId,\n };\n}\n/** Internal priority never becomes a developer-facing badge. */\nexport function notificationPriority(notice: CohortNotification): number {\n return notice.priorityOverride ?? priorities[notice.priority];\n}\n/** Stable ordering used by authoring reports and first-time selection. */\nexport function compareNotifications(\n a: CohortNotification,\n b: CohortNotification,\n): number {\n return (\n notificationPriority(b) - notificationPriority(a) ||\n Date.parse(b.publishedAt) - Date.parse(a.publishedAt) ||\n (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)\n );\n}\n/** Refresh eligibility without promoting the previously delivered backlog. */\nexport function reconcileNotifications(\n state: NotificationState,\n feed: NotificationFeed,\n context: NotificationContext,\n): NotificationState {\n const eligible = feed.notifications.filter(\n (n) => matchNotification(n, context).matches,\n );\n const quiet = (n: CohortNotification) =>\n state.readIds.includes(n.id) || state.suppressedIds.includes(n.id);\n const active =\n eligible.find((n) => n.id === state.activeId && !quiet(n)) ??\n feed.notifications.find(\n (n) =>\n n.id === state.activeId &&\n !quiet(n) &&\n n.audiences.some((conditions) => {\n const entries = Object.entries(conditions);\n if (\n !entries.some(\n ([key]) =>\n context[key as keyof NotificationConditions] === undefined,\n )\n )\n return false;\n const known = Object.fromEntries(\n entries.filter(\n ([key]) =>\n context[key as keyof NotificationConditions] !== undefined,\n ),\n );\n return matchNotification({ ...n, audiences: [known] }, context)\n .matches;\n }),\n );\n const newcomer = eligible\n .filter((n) => !quiet(n) && !state.eligibleIds.includes(n.id))\n .sort(compareNotifications)[0];\n const winner =\n active &&\n (!newcomer ||\n notificationPriority(active) >= notificationPriority(newcomer))\n ? active\n : newcomer;\n return {\n ...state,\n eligibleIds: eligible.map((n) => n.id),\n activeId: winner?.id ?? null,\n };\n}\n/** Reading or closing the highlight quiets its delivered backlog, not future IDs. */\nexport function acknowledgeNotification(\n state: NotificationState,\n noticeId: string,\n): NotificationState {\n if (!state.eligibleIds.includes(noticeId)) return state;\n const highlighted = state.activeId === noticeId;\n return {\n ...state,\n readIds: [...new Set([...state.readIds, noticeId])],\n suppressedIds: [\n ...new Set([\n ...state.suppressedIds,\n ...(highlighted ? state.eligibleIds : [noticeId]),\n ]),\n ],\n activeId: highlighted ? null : state.activeId,\n };\n}\n/** Explain targeting using the same rules in CI and the Inspector. */\nexport function matchNotification(\n notice: CohortNotification,\n context: NotificationContext,\n): { matches: boolean; reasons: string[] } {\n if (!context.development)\n return { matches: false, reasons: [\"Development only\"] };\n if (\n !context.sdkVersion ||\n !valid(context.sdkVersion) ||\n prerelease(context.sdkVersion) !== null\n )\n return {\n matches: false,\n reasons: [\"Known stable SDK required\"],\n };\n const reasons: string[] = [];\n const matches = notice.audiences.some((conditions) => {\n return Object.entries(conditions).every(([field, expected]) => {\n const actual = context[field as keyof NotificationConditions];\n const matches =\n field === \"sdkVersion\"\n ? actual !== undefined &&\n valid(actual) !== null &&\n prerelease(actual) === null &&\n satisfies(actual, expected)\n : actual === expected;\n if (!matches)\n reasons.push(\n `${field} ${actual === undefined ? \"is unknown\" : `requires ${expected} (got ${actual})`}`,\n );\n return matches;\n });\n });\n return {\n matches,\n reasons: matches ? [] : reasons,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"notifications.mjs","names":[],"sources":["../../src/lib/notifications.ts"],"sourcesContent":["import { isStableVersion, isValidRange, satisfies } from \"./semver.js\";\n\n/** Versioned CDN notification contract. Keep the Intelligence copy and fixtures aligned. */\nexport type NotificationFramework = \"react\" | \"vue\" | \"angular\";\nexport type NotificationPriority = \"Low\" | \"Normal\" | \"High\" | \"Urgent\";\nexport interface NotificationConditions {\n framework?: NotificationFramework;\n sdkVersion?: string;\n intelligence?: \"enabled\" | \"disabled\";\n deployment?: \"managed\" | \"self-hosted\";\n plan?: string;\n}\nexport interface CohortNotification {\n id: string;\n title: string;\n body: string;\n publishedAt: string;\n audiences: NotificationConditions[];\n priority: NotificationPriority;\n priorityOverride?: number;\n}\nexport interface NotificationFeed {\n schemaVersion: 1;\n notifications: CohortNotification[];\n}\nexport interface NotificationContext extends Omit<\n NotificationConditions,\n \"sdkVersion\"\n> {\n development: boolean;\n sdkVersion?: string;\n}\nexport interface NotificationState {\n schemaVersion: 1;\n eligibleIds: string[];\n readIds: string[];\n suppressedIds: string[];\n activeId: string | null;\n}\nexport function emptyNotificationState(): NotificationState {\n return {\n schemaVersion: 1,\n eligibleIds: [],\n readIds: [],\n suppressedIds: [],\n activeId: null,\n };\n}\nconst priorities: Record<NotificationPriority, number> = {\n Low: 10,\n Normal: 20,\n High: 30,\n Urgent: 40,\n};\nconst conditionValues: Record<string, readonly string[]> = {\n framework: [\"react\", \"vue\", \"angular\"],\n intelligence: [\"enabled\", \"disabled\"],\n deployment: [\"managed\", \"self-hosted\"],\n};\nfunction record(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\nfunction onlyKeys(value: Record<string, unknown>, keys: string[]): boolean {\n return Object.keys(value).every((key) => keys.includes(key));\n}\nfunction text(value: unknown, max = 256): value is string {\n return (\n typeof value === \"string\" && value.trim().length > 0 && value.length <= max\n );\n}\nexport function isNotificationId(value: unknown): value is string {\n return (\n typeof value === \"string\" &&\n /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(\n value,\n )\n );\n}\nfunction ids(value: unknown): value is string[] {\n return (\n Array.isArray(value) &&\n value.every(isNotificationId) &&\n new Set(value).size === value.length\n );\n}\n/** Validate authored conditions; unsupported predicates cannot broaden the audience. */\nexport function validNotificationConditions(\n value: unknown,\n): value is NotificationConditions {\n if (!record(value)) return false;\n if (value.sdkVersion !== undefined && value.framework === undefined)\n return false;\n return Object.entries(value).every(([key, expected]) => {\n if (!text(expected)) return false;\n if (key === \"sdkVersion\") return isValidRange(expected);\n if (key === \"plan\") return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(expected);\n return (\n Object.prototype.hasOwnProperty.call(conditionValues, key) &&\n (conditionValues[key]?.includes(expected) ?? false)\n );\n });\n}\n/** Parse the public envelope. Any invalid entry rejects the whole feed. */\nexport function parseNotificationFeed(value: unknown): NotificationFeed | null {\n if (\n !record(value) ||\n !onlyKeys(value, [\"schemaVersion\", \"notifications\"]) ||\n value.schemaVersion !== 1\n )\n return null;\n if (!Array.isArray(value.notifications) || value.notifications.length > 100)\n return null;\n const notifications: CohortNotification[] = [];\n for (const notice of value.notifications) {\n if (\n !record(notice) ||\n !onlyKeys(notice, [\n \"id\",\n \"title\",\n \"body\",\n \"publishedAt\",\n \"audiences\",\n \"priority\",\n \"priorityOverride\",\n ])\n )\n return null;\n if (\n !isNotificationId(notice.id) ||\n !text(notice.title) ||\n !text(notice.body, 100_000) ||\n !text(notice.publishedAt)\n )\n return null;\n if (\n !/^\\d{4}-\\d\\d-\\d\\dT\\d\\d:\\d\\d:\\d\\d\\.\\d{3}Z$/.test(notice.publishedAt) ||\n !Number.isFinite(Date.parse(notice.publishedAt)) ||\n new Date(notice.publishedAt).toISOString() !== notice.publishedAt\n )\n return null;\n if (\n !Array.isArray(notice.audiences) ||\n !notice.audiences.length ||\n notice.audiences.length > 100 ||\n !notice.audiences.every(validNotificationConditions)\n )\n return null;\n if (\n !text(notice.priority) ||\n !Object.prototype.hasOwnProperty.call(priorities, notice.priority)\n )\n return null;\n if (\n notice.priorityOverride !== undefined &&\n (typeof notice.priorityOverride !== \"number\" ||\n !Number.isSafeInteger(notice.priorityOverride) ||\n notice.priorityOverride < 0)\n )\n return null;\n if (notifications.some((n) => n.id === notice.id)) return null;\n notifications.push({\n id: notice.id,\n title: notice.title,\n body: notice.body,\n publishedAt: notice.publishedAt,\n audiences: notice.audiences,\n priority: notice.priority as NotificationPriority,\n ...(notice.priorityOverride === undefined\n ? {}\n : { priorityOverride: notice.priorityOverride }),\n });\n }\n return { schemaVersion: 1, notifications };\n}\n/** Ignore malformed or incompatible browser state. */\nexport function parseNotificationState(\n value: unknown,\n): NotificationState | null {\n if (\n !record(value) ||\n value.schemaVersion !== 1 ||\n !ids(value.eligibleIds) ||\n !ids(value.readIds) ||\n !ids(value.suppressedIds) ||\n (value.activeId !== null && !isNotificationId(value.activeId))\n )\n return null;\n return {\n schemaVersion: 1,\n eligibleIds: value.eligibleIds,\n readIds: value.readIds,\n suppressedIds: value.suppressedIds,\n activeId: value.activeId,\n };\n}\n/** Internal priority never becomes a developer-facing badge. */\nexport function notificationPriority(notice: CohortNotification): number {\n return notice.priorityOverride ?? priorities[notice.priority];\n}\n/** Stable ordering used by authoring reports and first-time selection. */\nexport function compareNotifications(\n a: CohortNotification,\n b: CohortNotification,\n): number {\n return (\n notificationPriority(b) - notificationPriority(a) ||\n Date.parse(b.publishedAt) - Date.parse(a.publishedAt) ||\n (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)\n );\n}\n/** Refresh eligibility without promoting the previously delivered backlog. */\nexport function reconcileNotifications(\n state: NotificationState,\n feed: NotificationFeed,\n context: NotificationContext,\n): NotificationState {\n const eligible = feed.notifications.filter(\n (n) => matchNotification(n, context).matches,\n );\n const quiet = (n: CohortNotification) =>\n state.readIds.includes(n.id) || state.suppressedIds.includes(n.id);\n const active =\n eligible.find((n) => n.id === state.activeId && !quiet(n)) ??\n feed.notifications.find(\n (n) =>\n n.id === state.activeId &&\n !quiet(n) &&\n n.audiences.some((conditions) => {\n const entries = Object.entries(conditions);\n if (\n !entries.some(\n ([key]) =>\n context[key as keyof NotificationConditions] === undefined,\n )\n )\n return false;\n const known = Object.fromEntries(\n entries.filter(\n ([key]) =>\n context[key as keyof NotificationConditions] !== undefined,\n ),\n );\n return matchNotification({ ...n, audiences: [known] }, context)\n .matches;\n }),\n );\n const newcomer = eligible\n .filter((n) => !quiet(n) && !state.eligibleIds.includes(n.id))\n .sort(compareNotifications)[0];\n const winner =\n active &&\n (!newcomer ||\n notificationPriority(active) >= notificationPriority(newcomer))\n ? active\n : newcomer;\n return {\n ...state,\n eligibleIds: eligible.map((n) => n.id),\n activeId: winner?.id ?? null,\n };\n}\n/** Reading or closing the highlight quiets its delivered backlog, not future IDs. */\nexport function acknowledgeNotification(\n state: NotificationState,\n noticeId: string,\n): NotificationState {\n if (!state.eligibleIds.includes(noticeId)) return state;\n const highlighted = state.activeId === noticeId;\n return {\n ...state,\n readIds: [...new Set([...state.readIds, noticeId])],\n suppressedIds: [\n ...new Set([\n ...state.suppressedIds,\n ...(highlighted ? state.eligibleIds : [noticeId]),\n ]),\n ],\n activeId: highlighted ? null : state.activeId,\n };\n}\n/** Explain targeting using the same rules in CI and the Inspector. */\nexport function matchNotification(\n notice: CohortNotification,\n context: NotificationContext,\n): { matches: boolean; reasons: string[] } {\n if (!context.development)\n return { matches: false, reasons: [\"Development only\"] };\n if (!context.sdkVersion || !isStableVersion(context.sdkVersion))\n return {\n matches: false,\n reasons: [\"Known stable SDK required\"],\n };\n const reasons: string[] = [];\n const matches = notice.audiences.some((conditions) => {\n return Object.entries(conditions).every(([field, expected]) => {\n const actual = context[field as keyof NotificationConditions];\n const matches =\n field === \"sdkVersion\"\n ? actual !== undefined &&\n isStableVersion(actual) &&\n satisfies(actual, expected)\n : actual === expected;\n if (!matches)\n reasons.push(\n `${field} ${actual === undefined ? \"is unknown\" : `requires ${expected} (got ${actual})`}`,\n );\n return matches;\n });\n });\n return {\n matches,\n reasons: matches ? [] : reasons,\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,224 @@
1
+
2
+ //#region src/lib/semver.ts
3
+ const MAX_LENGTH = 256;
4
+ const IDENTIFIER = "(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)";
5
+ const PRERELEASE = `(?:-(${IDENTIFIER}(?:\\.${IDENTIFIER})*))?`;
6
+ const BUILD = "(?:\\+[0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*)?";
7
+ const NUMBER = "(0|[1-9]\\d*)";
8
+ const FULL_VERSION = new RegExp(`^v?${NUMBER}\\.${NUMBER}\\.${NUMBER}${PRERELEASE}${BUILD}$`);
9
+ const PART = "(0|[1-9]\\d*|x|X|\\*)";
10
+ const PARTIAL_VERSION = new RegExp(`^v?${PART}(?:\\.${PART}(?:\\.${PART}${PRERELEASE}${BUILD})?)?$`);
11
+ const COMPARATOR = /^(<=|>=|<|>|=|~>|~|\^)?(.*)$/;
12
+ function parseIdentifiers(value) {
13
+ if (!value) return [];
14
+ return value.split(".").map((id) => /^\d+$/.test(id) ? Number(id) : id);
15
+ }
16
+ function parseVersion(value) {
17
+ const trimmed = value.trim();
18
+ if (trimmed.length > MAX_LENGTH) return null;
19
+ const match = FULL_VERSION.exec(trimmed);
20
+ if (!match) return null;
21
+ const major = Number(match[1]);
22
+ const minor = Number(match[2]);
23
+ const patch = Number(match[3]);
24
+ if (![
25
+ major,
26
+ minor,
27
+ patch
28
+ ].every(Number.isSafeInteger)) return null;
29
+ return {
30
+ major,
31
+ minor,
32
+ patch,
33
+ prerelease: parseIdentifiers(match[4])
34
+ };
35
+ }
36
+ function compareIdentifiers(a, b) {
37
+ if (typeof a === "number" && typeof b === "number") return a - b;
38
+ if (typeof a === "number") return -1;
39
+ if (typeof b === "number") return 1;
40
+ return a < b ? -1 : a > b ? 1 : 0;
41
+ }
42
+ function compare(a, b) {
43
+ const core = a.major - b.major || a.minor - b.minor || a.patch - b.patch;
44
+ if (core) return core;
45
+ if (!a.prerelease.length || !b.prerelease.length) return b.prerelease.length - a.prerelease.length;
46
+ for (let i = 0; i < Math.max(a.prerelease.length, b.prerelease.length); i++) {
47
+ const left = a.prerelease[i];
48
+ const right = b.prerelease[i];
49
+ if (left === void 0) return -1;
50
+ if (right === void 0) return 1;
51
+ const result = compareIdentifiers(left, right);
52
+ if (result) return result;
53
+ }
54
+ return 0;
55
+ }
56
+ /** The lowest version of a release line: `1.2.3-0` sorts before every `1.2.3-*`. */
57
+ function floor(major, minor, patch) {
58
+ return {
59
+ major,
60
+ minor,
61
+ patch,
62
+ prerelease: [0]
63
+ };
64
+ }
65
+ function at(major, minor, patch) {
66
+ return {
67
+ major,
68
+ minor,
69
+ patch,
70
+ prerelease: []
71
+ };
72
+ }
73
+ const NOTHING = [{
74
+ operator: "<",
75
+ version: floor(0, 0, 0)
76
+ }];
77
+ /** Desugar one comparator, such as `^1.2` or `>=1.70.1`, into plain bounds. */
78
+ function parseComparator(value) {
79
+ const [, rawOperator = "", rest = ""] = COMPARATOR.exec(value) ?? [];
80
+ const match = PARTIAL_VERSION.exec(rest);
81
+ if (!match) return null;
82
+ const isWild = (part) => part === void 0 || part === "x" || part === "X" || part === "*";
83
+ const [majorPart, minorPart, patchPart] = match.slice(1, 4);
84
+ const major = isWild(majorPart) ? null : Number(majorPart);
85
+ const minor = major === null || isWild(minorPart) ? null : Number(minorPart);
86
+ const patch = minor === null || isWild(patchPart) ? null : Number(patchPart);
87
+ const prerelease = patch === null ? [] : parseIdentifiers(match[4]);
88
+ const operator = rawOperator === "~>" ? "~" : rawOperator;
89
+ if (operator === "~" || operator === "^") {
90
+ if (major === null) return [];
91
+ const lower = {
92
+ operator: ">=",
93
+ version: {
94
+ major,
95
+ minor: minor ?? 0,
96
+ patch: patch ?? 0,
97
+ prerelease
98
+ }
99
+ };
100
+ let upper;
101
+ if (operator === "~" || minor === null) upper = minor === null || operator === "^" ? floor(major + 1, 0, 0) : floor(major, minor + 1, 0);
102
+ else if (major > 0) upper = floor(major + 1, 0, 0);
103
+ else if (minor > 0 || patch === null) upper = floor(0, minor + 1, 0);
104
+ else upper = floor(0, 0, patch + 1);
105
+ return [lower, {
106
+ operator: "<",
107
+ version: upper
108
+ }];
109
+ }
110
+ if (major === null) return operator === "<" || operator === ">" ? NOTHING : [];
111
+ if (patch !== null) return [{
112
+ operator: operator || "=",
113
+ version: {
114
+ major,
115
+ minor,
116
+ patch,
117
+ prerelease
118
+ }
119
+ }];
120
+ const lower = at(major, minor ?? 0, 0);
121
+ const next = minor === null ? floor(major + 1, 0, 0) : floor(major, minor + 1, 0);
122
+ switch (operator) {
123
+ case ">": return [{
124
+ operator: ">=",
125
+ version: {
126
+ ...next,
127
+ prerelease: []
128
+ }
129
+ }];
130
+ case ">=": return [{
131
+ operator: ">=",
132
+ version: lower
133
+ }];
134
+ case "<": return [{
135
+ operator: "<",
136
+ version: floor(major, minor ?? 0, 0)
137
+ }];
138
+ case "<=": return [{
139
+ operator: "<",
140
+ version: next
141
+ }];
142
+ default: return [{
143
+ operator: ">=",
144
+ version: lower
145
+ }, {
146
+ operator: "<",
147
+ version: next
148
+ }];
149
+ }
150
+ }
151
+ /** Desugar `1.2 - 2.3.4` into `>=1.2.0 <=2.3.4`. */
152
+ function parseHyphen(from, to) {
153
+ const lower = parseComparator(">=" + from);
154
+ const upper = PARTIAL_VERSION.exec(to);
155
+ if (!lower || !upper) return null;
156
+ const full = parseComparator("=" + to);
157
+ if (full?.length === 1 && full[0]) return [...lower, {
158
+ operator: "<=",
159
+ version: full[0].version
160
+ }];
161
+ return [...lower, ...parseComparator("<=" + to) ?? []];
162
+ }
163
+ function parseRange(range) {
164
+ if (range.length > MAX_LENGTH) return null;
165
+ const sets = [];
166
+ for (const raw of range.split("||")) {
167
+ const set = raw.trim().replace(/(<=|>=|<|>|=|~>|~|\^)\s+/g, "$1");
168
+ const hyphen = /^(\S+)\s+-\s+(\S+)$/.exec(set);
169
+ if (hyphen) {
170
+ const comparators = parseHyphen(hyphen[1] ?? "", hyphen[2] ?? "");
171
+ if (!comparators) return null;
172
+ sets.push(comparators);
173
+ continue;
174
+ }
175
+ const comparators = [];
176
+ for (const part of set.split(/\s+/).filter(Boolean)) {
177
+ const parsed = parseComparator(part);
178
+ if (!parsed) return null;
179
+ comparators.push(...parsed);
180
+ }
181
+ sets.push(comparators);
182
+ }
183
+ return sets;
184
+ }
185
+ function test(version, { operator, version: bound }) {
186
+ const result = compare(version, bound);
187
+ switch (operator) {
188
+ case "<": return result < 0;
189
+ case "<=": return result <= 0;
190
+ case ">": return result > 0;
191
+ case ">=": return result >= 0;
192
+ default: return result === 0;
193
+ }
194
+ }
195
+ function sameLine(a, b) {
196
+ return a.major === b.major && a.minor === b.minor && a.patch === b.patch;
197
+ }
198
+ function satisfiesSet(version, set) {
199
+ if (!set.every((comparator) => test(version, comparator))) return false;
200
+ if (!version.prerelease.length) return true;
201
+ return set.some(({ version: bound }) => bound.prerelease.length > 0 && sameLine(bound, version));
202
+ }
203
+ /** Whether `value` is a stable version, such as `1.70.2`, and not `1.70.2-beta.1`. */
204
+ function isStableVersion(value) {
205
+ const version = parseVersion(value);
206
+ return version !== null && version.prerelease.length === 0;
207
+ }
208
+ /** Whether `range` is a valid npm range, such as `>=1.70.1 <1.71.0` or `^1.73.0`. */
209
+ function isValidRange(range) {
210
+ return parseRange(range) !== null;
211
+ }
212
+ /** Whether `version` is in `range`, with the same result as `semver.satisfies`. */
213
+ function satisfies(version, range) {
214
+ const parsed = parseVersion(version);
215
+ const sets = parseRange(range);
216
+ if (!parsed || !sets) return false;
217
+ return sets.some((set) => satisfiesSet(parsed, set));
218
+ }
219
+
220
+ //#endregion
221
+ exports.isStableVersion = isStableVersion;
222
+ exports.isValidRange = isValidRange;
223
+ exports.satisfies = satisfies;
224
+ //# sourceMappingURL=semver.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semver.cjs","names":[],"sources":["../../src/lib/semver.ts"],"sourcesContent":["/**\n * The part of npm's semver that the notification and HUD feeds use: version\n * validation and range matching. It follows node-semver's rules for the npm\n * range syntax (comparators, `^`, `~`, x-ranges, hyphen ranges and `||`), so a\n * feed rule matches the same SDK versions here as it does in `semver`.\n */\n\ninterface Version {\n major: number;\n minor: number;\n patch: number;\n prerelease: Array<string | number>;\n}\n\ntype Operator = \"<\" | \"<=\" | \">\" | \">=\" | \"=\";\ninterface Comparator {\n operator: Operator;\n version: Version;\n}\n\nconst MAX_LENGTH = 256;\nconst IDENTIFIER = \"(?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*)\";\nconst PRERELEASE = `(?:-(${IDENTIFIER}(?:\\\\.${IDENTIFIER})*))?`;\nconst BUILD = \"(?:\\\\+[0-9a-zA-Z-]+(?:\\\\.[0-9a-zA-Z-]+)*)?\";\nconst NUMBER = \"(0|[1-9]\\\\d*)\";\nconst FULL_VERSION = new RegExp(\n `^v?${NUMBER}\\\\.${NUMBER}\\\\.${NUMBER}${PRERELEASE}${BUILD}$`,\n);\nconst PART = \"(0|[1-9]\\\\d*|x|X|\\\\*)\";\nconst PARTIAL_VERSION = new RegExp(\n `^v?${PART}(?:\\\\.${PART}(?:\\\\.${PART}${PRERELEASE}${BUILD})?)?$`,\n);\nconst COMPARATOR = /^(<=|>=|<|>|=|~>|~|\\^)?(.*)$/;\n\nfunction parseIdentifiers(value: string | undefined): Array<string | number> {\n if (!value) return [];\n return value.split(\".\").map((id) => (/^\\d+$/.test(id) ? Number(id) : id));\n}\n\nfunction parseVersion(value: string): Version | null {\n const trimmed = value.trim();\n if (trimmed.length > MAX_LENGTH) return null;\n const match = FULL_VERSION.exec(trimmed);\n if (!match) return null;\n const major = Number(match[1]);\n const minor = Number(match[2]);\n const patch = Number(match[3]);\n if (![major, minor, patch].every(Number.isSafeInteger)) return null;\n return { major, minor, patch, prerelease: parseIdentifiers(match[4]) };\n}\n\nfunction compareIdentifiers(a: string | number, b: string | number): number {\n if (typeof a === \"number\" && typeof b === \"number\") return a - b;\n if (typeof a === \"number\") return -1;\n if (typeof b === \"number\") return 1;\n return a < b ? -1 : a > b ? 1 : 0;\n}\n\nfunction compare(a: Version, b: Version): number {\n const core = a.major - b.major || a.minor - b.minor || a.patch - b.patch;\n if (core) return core;\n if (!a.prerelease.length || !b.prerelease.length)\n return b.prerelease.length - a.prerelease.length;\n for (let i = 0; i < Math.max(a.prerelease.length, b.prerelease.length); i++) {\n const left = a.prerelease[i];\n const right = b.prerelease[i];\n if (left === undefined) return -1;\n if (right === undefined) return 1;\n const result = compareIdentifiers(left, right);\n if (result) return result;\n }\n return 0;\n}\n\n/** The lowest version of a release line: `1.2.3-0` sorts before every `1.2.3-*`. */\nfunction floor(major: number, minor: number, patch: number): Version {\n return { major, minor, patch, prerelease: [0] };\n}\n\nfunction at(major: number, minor: number, patch: number): Version {\n return { major, minor, patch, prerelease: [] };\n}\n\nconst NOTHING: Comparator[] = [{ operator: \"<\", version: floor(0, 0, 0) }];\n\n/** Desugar one comparator, such as `^1.2` or `>=1.70.1`, into plain bounds. */\nfunction parseComparator(value: string): Comparator[] | null {\n const [, rawOperator = \"\", rest = \"\"] = COMPARATOR.exec(value) ?? [];\n const match = PARTIAL_VERSION.exec(rest);\n if (!match) return null;\n const isWild = (part: string | undefined) =>\n part === undefined || part === \"x\" || part === \"X\" || part === \"*\";\n const [majorPart, minorPart, patchPart] = match.slice(1, 4);\n const major = isWild(majorPart) ? null : Number(majorPart);\n const minor = major === null || isWild(minorPart) ? null : Number(minorPart);\n const patch = minor === null || isWild(patchPart) ? null : Number(patchPart);\n const prerelease = patch === null ? [] : parseIdentifiers(match[4]);\n const operator = rawOperator === \"~>\" ? \"~\" : rawOperator;\n\n if (operator === \"~\" || operator === \"^\") {\n if (major === null) return [];\n const lower: Comparator = {\n operator: \">=\",\n version: { major, minor: minor ?? 0, patch: patch ?? 0, prerelease },\n };\n let upper: Version;\n if (operator === \"~\" || minor === null) {\n upper =\n minor === null || operator === \"^\"\n ? floor(major + 1, 0, 0)\n : floor(major, minor + 1, 0);\n } else if (major > 0) upper = floor(major + 1, 0, 0);\n else if (minor > 0 || patch === null) upper = floor(0, minor + 1, 0);\n else upper = floor(0, 0, patch + 1);\n return [lower, { operator: \"<\", version: upper }];\n }\n\n if (major === null) {\n return operator === \"<\" || operator === \">\" ? NOTHING : [];\n }\n if (patch !== null) {\n return [\n {\n operator: (operator || \"=\") as Operator,\n version: { major, minor: minor!, patch, prerelease },\n },\n ];\n }\n\n // A partial version: `1` is 1.x.x and `1.2` is 1.2.x.\n const lower = at(major, minor ?? 0, 0);\n const next =\n minor === null ? floor(major + 1, 0, 0) : floor(major, minor + 1, 0);\n switch (operator) {\n case \">\":\n return [{ operator: \">=\", version: { ...next, prerelease: [] } }];\n case \">=\":\n return [{ operator: \">=\", version: lower }];\n case \"<\":\n return [{ operator: \"<\", version: floor(major, minor ?? 0, 0) }];\n case \"<=\":\n return [{ operator: \"<\", version: next }];\n default:\n return [\n { operator: \">=\", version: lower },\n { operator: \"<\", version: next },\n ];\n }\n}\n\n/** Desugar `1.2 - 2.3.4` into `>=1.2.0 <=2.3.4`. */\nfunction parseHyphen(from: string, to: string): Comparator[] | null {\n const lower = parseComparator(\">=\" + from);\n const upper = PARTIAL_VERSION.exec(to);\n if (!lower || !upper) return null;\n const full = parseComparator(\"=\" + to);\n if (full?.length === 1 && full[0])\n return [...lower, { operator: \"<=\", version: full[0].version }];\n return [...lower, ...(parseComparator(\"<=\" + to) ?? [])];\n}\n\nfunction parseRange(range: string): Comparator[][] | null {\n if (range.length > MAX_LENGTH) return null;\n const sets: Comparator[][] = [];\n for (const raw of range.split(\"||\")) {\n const set = raw.trim().replace(/(<=|>=|<|>|=|~>|~|\\^)\\s+/g, \"$1\");\n const hyphen = /^(\\S+)\\s+-\\s+(\\S+)$/.exec(set);\n if (hyphen) {\n const comparators = parseHyphen(hyphen[1] ?? \"\", hyphen[2] ?? \"\");\n if (!comparators) return null;\n sets.push(comparators);\n continue;\n }\n const comparators: Comparator[] = [];\n for (const part of set.split(/\\s+/).filter(Boolean)) {\n const parsed = parseComparator(part);\n if (!parsed) return null;\n comparators.push(...parsed);\n }\n sets.push(comparators);\n }\n return sets;\n}\n\nfunction test(version: Version, { operator, version: bound }: Comparator) {\n const result = compare(version, bound);\n switch (operator) {\n case \"<\":\n return result < 0;\n case \"<=\":\n return result <= 0;\n case \">\":\n return result > 0;\n case \">=\":\n return result >= 0;\n default:\n return result === 0;\n }\n}\n\nfunction sameLine(a: Version, b: Version): boolean {\n return a.major === b.major && a.minor === b.minor && a.patch === b.patch;\n}\n\nfunction satisfiesSet(version: Version, set: Comparator[]): boolean {\n if (!set.every((comparator) => test(version, comparator))) return false;\n // A prerelease only matches a range that names a prerelease of its line.\n if (!version.prerelease.length) return true;\n return set.some(\n ({ version: bound }) =>\n bound.prerelease.length > 0 && sameLine(bound, version),\n );\n}\n\n/** Whether `value` is a stable version, such as `1.70.2`, and not `1.70.2-beta.1`. */\nexport function isStableVersion(value: string): boolean {\n const version = parseVersion(value);\n return version !== null && version.prerelease.length === 0;\n}\n\n/** Whether `range` is a valid npm range, such as `>=1.70.1 <1.71.0` or `^1.73.0`. */\nexport function isValidRange(range: string): boolean {\n return parseRange(range) !== null;\n}\n\n/** Whether `version` is in `range`, with the same result as `semver.satisfies`. */\nexport function satisfies(version: string, range: string): boolean {\n const parsed = parseVersion(version);\n const sets = parseRange(range);\n if (!parsed || !sets) return false;\n return sets.some((set) => satisfiesSet(parsed, set));\n}\n"],"mappings":"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"}