@instruments/materialgraph 0.25.0 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/association-types.d.ts +20 -20
  2. package/associations-client.d.ts +4 -4
  3. package/associations-client.js +3 -10
  4. package/attribution-types.d.ts +3 -3
  5. package/brand-most-sampled-types.d.ts +4 -4
  6. package/brand-types.d.ts +6 -6
  7. package/brands-client.d.ts +4 -4
  8. package/brands-client.js +9 -13
  9. package/browse-types.d.ts +8 -8
  10. package/by-colour-types.d.ts +18 -18
  11. package/by-spec-types.d.ts +14 -14
  12. package/catalog-discovery-types.d.ts +14 -14
  13. package/catalogue-client.d.ts +4 -4
  14. package/catalogue-client.js +38 -37
  15. package/catalogue-types.d.ts +4 -4
  16. package/citation-types.d.ts +4 -4
  17. package/client-browse.d.ts +4 -4
  18. package/client-browse.js +24 -25
  19. package/client-config.d.ts +2 -2
  20. package/client-exploration.d.ts +3 -3
  21. package/client-exploration.js +4 -6
  22. package/client-source-material-comments.d.ts +4 -4
  23. package/client-source-material-comments.js +4 -38
  24. package/client-vocabulary.d.ts +8 -8
  25. package/client-vocabulary.js +20 -30
  26. package/client.d.ts +11 -45
  27. package/client.js +143 -188
  28. package/codes-client.d.ts +2 -2
  29. package/codes-client.js +8 -8
  30. package/codes-types.d.ts +34 -34
  31. package/colour-aggregate-types.d.ts +24 -24
  32. package/colours-client.d.ts +2 -2
  33. package/colours-client.js +15 -15
  34. package/compare-types.d.ts +10 -10
  35. package/composition-client.d.ts +3 -3
  36. package/composition-client.js +8 -7
  37. package/composition-types.d.ts +12 -12
  38. package/coordinate-client.d.ts +2 -2
  39. package/coordinate-client.js +1 -13
  40. package/corpus-scope-types.d.ts +3 -3
  41. package/corpus-scope-types.js +1 -25
  42. package/correction-types.d.ts +10 -10
  43. package/demand-batch-types.d.ts +18 -18
  44. package/demand-types.d.ts +24 -24
  45. package/errors.d.ts +12 -12
  46. package/errors.js +53 -52
  47. package/exploration-types.d.ts +22 -22
  48. package/fan-deck-types.d.ts +6 -6
  49. package/http-transport.d.ts +13 -21
  50. package/http-transport.js +31 -28
  51. package/identify-types.d.ts +10 -10
  52. package/index.d.ts +0 -6
  53. package/index.js +0 -2
  54. package/match-rejection-types.d.ts +6 -6
  55. package/match-stream-types.d.ts +32 -32
  56. package/match-stream.d.ts +2 -2
  57. package/match-stream.js +55 -41
  58. package/match-types.d.ts +8 -8
  59. package/material-asset-types.d.ts +26 -26
  60. package/material-block-types.d.ts +6 -6
  61. package/material-query-plan-types.d.ts +26 -26
  62. package/material-resources-client.d.ts +5 -5
  63. package/material-resources-client.js +10 -20
  64. package/material-types.d.ts +12 -12
  65. package/ndjson.js +16 -8
  66. package/operations.d.ts +6 -6
  67. package/operations.js +9 -39
  68. package/package.json +1 -1
  69. package/palette-completion-types.d.ts +22 -22
  70. package/palette-search-types.d.ts +28 -28
  71. package/query-understanding-types.d.ts +38 -35
  72. package/render-assets-types.d.ts +8 -8
  73. package/resolve-client.d.ts +2 -2
  74. package/resolve-client.js +4 -6
  75. package/resolve-colour-types.d.ts +8 -8
  76. package/resolve-types.d.ts +10 -10
  77. package/run-types.d.ts +70 -70
  78. package/runs.d.ts +13 -26
  79. package/runs.js +77 -89
  80. package/samplize-catalogue-types.d.ts +18 -18
  81. package/samplize-colorvec.d.ts +20 -20
  82. package/samplize-colorvec.js +66 -42
  83. package/schema-resource-types.d.ts +38 -38
  84. package/schema-resource-types.js +5 -5
  85. package/search-card-types.d.ts +4 -4
  86. package/search-collapse-types.d.ts +4 -4
  87. package/search-mood-types.d.ts +2 -2
  88. package/search-refinement-types.d.ts +8 -8
  89. package/search-request-types.d.ts +5 -5
  90. package/search-response-receipt-types.d.ts +58 -42
  91. package/search-response-types.d.ts +2 -2
  92. package/search-result-validation.d.ts +3 -3
  93. package/search-result-validation.js +53 -36
  94. package/search-window-types.d.ts +2 -2
  95. package/slot-pool-types.d.ts +20 -20
  96. package/source-material-validation.d.ts +3 -3
  97. package/source-material-validation.js +35 -39
  98. package/spec-facet-types.d.ts +10 -10
  99. package/spec-types.d.ts +6 -6
  100. package/specifications.d.ts +9 -25
  101. package/specifications.js +15 -21
  102. package/sse.d.ts +2 -2
  103. package/sse.js +32 -16
  104. package/substitute-types.d.ts +22 -22
  105. package/suggest-types.d.ts +7 -7
  106. package/suggest-types.js +3 -3
  107. package/taxonomy-coverage-types.d.ts +8 -8
  108. package/types.d.ts +119 -119
  109. package/validation.d.ts +26 -26
  110. package/validation.js +97 -92
  111. package/webmcp.d.ts +2 -2
  112. package/webmcp.generated.js +94 -93
  113. package/project-types.d.ts +0 -79
  114. package/project-types.js +0 -9
  115. package/projects.d.ts +0 -13
  116. package/projects.js +0 -46
  117. package/run-events.d.ts +0 -106
  118. package/run-events.js +0 -32
  119. package/schedule-reference-types.d.ts +0 -46
  120. package/schedule-reference-types.js +0 -1
  121. package/schedule-references-client.d.ts +0 -14
  122. package/schedule-references-client.js +0 -26
  123. package/schedule-types.d.ts +0 -261
  124. package/schedule-types.js +0 -1
