@decantr/registry 2.0.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -121,6 +121,8 @@ var RegistryAPIClient = class {
121
121
  if (params?.recommended) searchParams.set("recommended", "true");
122
122
  if (params?.intelligenceSource)
123
123
  searchParams.set("intelligence_source", params.intelligenceSource);
124
+ if (params?.blueprintSet) searchParams.set("blueprint_set", params.blueprintSet);
125
+ if (params?.labs) searchParams.set("labs", "true");
124
126
  if (params?.limit) searchParams.set("limit", String(params.limit));
125
127
  if (params?.offset) searchParams.set("offset", String(params.offset));
126
128
  const query = searchParams.toString();
@@ -180,6 +182,8 @@ var RegistryAPIClient = class {
180
182
  if (params.recommended) searchParams.set("recommended", "true");
181
183
  if (params.intelligenceSource)
182
184
  searchParams.set("intelligence_source", params.intelligenceSource);
185
+ if (params.blueprintSet) searchParams.set("blueprint_set", params.blueprintSet);
186
+ if (params.labs) searchParams.set("labs", "true");
183
187
  if (params.limit) searchParams.set("limit", String(params.limit));
184
188
  if (params.offset) searchParams.set("offset", String(params.offset));
185
189
  return this.request(`/search?${searchParams}`);
@@ -203,6 +207,8 @@ var RegistryAPIClient = class {
203
207
  if (params?.recommended) searchParams.set("recommended", "true");
204
208
  if (params?.intelligenceSource)
205
209
  searchParams.set("intelligence_source", params.intelligenceSource);
210
+ if (params?.blueprintSet) searchParams.set("blueprint_set", params.blueprintSet);
211
+ if (params?.labs) searchParams.set("labs", "true");
206
212
  if (params?.limit != null) searchParams.set("limit", String(params.limit));
207
213
  if (params?.offset != null) searchParams.set("offset", String(params.offset));
208
214
  const query = searchParams.toString();
@@ -352,6 +358,257 @@ function createRegistryClient(options = {}) {
352
358
  };
353
359
  }
354
360
 
361
+ // src/discovery.ts
362
+ var STOP_WORDS = /* @__PURE__ */ new Set([
363
+ "a",
364
+ "an",
365
+ "and",
366
+ "app",
367
+ "are",
368
+ "as",
369
+ "at",
370
+ "be",
371
+ "by",
372
+ "for",
373
+ "from",
374
+ "has",
375
+ "have",
376
+ "in",
377
+ "into",
378
+ "is",
379
+ "it",
380
+ "of",
381
+ "on",
382
+ "or",
383
+ "page",
384
+ "route",
385
+ "section",
386
+ "that",
387
+ "the",
388
+ "this",
389
+ "to",
390
+ "ui",
391
+ "with"
392
+ ]);
393
+ var DOMAIN_LANGUAGE = {
394
+ recipe: [
395
+ "cookbook",
396
+ "cookbooks",
397
+ "cooking",
398
+ "dish",
399
+ "food",
400
+ "ingredient",
401
+ "ingredients",
402
+ "meal",
403
+ "photo",
404
+ "recipe",
405
+ "recipes"
406
+ ],
407
+ ai: [
408
+ "agent",
409
+ "ai",
410
+ "assistant",
411
+ "chat",
412
+ "claude",
413
+ "generate",
414
+ "generation",
415
+ "image",
416
+ "model",
417
+ "prompt",
418
+ "vision"
419
+ ],
420
+ social: [
421
+ "activity",
422
+ "avatar",
423
+ "comment",
424
+ "community",
425
+ "feed",
426
+ "follow",
427
+ "like",
428
+ "profile",
429
+ "share",
430
+ "social",
431
+ "user"
432
+ ],
433
+ commerce: ["cart", "checkout", "commerce", "ecommerce", "order", "price", "product", "shop"],
434
+ dashboard: ["analytics", "chart", "dashboard", "kpi", "metric", "report", "table"],
435
+ form: ["form", "input", "settings", "submit", "toggle", "upload"]
436
+ };
437
+ function isRecord2(value) {
438
+ return typeof value === "object" && value !== null && !Array.isArray(value);
439
+ }
440
+ function stringArray(value) {
441
+ return Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
442
+ }
443
+ function recordToText(value) {
444
+ if (!isRecord2(value)) return "";
445
+ return Object.values(value).flatMap((entry) => {
446
+ if (typeof entry === "string") return [entry];
447
+ if (Array.isArray(entry))
448
+ return entry.filter((item) => typeof item === "string");
449
+ if (isRecord2(entry)) return [recordToText(entry)];
450
+ return [];
451
+ }).join(" ");
452
+ }
453
+ function patternToDiscoveryCandidate(pattern, options = {}) {
454
+ const record = pattern;
455
+ const slug = options.slug || (typeof record.slug === "string" ? record.slug : void 0) || (typeof record.id === "string" ? record.id : void 0);
456
+ return {
457
+ id: typeof record.id === "string" ? record.id : slug ?? "pattern",
458
+ slug,
459
+ name: typeof record.name === "string" ? record.name : slug,
460
+ description: typeof record.description === "string" ? record.description : void 0,
461
+ tags: stringArray(record.tags),
462
+ components: stringArray(record.components),
463
+ interactions: stringArray(record.interactions),
464
+ visual_brief: typeof record.visual_brief === "string" ? record.visual_brief : typeof record.visualBrief === "string" ? record.visualBrief : void 0,
465
+ layout_hints: isRecord2(record.layout_hints) ? record.layout_hints : void 0,
466
+ aliases: stringArray(record.aliases),
467
+ category: typeof record.category === "string" ? record.category : void 0,
468
+ domain: typeof record.domain === "string" ? record.domain : void 0,
469
+ source: options.source,
470
+ pattern
471
+ };
472
+ }
473
+ function tokenize(value) {
474
+ if (!value) return [];
475
+ const tokens = value.toLowerCase().replace(/([a-z])([A-Z])/g, "$1 $2").split(/[^a-z0-9]+/g).map((token) => token.trim()).filter((token) => token.length >= 2 && !STOP_WORDS.has(token));
476
+ return [...new Set(tokens)];
477
+ }
478
+ function inferDomainTerms(tokens) {
479
+ const tokenSet = new Set(tokens);
480
+ const matches = [];
481
+ for (const [domain, terms] of Object.entries(DOMAIN_LANGUAGE)) {
482
+ const hits = terms.filter((term) => tokenSet.has(term));
483
+ if (hits.length > 0) matches.push({ domain, terms });
484
+ }
485
+ return matches;
486
+ }
487
+ function candidateText(candidate) {
488
+ const pattern = candidate.pattern;
489
+ const patternRecord = isRecord2(pattern) ? pattern : {};
490
+ const presets = isRecord2(patternRecord.presets) ? recordToText(patternRecord.presets) : "";
491
+ const composition = isRecord2(patternRecord.composition) ? recordToText(patternRecord.composition) : "";
492
+ const motion = isRecord2(patternRecord.motion) ? recordToText(patternRecord.motion) : "";
493
+ const responsive = isRecord2(patternRecord.responsive) ? recordToText(patternRecord.responsive) : "";
494
+ const accessibility = isRecord2(patternRecord.accessibility) ? recordToText(patternRecord.accessibility) : "";
495
+ return {
496
+ high: [
497
+ candidate.slug,
498
+ candidate.id,
499
+ candidate.name,
500
+ candidate.aliases?.join(" "),
501
+ candidate.category,
502
+ candidate.domain
503
+ ].filter(Boolean).join(" "),
504
+ medium: [
505
+ candidate.description,
506
+ candidate.tags?.join(" "),
507
+ candidate.components?.join(" "),
508
+ candidate.interactions?.join(" "),
509
+ candidate.visual_brief,
510
+ candidate.layout_hints ? recordToText(candidate.layout_hints) : ""
511
+ ].filter(Boolean).join(" "),
512
+ low: [presets, composition, motion, responsive, accessibility].filter(Boolean).join(" ")
513
+ };
514
+ }
515
+ function fieldMatches(tokens, text) {
516
+ const lower = text.toLowerCase();
517
+ return tokens.filter((token) => lower.includes(token));
518
+ }
519
+ function addReason(reasons, reason) {
520
+ if (!reasons.includes(reason)) reasons.push(reason);
521
+ }
522
+ function scorePatternCandidate(input, candidate) {
523
+ const queryTokens = tokenize(input.query);
524
+ const routeTokens = tokenize(input.route);
525
+ const codeTokens = tokenize(input.code).slice(0, 250);
526
+ const tokens = [.../* @__PURE__ */ new Set([...queryTokens, ...routeTokens, ...codeTokens])];
527
+ const text = candidateText(candidate);
528
+ const reasons = [];
529
+ const matchedTerms = /* @__PURE__ */ new Set();
530
+ let score = 0;
531
+ const highMatches = fieldMatches(tokens, text.high);
532
+ if (highMatches.length > 0) {
533
+ score += highMatches.length * 18;
534
+ for (const term of highMatches) matchedTerms.add(term);
535
+ addReason(reasons, "matched slug, name, alias, category, or domain");
536
+ }
537
+ const mediumMatches = fieldMatches(tokens, text.medium);
538
+ if (mediumMatches.length > 0) {
539
+ score += mediumMatches.length * 10;
540
+ for (const term of mediumMatches) matchedTerms.add(term);
541
+ addReason(
542
+ reasons,
543
+ "matched description, tags, components, interactions, visual brief, or layout hints"
544
+ );
545
+ }
546
+ const lowMatches = fieldMatches(tokens, text.low);
547
+ if (lowMatches.length > 0) {
548
+ score += lowMatches.length * 4;
549
+ for (const term of lowMatches) matchedTerms.add(term);
550
+ addReason(reasons, "matched composition, motion, responsive, preset, or accessibility details");
551
+ }
552
+ const slug = (candidate.slug || candidate.id).toLowerCase();
553
+ const query = (input.query || "").toLowerCase();
554
+ if (query && (slug === query || candidate.name?.toLowerCase() === query)) {
555
+ score += 50;
556
+ addReason(reasons, "exact slug or name match");
557
+ } else if (query && slug.includes(query.replace(/\s+/g, "-"))) {
558
+ score += 28;
559
+ addReason(reasons, "direct slug phrase match");
560
+ }
561
+ for (const domain of inferDomainTerms(tokens)) {
562
+ const candidateDomainText = [text.high, text.medium, text.low].join(" ").toLowerCase();
563
+ const hits = domain.terms.filter((term) => candidateDomainText.includes(term));
564
+ if (hits.length > 0) {
565
+ score += Math.min(30, hits.length * 6);
566
+ for (const term of hits) matchedTerms.add(term);
567
+ addReason(reasons, `${domain.domain} domain language`);
568
+ }
569
+ }
570
+ if (input.route) {
571
+ const route = input.route.toLowerCase();
572
+ if (route.includes(slug) || slug.includes(route.replace(/^\//, "").replace(/\//g, "-"))) {
573
+ score += 16;
574
+ addReason(reasons, "route name resembles pattern slug");
575
+ }
576
+ }
577
+ if (input.code) {
578
+ const code = input.code.toLowerCase();
579
+ const codeHints = [
580
+ ["intersectionobserver", "scroll-reveal"],
581
+ ["avatar", "social profile or feed"],
582
+ ["like", "social engagement"],
583
+ ["upload", "upload flow"],
584
+ ['input type="file"', "upload flow"],
585
+ ["textarea", "form or chat input"],
586
+ ["message", "chat surface"],
587
+ ["grid", "grid composition"],
588
+ ["form", "form surface"]
589
+ ];
590
+ for (const [needle, label] of codeHints) {
591
+ if (code.includes(needle) && [text.high, text.medium, text.low].join(" ").toLowerCase().includes(label.split(" ")[0])) {
592
+ score += 8;
593
+ addReason(reasons, `source code hint: ${label}`);
594
+ }
595
+ }
596
+ }
597
+ return {
598
+ candidate,
599
+ score,
600
+ reasons,
601
+ matchedTerms: [...matchedTerms].sort()
602
+ };
603
+ }
604
+ function rankPatternCandidates(input, candidates) {
605
+ const limit = input.limit ?? 10;
606
+ return candidates.map((candidate) => scorePatternCandidate(input, candidate)).filter((match) => match.score > 0).sort((a, b) => {
607
+ if (b.score !== a.score) return b.score - a.score;
608
+ return (a.candidate.slug || a.candidate.id).localeCompare(b.candidate.slug || b.candidate.id);
609
+ }).slice(0, limit);
610
+ }
611
+
355
612
  // src/pattern.ts
356
613
  function resolvePatternPreset(pattern, explicitPreset, themeDefaultPresets) {
357
614
  const presets = pattern.presets;
@@ -375,6 +632,95 @@ function resolvePatternPreset(pattern, explicitPreset, themeDefaultPresets) {
375
632
  return { preset: presetName, layout: preset.layout, code: preset.code };
376
633
  }
377
634
 
635
+ // src/types.ts
636
+ var BLUEPRINT_PORTFOLIO_VISIBILITIES = [
637
+ "featured",
638
+ "public",
639
+ "labs",
640
+ "hidden"
641
+ ];
642
+ var BLUEPRINT_PORTFOLIO_MATURITIES = [
643
+ "certified-flagship",
644
+ "supported-contract",
645
+ "experimental",
646
+ "fold-candidate",
647
+ "legacy-hidden"
648
+ ];
649
+ var BLUEPRINT_ARTIFACT_STATUSES = ["none", "planned", "candidate", "certified"];
650
+ var PUBLIC_BLUEPRINT_SETS = ["all", "featured", "certified", "labs"];
651
+ function isBlueprintPortfolioVisibility(value) {
652
+ return BLUEPRINT_PORTFOLIO_VISIBILITIES.includes(value);
653
+ }
654
+ function isBlueprintPortfolioMaturity(value) {
655
+ return BLUEPRINT_PORTFOLIO_MATURITIES.includes(value);
656
+ }
657
+ function isBlueprintArtifactStatus(value) {
658
+ return BLUEPRINT_ARTIFACT_STATUSES.includes(value);
659
+ }
660
+ function isPublicBlueprintSet(value) {
661
+ return PUBLIC_BLUEPRINT_SETS.includes(value);
662
+ }
663
+ function isRecord3(value) {
664
+ return typeof value === "object" && value !== null && !Array.isArray(value);
665
+ }
666
+ function getBlueprintPortfolioMetadata(value) {
667
+ const candidate = isRecord3(value) && isRecord3(value.blueprint_portfolio) ? value.blueprint_portfolio : value;
668
+ if (!isRecord3(candidate)) {
669
+ return null;
670
+ }
671
+ const artifact = candidate.artifact;
672
+ if (!isBlueprintPortfolioVisibility(candidate.visibility) || !isBlueprintPortfolioMaturity(candidate.maturity) || typeof candidate.rationale !== "string" || !isRecord3(artifact) || !isBlueprintArtifactStatus(artifact.status)) {
673
+ return null;
674
+ }
675
+ return {
676
+ visibility: candidate.visibility,
677
+ maturity: candidate.maturity,
678
+ rationale: candidate.rationale,
679
+ recommended_alternative: typeof candidate.recommended_alternative === "string" ? candidate.recommended_alternative : void 0,
680
+ artifact: {
681
+ status: artifact.status,
682
+ showcase: typeof artifact.showcase === "string" ? artifact.showcase : void 0,
683
+ notes: typeof artifact.notes === "string" ? artifact.notes : void 0
684
+ }
685
+ };
686
+ }
687
+ var CONTENT_TYPES = ["pattern", "theme", "blueprint", "archetype", "shell"];
688
+ var API_CONTENT_TYPES = [
689
+ "patterns",
690
+ "themes",
691
+ "blueprints",
692
+ "archetypes",
693
+ "shells"
694
+ ];
695
+ var CONTENT_TYPE_TO_API_CONTENT_TYPE = {
696
+ pattern: "patterns",
697
+ theme: "themes",
698
+ blueprint: "blueprints",
699
+ archetype: "archetypes",
700
+ shell: "shells"
701
+ };
702
+ var API_CONTENT_TYPE_TO_CONTENT_TYPE = {
703
+ patterns: "pattern",
704
+ themes: "theme",
705
+ blueprints: "blueprint",
706
+ archetypes: "archetype",
707
+ shells: "shell"
708
+ };
709
+ function isContentType(value) {
710
+ return CONTENT_TYPES.includes(value);
711
+ }
712
+ function isApiContentType(value) {
713
+ return API_CONTENT_TYPES.includes(value);
714
+ }
715
+ var PUBLIC_CONTENT_SOURCES = ["official", "community", "organization"];
716
+ function isPublicContentSource(value) {
717
+ return PUBLIC_CONTENT_SOURCES.includes(value);
718
+ }
719
+ var CONTENT_INTELLIGENCE_SOURCES = ["authored", "benchmark", "hybrid"];
720
+ function isContentIntelligenceSource(value) {
721
+ return CONTENT_INTELLIGENCE_SOURCES.includes(value);
722
+ }
723
+
378
724
  // src/ranking.ts
379
725
  function verificationScore(status) {
380
726
  switch (status) {
@@ -420,6 +766,18 @@ function confidenceTierScore(tier) {
420
766
  function getRecommendedPriority(item) {
421
767
  const intelligence = item.intelligence;
422
768
  let score = 0;
769
+ const portfolio = getBlueprintPortfolioMetadata(item.blueprint_portfolio);
770
+ if (portfolio?.visibility === "featured") {
771
+ score += 900;
772
+ }
773
+ if (portfolio?.artifact.status === "certified") {
774
+ score += 650;
775
+ } else if (portfolio?.artifact.status === "candidate") {
776
+ score += 120;
777
+ }
778
+ if (portfolio?.visibility === "labs" || portfolio?.visibility === "hidden") {
779
+ score -= 400;
780
+ }
423
781
  if (!intelligence) {
424
782
  return score;
425
783
  }
@@ -522,44 +880,6 @@ function createResolver(options) {
522
880
  };
523
881
  }
524
882
 
525
- // src/types.ts
526
- var CONTENT_TYPES = ["pattern", "theme", "blueprint", "archetype", "shell"];
527
- var API_CONTENT_TYPES = [
528
- "patterns",
529
- "themes",
530
- "blueprints",
531
- "archetypes",
532
- "shells"
533
- ];
534
- var CONTENT_TYPE_TO_API_CONTENT_TYPE = {
535
- pattern: "patterns",
536
- theme: "themes",
537
- blueprint: "blueprints",
538
- archetype: "archetypes",
539
- shell: "shells"
540
- };
541
- var API_CONTENT_TYPE_TO_CONTENT_TYPE = {
542
- patterns: "pattern",
543
- themes: "theme",
544
- blueprints: "blueprint",
545
- archetypes: "archetype",
546
- shells: "shell"
547
- };
548
- function isContentType(value) {
549
- return CONTENT_TYPES.includes(value);
550
- }
551
- function isApiContentType(value) {
552
- return API_CONTENT_TYPES.includes(value);
553
- }
554
- var PUBLIC_CONTENT_SOURCES = ["official", "community", "organization"];
555
- function isPublicContentSource(value) {
556
- return PUBLIC_CONTENT_SOURCES.includes(value);
557
- }
558
- var CONTENT_INTELLIGENCE_SOURCES = ["authored", "benchmark", "hybrid"];
559
- function isContentIntelligenceSource(value) {
560
- return CONTENT_INTELLIGENCE_SOURCES.includes(value);
561
- }
562
-
563
883
  // src/wiring.ts
564
884
  var WIRING_RULES = [
565
885
  {
@@ -702,9 +1022,13 @@ function classifySignal(signal) {
702
1022
  export {
703
1023
  API_CONTENT_TYPES,
704
1024
  API_CONTENT_TYPE_TO_CONTENT_TYPE,
1025
+ BLUEPRINT_ARTIFACT_STATUSES,
1026
+ BLUEPRINT_PORTFOLIO_MATURITIES,
1027
+ BLUEPRINT_PORTFOLIO_VISIBILITIES,
705
1028
  CONTENT_INTELLIGENCE_SOURCES,
706
1029
  CONTENT_TYPES,
707
1030
  CONTENT_TYPE_TO_API_CONTENT_TYPE,
1031
+ PUBLIC_BLUEPRINT_SETS,
708
1032
  PUBLIC_CONTENT_SOURCES,
709
1033
  RegistryAPIClient,
710
1034
  RegistryAPIError,
@@ -715,12 +1039,20 @@ export {
715
1039
  createResolver,
716
1040
  deriveIOWirings,
717
1041
  detectWirings,
1042
+ getBlueprintPortfolioMetadata,
718
1043
  isApiContentType,
1044
+ isBlueprintArtifactStatus,
1045
+ isBlueprintPortfolioMaturity,
1046
+ isBlueprintPortfolioVisibility,
719
1047
  isContentIntelligenceSource,
720
1048
  isContentType,
1049
+ isPublicBlueprintSet,
721
1050
  isPublicContentSource,
722
1051
  normalizePublicContentSort,
1052
+ patternToDiscoveryCandidate,
1053
+ rankPatternCandidates,
723
1054
  resolvePatternPreset,
1055
+ scorePatternCandidate,
724
1056
  sortPublicContent
725
1057
  };
726
1058
  //# sourceMappingURL=index.js.map