@@ -48,23 +48,21 @@ const SOURCE_ROLES = [
48
48
  "inspiration_image",
49
49
  "unclassified",
50
50
  ];
51
- function detailFailure(batchId) {
52
- return (reason) => MaterialGraphError.unexpected(`GET /api/v1/source-material/${batchId} returned a malformed body: ${reason}`);
53
- }
54
- function checkAnalysis(value, what, fail) {
51
+ const detailFailure = (batchId) => (reason) => MaterialGraphError.unexpected(`GET /api/v1/source-material/${batchId} returned a malformed body: ${reason}`);
52
+ const checkAnalysis = (value, what, fail) => {
55
53
  const record = requireRecord(value, what, fail);
56
54
  // `model` and `analysedAt` ARE `.min(1)` canonically — fields this endpoint
57
55
  // introduces, where an empty value is meaningless and `model` already cannot
58
56
  // be written empty. Parity here is achieved by the schema being strict, never
59
57
  // by this mirror freelancing past it.
60
58
  return {
59
+ analysedAt: requireNonEmptyString(record.analysedAt, `${what}.analysedAt`, fail),
61
60
  kind: requireEnum(record.kind, ANALYSIS_KINDS, `${what}.kind`, fail),
62
61
  model: requireNonEmptyString(record.model, `${what}.model`, fail),
63
- status: requireEnum(record.status, ANALYSIS_STATUSES, `${what}.status`, fail),
64
62
  result: requireJsonRecord(record.result, `${what}.result`, fail),
65
- analysedAt: requireNonEmptyString(record.analysedAt, `${what}.analysedAt`, fail),
63
+ status: requireEnum(record.status, ANALYSIS_STATUSES, `${what}.status`, fail),
66
64
  };
67
- }
65
+ };
68
66
  /** `sourceMaterialCommentAuthorSchema`'s `attribution` members, verbatim. */
69
67
  const COMMENT_ATTRIBUTIONS = ["reported"];
70
68
  /** The canonical cap on both author fields: `.trim().min(1).max(160)`. */
@@ -84,27 +82,27 @@ const AUTHOR_FIELD_MAX = 160;
84
82
  * schema caps. A validator that is looser than the contract is a validator that
85
83
  * lets the two drift without anything failing.
86
84
  */
87
- function checkCommentBase(record, what, fail) {
85
+ const checkCommentBase = (record, what, fail) => {
88
86
  const author = requireRecord(record.author, `${what}.author`, fail);
89
87
  const recordedBy = requireRecord(record.recordedBy, `${what}.recordedBy`, fail);
90
88
  return {
91
- commentId: requireNonEmptyString(record.commentId, `${what}.commentId`, fail),
92
89
  author: {
93
- name: requireTrimmedBoundedString(author.name, `${what}.author.name`, AUTHOR_FIELD_MAX, fail),
94
- organisation: requireNullableTrimmedBoundedString(author.organisation, `${what}.author.organisation`, AUTHOR_FIELD_MAX, fail),
95
90
  // Closed here, matching the canonical `z.literal("reported")`, while the
96
91
  // published type is `OpenEnum` so a newer server value keeps
97
92
  // type-checking — the same split the analysis enums make.
98
93
  attribution: requireEnum(author.attribution, COMMENT_ATTRIBUTIONS, `${what}.author.attribution`, fail),
94
+ name: requireTrimmedBoundedString(author.name, `${what}.author.name`, AUTHOR_FIELD_MAX, fail),
95
+ organisation: requireNullableTrimmedBoundedString(author.organisation, `${what}.author.organisation`, AUTHOR_FIELD_MAX, fail),
99
96
  },
97
+ body: requireNonEmptyString(record.body, `${what}.body`, fail),
98
+ commentId: requireNonEmptyString(record.commentId, `${what}.commentId`, fail),
99
+ createdAt: requireNonEmptyString(record.createdAt, `${what}.createdAt`, fail),
100
100
  recordedBy: {
101
101
  principalId: requireNonEmptyString(recordedBy.principalId, `${what}.recordedBy.principalId`, fail),
102
102
  },
103
- body: requireNonEmptyString(record.body, `${what}.body`, fail),
104
- createdAt: requireNonEmptyString(record.createdAt, `${what}.createdAt`, fail),
105
103
  };
106
- }
107
- function checkComment(value, what, fail) {
104
+ };
105
+ const checkComment = (value, what, fail) => {
108
106
  const record = requireRecord(value, what, fail);
109
107
  // Same reasoning as `analyses`: no `.default([])` canonically, so an absent
110
108
  // `replies` is malformed rather than empty.
@@ -113,8 +111,8 @@ function checkComment(value, what, fail) {
113
111
  ...checkCommentBase(record, what, fail),
114
112
  replies: replies.map((reply, index) => checkCommentBase(requireRecord(reply, `${what}.replies[${index}]`, fail), `${what}.replies[${index}]`, fail)),
115
113
  };
116
- }
117
- function checkFile(value, what, fail) {
114
+ };
115
+ const checkFile = (value, what, fail) => {
118
116
  const record = requireRecord(value, what, fail);
119
117
  // NOT `requireDefaultedArray`: the schema has no `.default([])`, so an absent
120
118
  // `analyses` is a malformed body rather than an empty one. Keeping that
@@ -133,11 +131,7 @@ function checkFile(value, what, fail) {
133
131
  // refuse a response the server can legitimately produce, and tightening them
134
132
  // is a change to a contract this endpoint does not own.
135
133
  return {
136
- sourceId: requireString(record.sourceId, `${what}.sourceId`, fail),
137
- role: requireEnum(record.role, SOURCE_ROLES, `${what}.role`, fail),
138
- filename: requireString(record.filename, `${what}.filename`, fail),
139
- mediaType: requireString(record.mediaType, `${what}.mediaType`, fail),
140
- sha256: requireString(record.sha256, `${what}.sha256`, fail),
134
+ analyses: analyses.map((analysis, index) => checkAnalysis(analysis, `${what}.analyses[${index}]`, fail)),
141
135
  // `requireSafeInteger`, NOT `requireIntegerAtLeast(…, 0)`. Canonically these
142
136
  // are a bare `z.number().int()` on the INHERITED
143
137
  // `sourceMaterialFileReceiptSchema`, which carries no lower bound — so a
@@ -150,17 +144,21 @@ function checkFile(value, what, fail) {
150
144
  // fields is a canonical change to a schema shared with the upload path, and
151
145
  // belongs in its own ticket with this mirror following after.
152
146
  bytes: requireSafeInteger(record.bytes, `${what}.bytes`, fail),
147
+ comments: comments.map((comment, index) => checkComment(comment, `${what}.comments[${index}]`, fail)),
148
+ commentsTruncated,
149
+ filename: requireString(record.filename, `${what}.filename`, fail),
150
+ mediaType: requireString(record.mediaType, `${what}.mediaType`, fail),
153
151
  pages: record.pages === null
154
152
  ? null
155
153
  : requireSafeInteger(record.pages, `${what}.pages`, fail),
154
+ role: requireEnum(record.role, SOURCE_ROLES, `${what}.role`, fail),
155
+ sha256: requireString(record.sha256, `${what}.sha256`, fail),
156
+ sourceId: requireString(record.sourceId, `${what}.sourceId`, fail),
156
157
  url: requireString(record.url, `${what}.url`, fail),
157
- analyses: analyses.map((analysis, index) => checkAnalysis(analysis, `${what}.analyses[${index}]`, fail)),
158
- comments: comments.map((comment, index) => checkComment(comment, `${what}.comments[${index}]`, fail)),
159
- commentsTruncated,
160
158
  };
161
- }
159
+ };
162
160
  /** Validate a batch-detail body, returning it typed, or throw explaining why. */
163
- export function checkSourceMaterialBatchDetail(value, batchId) {
161
+ export const checkSourceMaterialBatchDetail = (value, batchId) => {
164
162
  const fail = detailFailure(batchId);
165
163
  const record = requireRecord(value, "body", fail);
166
164
  const sources = requireArray(record.sources, "body.sources", fail);
@@ -169,21 +167,19 @@ export function checkSourceMaterialBatchDetail(value, batchId) {
169
167
  createdAt: requireString(record.createdAt, "body.createdAt", fail),
170
168
  sources: sources.map((file, index) => checkFile(file, `body.sources[${index}]`, fail)),
171
169
  };
172
- }
173
- function commentsFailure(verb, sourceId) {
174
- return (reason) => MaterialGraphError.unexpected(`${verb} .../sources/${sourceId}/comments returned a malformed body: ${reason}`);
175
- }
170
+ };
171
+ const commentsFailure = (verb, sourceId) => (reason) => MaterialGraphError.unexpected(`${verb} .../sources/${sourceId}/comments returned a malformed body: ${reason}`);
176
172
  /** Validate a comment-thread body, returning it typed, or throw explaining why. */
177
- export function checkSourceMaterialCommentThread(value, sourceId) {
173
+ export const checkSourceMaterialCommentThread = (value, sourceId) => {
178
174
  const fail = commentsFailure("GET", sourceId);
179
175
  const record = requireRecord(value, "body", fail);
180
176
  const comments = requireArray(record.comments, "body.comments", fail);
181
177
  return {
182
- sourceId: requireString(record.sourceId, "body.sourceId", fail),
183
178
  comments: comments.map((comment, index) => checkComment(comment, `body.comments[${index}]`, fail)),
179
+ sourceId: requireString(record.sourceId, "body.sourceId", fail),
184
180
  truncated: requireBoolean(record.truncated, "body.truncated", fail),
185
181
  };
186
- }
182
+ };
187
183
  /** `storedSourceMaterialCommentSchema`'s discriminator members, verbatim. */
188
184
  const STORED_COMMENT_KINDS = ["root", "reply"];
189
185
  /**
@@ -195,7 +191,7 @@ const STORED_COMMENT_KINDS = ["root", "reply"];
195
191
  * defect this discriminated union was added to fix, and a mirror that did it
196
192
  * would put the lie back on the client side.
197
193
  */
198
- function checkStoredComment(value, what, fail) {
194
+ const checkStoredComment = (value, what, fail) => {
199
195
  const record = requireRecord(value, what, fail);
200
196
  const kind = requireEnum(record.kind, STORED_COMMENT_KINDS, `${what}.kind`, fail);
201
197
  if (kind === "reply") {
@@ -206,13 +202,13 @@ function checkStoredComment(value, what, fail) {
206
202
  };
207
203
  }
208
204
  return { kind: "root", ...checkComment(record, what, fail) };
209
- }
205
+ };
210
206
  /** Validate a posted-comment body, returning it typed, or throw explaining why. */
211
- export function checkPostSourceMaterialCommentResult(value, sourceId) {
207
+ export const checkPostSourceMaterialCommentResult = (value, sourceId) => {
212
208
  const fail = commentsFailure("POST", sourceId);
213
209
  const record = requireRecord(value, "body", fail);
214
210
  return {
215
- sourceId: requireString(record.sourceId, "body.sourceId", fail),
216
211
  comment: checkStoredComment(record.comment, "body.comment", fail),
212
+ sourceId: requireString(record.sourceId, "body.sourceId", fail),
217
213
  };
218
- }
214
+ };
@@ -1,9 +1,9 @@
1
- export interface SpecFacetValue {
1
+ export type SpecFacetValue = {
2
2
  value: string;
3
3
  variants: number;
4
4
  brands: number;
5
5
  label?: string;
6
- }
6
+ };
7
7
  /**
8
8
  * A NUMERIC facet's observed span and the coverage behind it.
9
9
  *
@@ -17,13 +17,13 @@ export interface SpecFacetValue {
17
17
  * `unparseableVariants` is the honesty companion: values recorded here that are
18
18
  * not numbers, which a range EXCLUDES rather than reading as zero.
19
19
  */
20
- export interface SpecFacetRange {
20
+ export type SpecFacetRange = {
21
21
  min: number;
22
22
  max: number;
23
23
  variants: number;
24
24
  unparseableVariants: number;
25
- }
26
- export interface SpecFacet {
25
+ };
26
+ export type SpecFacet = {
27
27
  attributeId: string;
28
28
  name: string;
29
29
  isArray: boolean;
@@ -39,15 +39,15 @@ export interface SpecFacet {
39
39
  /** Present ONLY on a `numeric` facet. */
40
40
  range?: SpecFacetRange;
41
41
  description?: string;
42
- }
43
- export interface SpecFacetsListResponse {
42
+ };
43
+ export type SpecFacetsListResponse = {
44
44
  mode: "facets";
45
45
  count: number;
46
46
  facets: SpecFacet[];
47
- }
48
- export interface SpecFacetsDistributionResponse {
47
+ };
48
+ export type SpecFacetsDistributionResponse = {
49
49
  mode: "distribution";
50
50
  attributeId: string;
51
51
  count: number;
52
52
  values: SpecFacetValue[];
53
- }
53
+ };
package/spec-types.d.ts CHANGED
@@ -60,15 +60,15 @@ export type CertificationProgrammeAttributeId = typeof CERTIFICATION_PROGRAMME_A
60
60
  * and `client.attributeValues()`, or resolve free text with
61
61
  * `client.resolveAttribute()`.
62
62
  */
63
- export interface SpecTermFilter {
63
+ export type SpecTermFilter = {
64
64
  attributeId: string;
65
65
  term: string;
66
- }
66
+ };
67
67
  /** A negative spec criterion; absence of a value remains unproven. */
68
- export interface SpecExclusionFilter {
68
+ export type SpecExclusionFilter = {
69
69
  attributeId: string;
70
70
  excludesTerm: string;
71
- }
71
+ };
72
72
  /**
73
73
  * A presence spec criterion: the attribute must have ANY accepted value —
74
74
  * "has an EPD on record", "has a measured slip figure at all". Not supported for
@@ -90,10 +90,10 @@ export type SpecPresenceFilter = {
90
90
  * virtual {@link CERTIFICATION_PROGRAMME_ATTRIBUTE_ID}. Structurally assignable
91
91
  * to {@link SpecFilter}, so it drops straight into `search({ filters })`.
92
92
  */
93
- export interface CertificationProgrammeFilter {
93
+ export type CertificationProgrammeFilter = {
94
94
  attributeId: CertificationProgrammeAttributeId;
95
95
  term: string;
96
- }
96
+ };
97
97
  /**
98
98
  * A hard, canonical-attribute spec criterion — either a term match or a
99
99
  * presence check. The union the search engine applies as a restriction BEFORE
@@ -1,22 +1,6 @@
1
- /**
2
- * Zero-dependency builders for the specification surface. Turn a plain-language
3
- * spec intent into a typed criterion the SDK's two spec operations accept:
4
- *
5
- * - `search({ filters })` takes {@link SpecFilter} objects — a spec gate layered
6
- * onto a ranked (colour / visual / lexical) search. Build them with
7
- * {@link specTerm}, {@link hasSpec}, and {@link certificationProgramme}.
8
- * - `bySpec(specs)` takes `attribute:value` strings — the value-anchored,
9
- * spec-only primitive. Build them with {@link specCriterion} and
10
- * {@link certificationProgrammeCriterion}.
11
- *
12
- * The point of these helpers: Desk (and any spec-driven caller) never has to
13
- * remember that a certification programme lives under a VIRTUAL attribute, nor
14
- * hand-format the `attribute:value` string the `bySpec` route splits on its
15
- * first `:`.
16
- */
17
- import { type CertificationProgrammeFilter, type SpecExclusionFilter, type SpecPresenceFilter, type SpecTermFilter } from "./spec-types.js";
1
+ import type { CertificationProgrammeFilter, SpecExclusionFilter, SpecPresenceFilter, SpecTermFilter } from "./spec-types.js";
18
2
  /** A hard criterion excluding one known canonical value while retaining only variants with known values. */
19
- export declare function excludeSpec(attributeId: string, excludesTerm: string): SpecExclusionFilter;
3
+ export declare const excludeSpec: (attributeId: string, excludesTerm: string) => SpecExclusionFilter;
20
4
  /**
21
5
  * A term-equality criterion for `search({ filters })` — the attribute must
22
6
  * carry this canonical value.
@@ -25,7 +9,7 @@ export declare function excludeSpec(attributeId: string, excludesTerm: string):
25
9
  * // "same fire class as the seed"
26
10
  * client.search({ similarTo: { variantId }, filters: [specTerm("fire_classifications", "astm_e84_class_a")] });
27
11
  */
28
- export declare function specTerm(attributeId: string, term: string): SpecTermFilter;
12
+ export declare const specTerm: (attributeId: string, term: string) => SpecTermFilter;
29
13
  /**
30
14
  * A presence criterion for `search({ filters })` — the attribute must have ANY
31
15
  * accepted value.
@@ -34,7 +18,7 @@ export declare function specTerm(attributeId: string, term: string): SpecTermFil
34
18
  * // "…and it must have an EPD on record"
35
19
  * client.search({ similarTo: { variantId }, filters: [hasSpec("environmental_product_declaration")] });
36
20
  */
37
- export declare function hasSpec(attributeId: string): SpecPresenceFilter;
21
+ export declare const hasSpec: (attributeId: string) => SpecPresenceFilter;
38
22
  /**
39
23
  * A certification-programme criterion for `search({ filters })`, pinned to the
40
24
  * virtual attribute so a programme matches wherever it landed at ingest
@@ -49,7 +33,7 @@ export declare function hasSpec(attributeId: string): SpecPresenceFilter;
49
33
  * // "find a GREENGUARD-certified alternative"
50
34
  * client.search({ similarTo: { variantId: seed }, filters: [certificationProgramme("GREENGUARD Gold")] });
51
35
  */
52
- export declare function certificationProgramme(programme: string): CertificationProgrammeFilter;
36
+ export declare const certificationProgramme: (programme: string) => CertificationProgrammeFilter;
53
37
  /**
54
38
  * An `attribute:value` criterion string for `bySpec`. The `bySpec` route splits
55
39
  * on the FIRST `:`, so a value may itself contain colons; the attribute id may
@@ -58,7 +42,7 @@ export declare function certificationProgramme(programme: string): Certification
58
42
  * @example
59
43
  * client.bySpec([specCriterion("primary_material", "textile"), specCriterion("fire_classifications", "astm_e84_class_a")]);
60
44
  */
61
- export declare function specCriterion(attributeId: string, value: string): string;
45
+ export declare const specCriterion: (attributeId: string, value: string) => string;
62
46
  /**
63
47
  * An `attribute:min..max` RANGE criterion string for `bySpec`, for a
64
48
  * NUMBER-valued spec (`light_reflectance_value`, `nrc_rating`,
@@ -81,10 +65,10 @@ export declare function specCriterion(attributeId: string, value: string): strin
81
65
  * // a mid-tone band
82
66
  * specRangeCriterion("light_reflectance_value", { min: 30, max: 45 }); // "light_reflectance_value:30..45"
83
67
  */
84
- export declare function specRangeCriterion(attributeId: string, bounds: {
68
+ export declare const specRangeCriterion: (attributeId: string, bounds: {
85
69
  min?: number;
86
70
  max?: number;
87
- }): string;
71
+ }) => string;
88
72
  /**
89
73
  * An `attribute:value` criterion string for `bySpec` that filters by
90
74
  * certification PROGRAMME across all four certification buckets.
@@ -97,4 +81,4 @@ export declare function specRangeCriterion(attributeId: string, bounds: {
97
81
  * // every brand's GREENGUARD-Gold materials
98
82
  * client.bySpec([certificationProgrammeCriterion("GREENGUARD Gold")]);
99
83
  */
100
- export declare function certificationProgrammeCriterion(programme: string): string;
84
+ export declare const certificationProgrammeCriterion: (programme: string) => string;
package/specifications.js CHANGED
@@ -14,11 +14,9 @@
14
14
  * hand-format the `attribute:value` string the `bySpec` route splits on its
15
15
  * first `:`.
16
16
  */
17
- import { CERTIFICATION_PROGRAMME_ATTRIBUTE_ID, } from "./spec-types.js";
17
+ import { CERTIFICATION_PROGRAMME_ATTRIBUTE_ID } from "./spec-types.js";
18
18
  /** A hard criterion excluding one known canonical value while retaining only variants with known values. */
19
- export function excludeSpec(attributeId, excludesTerm) {
20
- return { attributeId, excludesTerm };
21
- }
19
+ export const excludeSpec = (attributeId, excludesTerm) => ({ attributeId, excludesTerm });
22
20
  /**
23
21
  * A term-equality criterion for `search({ filters })` — the attribute must
24
22
  * carry this canonical value.
@@ -27,9 +25,7 @@ export function excludeSpec(attributeId, excludesTerm) {
27
25
  * // "same fire class as the seed"
28
26
  * client.search({ similarTo: { variantId }, filters: [specTerm("fire_classifications", "astm_e84_class_a")] });
29
27
  */
30
- export function specTerm(attributeId, term) {
31
- return { attributeId, term };
32
- }
28
+ export const specTerm = (attributeId, term) => ({ attributeId, term });
33
29
  /**
34
30
  * A presence criterion for `search({ filters })` — the attribute must have ANY
35
31
  * accepted value.
@@ -38,9 +34,10 @@ export function specTerm(attributeId, term) {
38
34
  * // "…and it must have an EPD on record"
39
35
  * client.search({ similarTo: { variantId }, filters: [hasSpec("environmental_product_declaration")] });
40
36
  */
41
- export function hasSpec(attributeId) {
42
- return { attributeId, hasValue: true };
43
- }
37
+ export const hasSpec = (attributeId) => ({
38
+ attributeId,
39
+ hasValue: true,
40
+ });
44
41
  /**
45
42
  * A certification-programme criterion for `search({ filters })`, pinned to the
46
43
  * virtual attribute so a programme matches wherever it landed at ingest
@@ -55,9 +52,10 @@ export function hasSpec(attributeId) {
55
52
  * // "find a GREENGUARD-certified alternative"
56
53
  * client.search({ similarTo: { variantId: seed }, filters: [certificationProgramme("GREENGUARD Gold")] });
57
54
  */
58
- export function certificationProgramme(programme) {
59
- return { attributeId: CERTIFICATION_PROGRAMME_ATTRIBUTE_ID, term: programme };
60
- }
55
+ export const certificationProgramme = (programme) => ({
56
+ attributeId: CERTIFICATION_PROGRAMME_ATTRIBUTE_ID,
57
+ term: programme,
58
+ });
61
59
  /**
62
60
  * An `attribute:value` criterion string for `bySpec`. The `bySpec` route splits
63
61
  * on the FIRST `:`, so a value may itself contain colons; the attribute id may
@@ -66,9 +64,7 @@ export function certificationProgramme(programme) {
66
64
  * @example
67
65
  * client.bySpec([specCriterion("primary_material", "textile"), specCriterion("fire_classifications", "astm_e84_class_a")]);
68
66
  */
69
- export function specCriterion(attributeId, value) {
70
- return `${attributeId}:${value}`;
71
- }
67
+ export const specCriterion = (attributeId, value) => `${attributeId}:${value}`;
72
68
  /**
73
69
  * An `attribute:min..max` RANGE criterion string for `bySpec`, for a
74
70
  * NUMBER-valued spec (`light_reflectance_value`, `nrc_rating`,
@@ -91,7 +87,7 @@ export function specCriterion(attributeId, value) {
91
87
  * // a mid-tone band
92
88
  * specRangeCriterion("light_reflectance_value", { min: 30, max: 45 }); // "light_reflectance_value:30..45"
93
89
  */
94
- export function specRangeCriterion(attributeId, bounds) {
90
+ export const specRangeCriterion = (attributeId, bounds) => {
95
91
  const { min, max } = bounds;
96
92
  if (min === undefined && max === undefined) {
97
93
  throw new Error(`specRangeCriterion("${attributeId}") needs a min, a max, or both — a range with neither would match every recorded value. Use hasSpec() for a presence check.`);
@@ -100,7 +96,7 @@ export function specRangeCriterion(attributeId, bounds) {
100
96
  throw new Error(`specRangeCriterion("${attributeId}") is inverted (min ${min} > max ${max}) and can only ever match nothing.`);
101
97
  }
102
98
  return `${attributeId}:${min ?? ""}..${max ?? ""}`;
103
- }
99
+ };
104
100
  /**
105
101
  * An `attribute:value` criterion string for `bySpec` that filters by
106
102
  * certification PROGRAMME across all four certification buckets.
@@ -113,6 +109,4 @@ export function specRangeCriterion(attributeId, bounds) {
113
109
  * // every brand's GREENGUARD-Gold materials
114
110
  * client.bySpec([certificationProgrammeCriterion("GREENGUARD Gold")]);
115
111
  */
116
- export function certificationProgrammeCriterion(programme) {
117
- return specCriterion(CERTIFICATION_PROGRAMME_ATTRIBUTE_ID, programme);
118
- }
112
+ export const certificationProgrammeCriterion = (programme) => specCriterion(CERTIFICATION_PROGRAMME_ATTRIBUTE_ID, programme);
package/sse.d.ts CHANGED
@@ -11,11 +11,11 @@
11
11
  * chunks are held in an internal buffer until their terminating blank line.
12
12
  */
13
13
  /** One dispatched SSE frame. `data` is the joined data lines (no trailing newline). */
14
- export interface SseFrame {
14
+ export type SseFrame = {
15
15
  id?: string;
16
16
  event?: string;
17
17
  data: string;
18
- }
18
+ };
19
19
  /**
20
20
  * A push-based SSE line assembler. Feed it decoded text with {@link push}; it
21
21
  * returns any frames completed by that text. Call {@link flush} once the stream
package/sse.js CHANGED
@@ -26,13 +26,15 @@ export class SseDecoder {
26
26
  const frames = [];
27
27
  for (;;) {
28
28
  const lineEnd = this.#nextLineEnd();
29
- if (lineEnd === null)
29
+ if (lineEnd === null) {
30
30
  break;
31
+ }
31
32
  const line = this.#buffer.slice(0, lineEnd.index);
32
33
  this.#buffer = this.#buffer.slice(lineEnd.nextIndex);
33
34
  const frame = this.#consumeLine(line);
34
- if (frame)
35
+ if (frame) {
35
36
  frames.push(frame);
37
+ }
36
38
  }
37
39
  return frames;
38
40
  }
@@ -45,8 +47,9 @@ export class SseDecoder {
45
47
  : this.#buffer;
46
48
  this.#buffer = "";
47
49
  const frame = this.#consumeLine(line);
48
- if (frame)
50
+ if (frame) {
49
51
  return [frame];
52
+ }
50
53
  }
51
54
  const frame = this.#dispatch();
52
55
  return frame ? [frame] : [];
@@ -55,11 +58,13 @@ export class SseDecoder {
55
58
  #nextLineEnd() {
56
59
  for (let i = 0; i < this.#buffer.length; i += 1) {
57
60
  const char = this.#buffer[i];
58
- if (char === "\n")
61
+ if (char === "\n") {
59
62
  return { index: i, nextIndex: i + 1 };
63
+ }
60
64
  if (char === "\r") {
61
- if (i === this.#buffer.length - 1)
65
+ if (i === this.#buffer.length - 1) {
62
66
  return null;
67
+ }
63
68
  return this.#buffer[i + 1] === "\n"
64
69
  ? { index: i, nextIndex: i + 2 }
65
70
  : { index: i, nextIndex: i + 1 };
@@ -68,15 +73,18 @@ export class SseDecoder {
68
73
  return null;
69
74
  }
70
75
  #consumeLine(line) {
71
- if (line.length === 0)
76
+ if (line.length === 0) {
72
77
  return this.#dispatch();
73
- if (line.startsWith(":"))
78
+ }
79
+ if (line.startsWith(":")) {
74
80
  return undefined;
81
+ }
75
82
  const colon = line.indexOf(":");
76
83
  const field = colon === -1 ? line : line.slice(0, colon);
77
84
  let value = colon === -1 ? "" : line.slice(colon + 1);
78
- if (value.startsWith(" "))
85
+ if (value.startsWith(" ")) {
79
86
  value = value.slice(1);
87
+ }
80
88
  if (field === "data") {
81
89
  this.#dataLines.push(value);
82
90
  this.#hasFields = true;
@@ -92,13 +100,16 @@ export class SseDecoder {
92
100
  return undefined;
93
101
  }
94
102
  #dispatch() {
95
- if (!this.#hasFields)
103
+ if (!this.#hasFields) {
96
104
  return undefined;
105
+ }
97
106
  const frame = { data: this.#dataLines.join("\n") };
98
- if (this.#event !== undefined)
107
+ if (this.#event !== undefined) {
99
108
  frame.event = this.#event;
100
- if (this.#id !== undefined)
109
+ }
110
+ if (this.#id !== undefined) {
101
111
  frame.id = this.#id;
112
+ }
102
113
  this.#dataLines = [];
103
114
  this.#event = undefined;
104
115
  this.#id = undefined;
@@ -117,28 +128,33 @@ export async function* readSseFrames(body, signal) {
117
128
  const sse = new SseDecoder();
118
129
  try {
119
130
  for (;;) {
120
- if (signal?.aborted)
131
+ if (signal?.aborted) {
121
132
  return;
133
+ }
122
134
  let result;
123
135
  try {
124
136
  result = await reader.read();
125
137
  }
126
138
  catch (error) {
127
- if (signal?.aborted)
139
+ if (signal?.aborted) {
128
140
  return;
141
+ }
129
142
  throw error;
130
143
  }
131
144
  if (result.done) {
132
- for (const frame of sse.push(decoder.decode()))
145
+ for (const frame of sse.push(decoder.decode())) {
133
146
  yield frame;
134
- for (const frame of sse.flush())
147
+ }
148
+ for (const frame of sse.flush()) {
135
149
  yield frame;
150
+ }
136
151
  return;
137
152
  }
138
153
  const text = decoder.decode(result.value, { stream: true });
139
154
  for (const frame of sse.push(text)) {
140
- if (signal?.aborted)
155
+ if (signal?.aborted) {
141
156
  return;
157
+ }
142
158
  yield frame;
143
159
  }
144
160
  }
@@ -2,40 +2,40 @@ import type { BySpecVerdict } from "./by-spec-types.js";
2
2
  import type { VariantId } from "./identifiers.js";
3
3
  import type { MaterialSuccessor } from "./material-types.js";
4
4
  export type SubstituteSignatureFamily = "textile" | "tile" | "coating" | "lighting_electrical" | "acoustic" | "surface" | "furniture" | "sanitaryware" | "fittings" | "architectural_hardware";
5
- export interface SubstituteSignatureCriterion {
5
+ export type SubstituteSignatureCriterion = {
6
6
  role: "primary_material" | "performance";
7
7
  attributeId: string;
8
8
  term: string;
9
- }
10
- export interface AvailableSubstituteSignature {
9
+ };
10
+ export type AvailableSubstituteSignature = {
11
11
  family: SubstituteSignatureFamily;
12
12
  form: string;
13
13
  criteria: [SubstituteSignatureCriterion, SubstituteSignatureCriterion];
14
- }
15
- export interface UnsupportedSubstituteSignature {
14
+ };
15
+ export type UnsupportedSubstituteSignature = {
16
16
  family: SubstituteSignatureFamily;
17
17
  form: string;
18
18
  reason: string;
19
- }
20
- export interface InsufficientSubstituteSignature {
19
+ };
20
+ export type InsufficientSubstituteSignature = {
21
21
  family: SubstituteSignatureFamily;
22
22
  form: string;
23
23
  missingRoles: ("primary_material" | "performance")[];
24
- }
25
- export interface FindSubstitutesOptions {
24
+ };
25
+ export type FindSubstitutesOptions = {
26
26
  excludeBrands?: string[];
27
27
  diversityBoost?: number;
28
28
  limit?: number;
29
29
  cursor?: string;
30
30
  includeSamplingArtifacts?: boolean;
31
31
  includeUnavailable?: boolean;
32
- }
33
- export interface SubstituteMatchedCriterion {
32
+ };
33
+ export type SubstituteMatchedCriterion = {
34
34
  attributeId: string;
35
35
  confidence: number | null;
36
36
  sourceBasis: string | null;
37
- }
38
- export interface SubstituteResultItem {
37
+ };
38
+ export type SubstituteResultItem = {
39
39
  variantId: VariantId;
40
40
  brandSlug: string;
41
41
  brand: string;
@@ -50,8 +50,8 @@ export interface SubstituteResultItem {
50
50
  diversityAdjustment: number;
51
51
  score: number;
52
52
  verdict: BySpecVerdict;
53
- }
54
- export interface AvailableSubstitutesResponse {
53
+ };
54
+ export type AvailableSubstitutesResponse = {
55
55
  status: "available";
56
56
  signatureUsed: AvailableSubstituteSignature;
57
57
  items: SubstituteResultItem[];
@@ -69,7 +69,7 @@ export interface AvailableSubstitutesResponse {
69
69
  poolTruncated: boolean;
70
70
  supersededBy: MaterialSuccessor | null;
71
71
  nextCursor?: string;
72
- }
72
+ };
73
73
  /**
74
74
  * MG-1686. The search read a bounded window of the matches and nothing in it
75
75
  * reached the page, while more matches existed past the window. It is not an
@@ -77,7 +77,7 @@ export interface AvailableSubstitutesResponse {
77
77
  * empty `items`. `reason` names what happened and the next move; follow
78
78
  * `nextCursor` when it is present.
79
79
  */
80
- export interface InconclusiveSubstitutesResponse {
80
+ export type InconclusiveSubstitutesResponse = {
81
81
  status: "inconclusive";
82
82
  signatureUsed: AvailableSubstituteSignature;
83
83
  items: [];
@@ -86,17 +86,17 @@ export interface InconclusiveSubstitutesResponse {
86
86
  reason: string;
87
87
  supersededBy: MaterialSuccessor | null;
88
88
  nextCursor?: string;
89
- }
90
- export interface UnsupportedSubstitutesResponse {
89
+ };
90
+ export type UnsupportedSubstitutesResponse = {
91
91
  status: "unsupported";
92
92
  signature: UnsupportedSubstituteSignature;
93
93
  items: [];
94
94
  supersededBy: MaterialSuccessor | null;
95
- }
96
- export interface InsufficientSubstitutesResponse {
95
+ };
96
+ export type InsufficientSubstitutesResponse = {
97
97
  status: "insufficient_signature";
98
98
  signature: InsufficientSubstituteSignature;
99
99
  items: [];
100
100
  supersededBy: MaterialSuccessor | null;
101
- }
101
+ };
102
102
  export type FindSubstitutesResponse = AvailableSubstitutesResponse | InconclusiveSubstitutesResponse | UnsupportedSubstitutesResponse | InsufficientSubstitutesResponse;