@package-broker/cli 0.19.7 → 2.0.0-rc.1

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 (181) hide show
  1. package/LICENSE +661 -0
  2. package/dist/.tsbuildinfo +1 -0
  3. package/dist/bin.d.ts +3 -0
  4. package/dist/bin.d.ts.map +1 -0
  5. package/dist/bin.js +12 -0
  6. package/dist/bin.js.map +1 -0
  7. package/dist/build-artifacts.d.ts +27 -0
  8. package/dist/build-artifacts.d.ts.map +1 -0
  9. package/dist/build-artifacts.js +169 -0
  10. package/dist/build-artifacts.js.map +1 -0
  11. package/dist/build-assets.d.ts +36 -0
  12. package/dist/build-assets.d.ts.map +1 -0
  13. package/dist/build-assets.js +224 -0
  14. package/dist/build-assets.js.map +1 -0
  15. package/dist/build.d.ts +31 -0
  16. package/dist/build.d.ts.map +1 -0
  17. package/dist/build.js +277 -0
  18. package/dist/build.js.map +1 -0
  19. package/dist/cli.d.ts +2 -0
  20. package/dist/cli.d.ts.map +1 -0
  21. package/dist/cli.js +801 -0
  22. package/dist/cli.js.map +1 -0
  23. package/dist/cloudflare/d1-http-executor.d.ts +30 -0
  24. package/dist/cloudflare/d1-http-executor.d.ts.map +1 -0
  25. package/dist/cloudflare/d1-http-executor.js +230 -0
  26. package/dist/cloudflare/d1-http-executor.js.map +1 -0
  27. package/dist/cloudflare/d1-http-response.d.ts +4 -0
  28. package/dist/cloudflare/d1-http-response.d.ts.map +1 -0
  29. package/dist/cloudflare/d1-http-response.js +113 -0
  30. package/dist/cloudflare/d1-http-response.js.map +1 -0
  31. package/dist/commands/admin.d.ts +47 -0
  32. package/dist/commands/admin.d.ts.map +1 -0
  33. package/dist/commands/admin.js +447 -0
  34. package/dist/commands/admin.js.map +1 -0
  35. package/dist/commands/built-manifest.d.ts +13 -0
  36. package/dist/commands/built-manifest.d.ts.map +1 -0
  37. package/dist/commands/built-manifest.js +197 -0
  38. package/dist/commands/built-manifest.js.map +1 -0
  39. package/dist/commands/cloudflare/api.d.ts +129 -0
  40. package/dist/commands/cloudflare/api.d.ts.map +1 -0
  41. package/dist/commands/cloudflare/api.js +480 -0
  42. package/dist/commands/cloudflare/api.js.map +1 -0
  43. package/dist/commands/cloudflare/deploy.d.ts +81 -0
  44. package/dist/commands/cloudflare/deploy.d.ts.map +1 -0
  45. package/dist/commands/cloudflare/deploy.js +1848 -0
  46. package/dist/commands/cloudflare/deploy.js.map +1 -0
  47. package/dist/commands/cloudflare/inventory.d.ts +243 -0
  48. package/dist/commands/cloudflare/inventory.d.ts.map +1 -0
  49. package/dist/commands/cloudflare/inventory.js +786 -0
  50. package/dist/commands/cloudflare/inventory.js.map +1 -0
  51. package/dist/commands/cloudflare/janitor.d.ts +10 -0
  52. package/dist/commands/cloudflare/janitor.d.ts.map +1 -0
  53. package/dist/commands/cloudflare/janitor.js +84 -0
  54. package/dist/commands/cloudflare/janitor.js.map +1 -0
  55. package/dist/commands/cloudflare/migrations-check.d.ts +5 -0
  56. package/dist/commands/cloudflare/migrations-check.d.ts.map +1 -0
  57. package/dist/commands/cloudflare/migrations-check.js +10 -0
  58. package/dist/commands/cloudflare/migrations-check.js.map +1 -0
  59. package/dist/commands/cloudflare/provision.d.ts +79 -0
  60. package/dist/commands/cloudflare/provision.d.ts.map +1 -0
  61. package/dist/commands/cloudflare/provision.js +961 -0
  62. package/dist/commands/cloudflare/provision.js.map +1 -0
  63. package/dist/commands/cloudflare/queue-consumer.d.ts +4 -0
  64. package/dist/commands/cloudflare/queue-consumer.d.ts.map +1 -0
  65. package/dist/commands/cloudflare/queue-consumer.js +57 -0
  66. package/dist/commands/cloudflare/queue-consumer.js.map +1 -0
  67. package/dist/commands/cloudflare/setup.d.ts +48 -0
  68. package/dist/commands/cloudflare/setup.d.ts.map +1 -0
  69. package/dist/commands/cloudflare/setup.js +836 -0
  70. package/dist/commands/cloudflare/setup.js.map +1 -0
  71. package/dist/commands/cloudflare/smoke.d.ts +29 -0
  72. package/dist/commands/cloudflare/smoke.d.ts.map +1 -0
  73. package/dist/commands/cloudflare/smoke.js +715 -0
  74. package/dist/commands/cloudflare/smoke.js.map +1 -0
  75. package/dist/commands/cloudflare/teardown.d.ts +62 -0
  76. package/dist/commands/cloudflare/teardown.d.ts.map +1 -0
  77. package/dist/commands/cloudflare/teardown.js +906 -0
  78. package/dist/commands/cloudflare/teardown.js.map +1 -0
  79. package/dist/commands/cloudflare/worker-settings.d.ts +4 -0
  80. package/dist/commands/cloudflare/worker-settings.d.ts.map +1 -0
  81. package/dist/commands/cloudflare/worker-settings.js +93 -0
  82. package/dist/commands/cloudflare/worker-settings.js.map +1 -0
  83. package/dist/commands/cloudflare.d.ts +55 -0
  84. package/dist/commands/cloudflare.d.ts.map +1 -0
  85. package/dist/commands/cloudflare.js +393 -0
  86. package/dist/commands/cloudflare.js.map +1 -0
  87. package/dist/commands/import-cutover.d.ts +5 -0
  88. package/dist/commands/import-cutover.d.ts.map +1 -0
  89. package/dist/commands/import-cutover.js +25 -0
  90. package/dist/commands/import-cutover.js.map +1 -0
  91. package/dist/commands/import-private-packagist.d.ts +38 -0
  92. package/dist/commands/import-private-packagist.d.ts.map +1 -0
  93. package/dist/commands/import-private-packagist.js +232 -0
  94. package/dist/commands/import-private-packagist.js.map +1 -0
  95. package/dist/commands/import-satis.d.ts +11 -0
  96. package/dist/commands/import-satis.d.ts.map +1 -0
  97. package/dist/commands/import-satis.js +112 -0
  98. package/dist/commands/import-satis.js.map +1 -0
  99. package/dist/commands/import.d.ts +22 -0
  100. package/dist/commands/import.d.ts.map +1 -0
  101. package/dist/commands/import.js +343 -0
  102. package/dist/commands/import.js.map +1 -0
  103. package/dist/commands/openapi-specs.d.ts +4 -0
  104. package/dist/commands/openapi-specs.d.ts.map +1 -0
  105. package/dist/commands/openapi-specs.js +94 -0
  106. package/dist/commands/openapi-specs.js.map +1 -0
  107. package/dist/commands/openapi.d.ts +12 -0
  108. package/dist/commands/openapi.d.ts.map +1 -0
  109. package/dist/commands/openapi.js +44 -0
  110. package/dist/commands/openapi.js.map +1 -0
  111. package/dist/commands/source.d.ts +8 -0
  112. package/dist/commands/source.d.ts.map +1 -0
  113. package/dist/commands/source.js +61 -0
  114. package/dist/commands/source.js.map +1 -0
  115. package/dist/commands/sqlite.d.ts +11 -0
  116. package/dist/commands/sqlite.d.ts.map +1 -0
  117. package/dist/commands/sqlite.js +76 -0
  118. package/dist/commands/sqlite.js.map +1 -0
  119. package/dist/import/client.d.ts +71 -0
  120. package/dist/import/client.d.ts.map +1 -0
  121. package/dist/import/client.js +348 -0
  122. package/dist/import/client.js.map +1 -0
  123. package/dist/import/credential-refs.d.ts +2 -0
  124. package/dist/import/credential-refs.d.ts.map +1 -0
  125. package/dist/import/credential-refs.js +22 -0
  126. package/dist/import/credential-refs.js.map +1 -0
  127. package/dist/import/cutover.d.ts +64 -0
  128. package/dist/import/cutover.d.ts.map +1 -0
  129. package/dist/import/cutover.js +544 -0
  130. package/dist/import/cutover.js.map +1 -0
  131. package/dist/import/private-packagist/api.d.ts +57 -0
  132. package/dist/import/private-packagist/api.d.ts.map +1 -0
  133. package/dist/import/private-packagist/api.js +246 -0
  134. package/dist/import/private-packagist/api.js.map +1 -0
  135. package/dist/import/private-packagist/dates.d.ts +30 -0
  136. package/dist/import/private-packagist/dates.d.ts.map +1 -0
  137. package/dist/import/private-packagist/dates.js +189 -0
  138. package/dist/import/private-packagist/dates.js.map +1 -0
  139. package/dist/import/private-packagist/map.d.ts +48 -0
  140. package/dist/import/private-packagist/map.d.ts.map +1 -0
  141. package/dist/import/private-packagist/map.js +162 -0
  142. package/dist/import/private-packagist/map.js.map +1 -0
  143. package/dist/import/private-packagist/plan.d.ts +61 -0
  144. package/dist/import/private-packagist/plan.d.ts.map +1 -0
  145. package/dist/import/private-packagist/plan.js +936 -0
  146. package/dist/import/private-packagist/plan.js.map +1 -0
  147. package/dist/import/private-packagist/snapshot.d.ts +31 -0
  148. package/dist/import/private-packagist/snapshot.d.ts.map +1 -0
  149. package/dist/import/private-packagist/snapshot.js +406 -0
  150. package/dist/import/private-packagist/snapshot.js.map +1 -0
  151. package/dist/import/satis.d.ts +93 -0
  152. package/dist/import/satis.d.ts.map +1 -0
  153. package/dist/import/satis.js +873 -0
  154. package/dist/import/satis.js.map +1 -0
  155. package/dist/index.d.ts +3 -2
  156. package/dist/index.d.ts.map +1 -1
  157. package/dist/index.js +3 -96
  158. package/dist/index.js.map +1 -1
  159. package/dist/manifest.d.ts +55 -0
  160. package/dist/manifest.d.ts.map +1 -0
  161. package/dist/manifest.js +125 -0
  162. package/dist/manifest.js.map +1 -0
  163. package/dist/migrations-collect.d.ts +6 -0
  164. package/dist/migrations-collect.d.ts.map +1 -0
  165. package/dist/migrations-collect.js +39 -0
  166. package/dist/migrations-collect.js.map +1 -0
  167. package/dist/prebuilt-expected.json +7 -0
  168. package/dist/prebuilt.d.ts +27 -0
  169. package/dist/prebuilt.d.ts.map +1 -0
  170. package/dist/prebuilt.js +391 -0
  171. package/dist/prebuilt.js.map +1 -0
  172. package/package.json +21 -25
  173. package/README.md +0 -29
  174. package/dist/__tests__/migrate.test.d.ts +0 -2
  175. package/dist/__tests__/migrate.test.d.ts.map +0 -1
  176. package/dist/__tests__/migrate.test.js +0 -116
  177. package/dist/__tests__/migrate.test.js.map +0 -1
  178. package/dist/migrate.d.ts +0 -40
  179. package/dist/migrate.d.ts.map +0 -1
  180. package/dist/migrate.js +0 -335
  181. package/dist/migrate.js.map +0 -1
@@ -0,0 +1,873 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { PROJECT_SLUG_PATTERN } from '@package-broker/core/authz/project-path';
3
+ import { PackageNameError, packageKey } from '@package-broker/core/catalogue/names';
4
+ import { MAX_PATTERN_BYTES } from '@package-broker/core/catalogue/patterns';
5
+ import { canonicalManifest, MAX_MANIFEST_BYTES, MAX_MANIFEST_ITEMS, parseManifest, planHash, } from '@package-broker/core/import/manifest';
6
+ import { isCanonicalUrl, sortConfigLists } from '@package-broker/core/import/source-config';
7
+ import { isCredentialReference as isReference } from './credential-refs.js';
8
+ export const DIAGNOSTIC_TEXT = {
9
+ invalid_satis_config: 'Invalid or unsupported Satis configuration; see the field path.',
10
+ invalid_repository_url: 'Use a canonical repository URL without credentials, query or fragment.',
11
+ repository_driver_required: 'Declare the Git driver and package name in the local repository map; remote probing is disabled.',
12
+ repository_package_required: 'Declare the Composer package name for this repository in the local repository map.',
13
+ unsupported_github_repository: 'Use one concrete GitHub repository and supply its package mapping.',
14
+ unsupported_driver_options: 'Only a concrete Git HTTPS repository is supported; remove API driver options.',
15
+ selection_required: 'Provide explicit package names or supported vendor filters for this Composer repository.',
16
+ inline_package_unsupported: 'Inline package definitions cannot be imported; publish them to a Composer repository first.',
17
+ local_repository_unsupported: 'Local path repositories cannot be imported; publish them to a reachable repository first.',
18
+ artifact_repository_unsupported: 'Artifact directories cannot be imported; publish their packages to a Composer repository first.',
19
+ repository_transport_unsupported: 'This importer supports HTTPS Git and Composer repositories only.',
20
+ package_filter_unsupported: 'Use a canonical package name or a single vendor/* filter.',
21
+ require_all_composer_unsupported: 'Enumerate the Composer packages or vendor filters before planning; require-all cannot be expanded offline.',
22
+ conflicting_selection: 'Choose require-all or explicit require filters.',
23
+ dependency_resolution_unsupported: 'Dependency expansion requires a resolver and cannot be represented by this import plan.',
24
+ dev_dependency_resolution_unsupported: 'Development dependency expansion is not supported by this import plan.',
25
+ only_dependencies_unsupported: 'A dependencies-only selection requires dependency resolution and is not supported.',
26
+ stability_filter_unsupported: "Set minimum-stability to 'dev' in a reviewed package-only configuration; this plan cannot preserve a stability filter.",
27
+ archive_policy_unsupported: 'Satis archive settings are not transferable; remove them from a reviewed package-only configuration and configure Broker storage separately.',
28
+ empty_selection: 'No packages are selected; no manifest was written.',
29
+ ambiguous_package_owner: 'Assign each package or vendor filter to one source in the local repository map.',
30
+ selection_coverage: 'Repository selections must cover the requested filters without adding packages.',
31
+ duplicate_repository: 'Each repository may be configured only once.',
32
+ source_name_collision: 'Two repositories produce the same source name.',
33
+ invalid_repository_map: 'Invalid repository map; see the field path.',
34
+ invalid_credential_map: 'Use a supported credential reference and explicit repository binding; inline material is forbidden.',
35
+ invalid_project: 'Use a valid project slug.',
36
+ invalid_baseline: 'The baseline is not a valid Satis import manifest.',
37
+ incomplete_baseline: 'Supply the full retained baseline manifest for this import.',
38
+ existing_source_change: 'This plan changes an existing source; review it through source administration before importing further additions.',
39
+ destructive_delta_unsupported: 'This change removes or replaces imported configuration; reconcile it in the UI before creating a new baseline.',
40
+ override_mode_unsupported: 'Override mode needs an ownership marker the import manifest cannot carry; use merge mode.',
41
+ single_run_limit_exceeded: 'This plan exceeds a single-run import limit. Queue-backed multi-part import requires the T669 follow-up; no manifest was written.',
42
+ manifest_invalid: 'The planned manifest was refused by the import manifest validator.',
43
+ };
44
+ export const WARNING_TEXT = {
45
+ version_constraint_ignored: 'The version constraint is not enforced; the broker serves all upstream versions of this package.',
46
+ blacklist_ignored: 'The blacklist entry is not enforced; the broker serves all upstream versions of this package.',
47
+ };
48
+ const GIT_KINDS = ['vcs', 'git', 'github', 'gitlab', 'bitbucket'];
49
+ const REFUSED_TYPES = {
50
+ package: 'inline_package_unsupported',
51
+ path: 'local_repository_unsupported',
52
+ artifact: 'artifact_repository_unsupported',
53
+ };
54
+ const PRESENTATION_KEYS = ['name', 'description', 'homepage', 'output-html'];
55
+ const ROOT_KEYS = [
56
+ 'repositories',
57
+ 'require',
58
+ 'require-all',
59
+ 'require-dependencies',
60
+ 'require-dev-dependencies',
61
+ 'blacklist',
62
+ 'only-dependencies',
63
+ 'minimum-stability',
64
+ 'archive',
65
+ ...PRESENTATION_KEYS,
66
+ ];
67
+ const CREDENTIAL_KINDS = ['http_basic', 'bearer', 'github_app'];
68
+ const SOURCE_CONFIG_LIST_LIMIT = 64;
69
+ const ATTACHMENT_PRECEDENCE = 1000;
70
+ const GITHUB_REPOSITORY = /^https:\/\/github\.com\/([A-Za-z0-9][A-Za-z0-9-]{0,38})\/([A-Za-z0-9._-]{1,100}?)(?:\.git)?\/?$/;
71
+ const VENDOR_FILTER = /^[a-z0-9][a-z0-9_.-]*\/\*$/;
72
+ const isObj = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
73
+ const own = (object, key) => Object.hasOwn(object, key) ? object[key] : undefined;
74
+ const sha = (...tuple) => createHash('sha256').update(JSON.stringify(tuple)).digest('hex');
75
+ const compare = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
76
+ const sorted = (values) => [...values].sort(compare);
77
+ function composerKey(name) {
78
+ try {
79
+ return packageKey(name, { ecosystem: 'composer' });
80
+ }
81
+ catch (error) {
82
+ if (error instanceof PackageNameError)
83
+ return null;
84
+ throw error;
85
+ }
86
+ }
87
+ function isVendorFilter(value) {
88
+ return VENDOR_FILTER.test(value) && composerKey(`${value.slice(0, -2)}/x`) !== null;
89
+ }
90
+ function covers(filter, name) {
91
+ return name.startsWith(filter.slice(0, -1));
92
+ }
93
+ function hasDuplicateKeys(text) {
94
+ const stack = [];
95
+ const quoted = /"(?:[^"\\]|\\.)*"/y;
96
+ let at = 0;
97
+ while (at < text.length) {
98
+ const char = text[at];
99
+ if (char === '"') {
100
+ quoted.lastIndex = at;
101
+ const match = quoted.exec(text);
102
+ if (match === null)
103
+ return false;
104
+ at += match[0].length;
105
+ const top = stack[stack.length - 1];
106
+ if (top) {
107
+ let next = at;
108
+ while (/\s/.test(text[next] ?? ''))
109
+ next += 1;
110
+ if (text[next] === ':') {
111
+ const key = JSON.parse(match[0]);
112
+ if (top.has(key))
113
+ return true;
114
+ top.add(key);
115
+ }
116
+ }
117
+ continue;
118
+ }
119
+ if (char === '{')
120
+ stack.push(new Set());
121
+ else if (char === '[')
122
+ stack.push(null);
123
+ else if (char === '}' || char === ']')
124
+ stack.pop();
125
+ at += 1;
126
+ }
127
+ return false;
128
+ }
129
+ function parseJson(text) {
130
+ try {
131
+ const value = JSON.parse(text);
132
+ return hasDuplicateKeys(text) ? { ok: false } : { ok: true, value };
133
+ }
134
+ catch {
135
+ return { ok: false };
136
+ }
137
+ }
138
+ function githubForm(url) {
139
+ const match = GITHUB_REPOSITORY.exec(url);
140
+ if (match === null)
141
+ return null;
142
+ const owner = (match[1] ?? '').toLowerCase();
143
+ const repo = (match[2] ?? '').toLowerCase();
144
+ return repo.startsWith('.') ? null : { owner, repo };
145
+ }
146
+ function identityOf(url) {
147
+ const form = githubForm(url);
148
+ return form === null ? url : `https://github.com/${form.owner}/${form.repo}`;
149
+ }
150
+ function classifyUrl(url) {
151
+ // eslint-disable-next-line no-control-regex
152
+ if (url === '' || /[\u0000- \u007f]/.test(url))
153
+ return 'invalid';
154
+ if (!/^https:\/\//.test(url))
155
+ return 'transport';
156
+ if (githubForm(url) !== null || isCanonicalUrl(url))
157
+ return 'ok';
158
+ return 'invalid';
159
+ }
160
+ const SOURCE_CONFIG_ENTRY_LENGTH_LIMIT = 200;
161
+ function listCap(list) {
162
+ if (!Array.isArray(list))
163
+ return null;
164
+ if (list.length > SOURCE_CONFIG_LIST_LIMIT)
165
+ return 'SOURCE_CONFIG_LIST_LIMIT';
166
+ const long = list.some((entry) => typeof entry === 'string' && entry.length > SOURCE_CONFIG_ENTRY_LENGTH_LIMIT);
167
+ return long ? 'SOURCE_CONFIG_ENTRY_LENGTH_LIMIT' : null;
168
+ }
169
+ export async function planSatisImport(input) {
170
+ const diagnostics = [];
171
+ const warnings = [];
172
+ const refuse = (file, code, pointer, cap) => {
173
+ diagnostics.push(cap === undefined ? { file, code, pointer } : { file, code, pointer, cap });
174
+ };
175
+ const fail = () => ({ ok: false, diagnostics, warnings });
176
+ if (input.mode === 'override') {
177
+ refuse('options', 'override_mode_unsupported', '/mode');
178
+ return fail();
179
+ }
180
+ if (input.project !== undefined && !PROJECT_SLUG_PATTERN.test(input.project)) {
181
+ refuse('options', 'invalid_project', '/project');
182
+ }
183
+ const satisJson = parseJson(input.satis);
184
+ if (!satisJson.ok || !isObj(satisJson.value)) {
185
+ refuse('satis', 'invalid_satis_config', '');
186
+ return fail();
187
+ }
188
+ const root = satisJson.value;
189
+ let ignoredPresentationKeys = 0;
190
+ let disabledDefaultRepositories = 0;
191
+ for (const key of Object.keys(root)) {
192
+ if (!ROOT_KEYS.includes(key))
193
+ refuse('satis', 'invalid_satis_config', '');
194
+ }
195
+ for (const key of PRESENTATION_KEYS) {
196
+ const value = own(root, key);
197
+ if (value === undefined)
198
+ continue;
199
+ if (typeof value === (key === 'output-html' ? 'boolean' : 'string'))
200
+ ignoredPresentationKeys += 1;
201
+ else
202
+ refuse('satis', 'invalid_satis_config', `/${key}`);
203
+ }
204
+ const flag = (key, code) => {
205
+ const value = own(root, key);
206
+ if (value === undefined || value === false)
207
+ return;
208
+ refuse('satis', value === true ? code : 'invalid_satis_config', `/${key}`);
209
+ };
210
+ const requireAll = (() => {
211
+ const value = own(root, 'require-all');
212
+ if (value === undefined)
213
+ return false;
214
+ if (typeof value !== 'boolean')
215
+ refuse('satis', 'invalid_satis_config', '/require-all');
216
+ return value === true;
217
+ })();
218
+ flag('require-dependencies', 'dependency_resolution_unsupported');
219
+ flag('require-dev-dependencies', 'dev_dependency_resolution_unsupported');
220
+ flag('only-dependencies', 'only_dependencies_unsupported');
221
+ if (own(root, 'minimum-stability') !== 'dev') {
222
+ refuse('satis', 'stability_filter_unsupported', '/minimum-stability');
223
+ }
224
+ if (own(root, 'archive') !== undefined)
225
+ refuse('satis', 'archive_policy_unsupported', '/archive');
226
+ const exactRoots = new Set();
227
+ const filterRoots = new Set();
228
+ const requireValue = own(root, 'require');
229
+ if (requireValue !== undefined) {
230
+ if (!isObj(requireValue))
231
+ refuse('satis', 'invalid_satis_config', '/require');
232
+ else {
233
+ for (const [rawName, constraint] of Object.entries(requireValue)) {
234
+ const name = rawName.includes('*') ? rawName : (composerKey(rawName) ?? '');
235
+ if (typeof constraint !== 'string')
236
+ refuse('satis', 'invalid_satis_config', '/require');
237
+ else if (isVendorFilter(name))
238
+ filterRoots.add(name);
239
+ else if (!name.includes('*') && name !== '')
240
+ exactRoots.add(name);
241
+ else {
242
+ refuse('satis', 'package_filter_unsupported', '/require');
243
+ continue;
244
+ }
245
+ if (typeof constraint === 'string' && constraint !== '*') {
246
+ warnings.push({ code: 'version_constraint_ignored', package: name, pointer: '/require' });
247
+ }
248
+ }
249
+ }
250
+ }
251
+ const blacklist = own(root, 'blacklist');
252
+ if (blacklist !== undefined) {
253
+ if (!isObj(blacklist))
254
+ refuse('satis', 'invalid_satis_config', '/blacklist');
255
+ else {
256
+ for (const [rawName, constraint] of Object.entries(blacklist)) {
257
+ if (typeof constraint !== 'string')
258
+ refuse('satis', 'invalid_satis_config', '/blacklist');
259
+ else {
260
+ const name = composerKey(rawName) ?? (isVendorFilter(rawName) ? rawName : '(unnamed)');
261
+ warnings.push({ code: 'blacklist_ignored', package: name, pointer: '/blacklist' });
262
+ }
263
+ }
264
+ }
265
+ }
266
+ if (requireAll && (exactRoots.size > 0 || filterRoots.size > 0)) {
267
+ refuse('satis', 'conflicting_selection', '/require-all');
268
+ }
269
+ const mapEntries = readRepositoryMap(input.repositoryMap, refuse);
270
+ const credentialEntries = readCredentials(input.credentials, refuse);
271
+ const repositories = own(root, 'repositories');
272
+ const repos = [];
273
+ const seen = new Map();
274
+ if (!Array.isArray(repositories))
275
+ refuse('satis', 'invalid_satis_config', '/repositories');
276
+ else {
277
+ repositories.forEach((entry, index) => {
278
+ const pointer = `/repositories/${index}`;
279
+ if (!isObj(entry))
280
+ return refuse('satis', 'invalid_satis_config', pointer);
281
+ const keys = Object.keys(entry);
282
+ if (keys.length === 1 && keys[0] === 'packagist.org' && entry['packagist.org'] === false) {
283
+ disabledDefaultRepositories += 1;
284
+ return;
285
+ }
286
+ const type = own(entry, 'type');
287
+ if (typeof type !== 'string')
288
+ return refuse('satis', 'invalid_satis_config', `${pointer}/type`);
289
+ if (Object.hasOwn(REFUSED_TYPES, type)) {
290
+ return refuse('satis', REFUSED_TYPES[type], pointer);
291
+ }
292
+ if (type !== 'composer' && !GIT_KINDS.includes(type)) {
293
+ return refuse('satis', 'invalid_satis_config', `${pointer}/type`);
294
+ }
295
+ const url = own(entry, 'url');
296
+ if (keys.some((key) => key !== 'type' && key !== 'url') || typeof url !== 'string') {
297
+ return refuse('satis', 'invalid_satis_config', pointer);
298
+ }
299
+ const urlClass = classifyUrl(url);
300
+ if (urlClass === 'transport') {
301
+ return refuse('satis', 'repository_transport_unsupported', `${pointer}/url`);
302
+ }
303
+ if (urlClass === 'invalid')
304
+ return refuse('satis', 'invalid_repository_url', `${pointer}/url`);
305
+ const kind = type;
306
+ const isGithubHost = /^https:\/\/github\.com(?:\/|$)/.test(url);
307
+ if (kind !== 'composer' && (kind === 'github' || isGithubHost) && githubForm(url) === null) {
308
+ return refuse('satis', 'unsupported_github_repository', `${pointer}/url`);
309
+ }
310
+ const identity = identityOf(url);
311
+ const previous = seen.get(identity);
312
+ if (previous !== undefined)
313
+ return refuse('satis', 'duplicate_repository', pointer);
314
+ seen.set(identity, index);
315
+ const hash = sha('satis-repository-v1', identity);
316
+ repos.push({
317
+ index,
318
+ kind,
319
+ url,
320
+ identity,
321
+ hash,
322
+ entry: mapEntries.byIdentity.get(identity),
323
+ credential: credentialEntries.byIdentity.get(identity),
324
+ });
325
+ });
326
+ }
327
+ for (const [identity, entry] of mapEntries.byIdentity) {
328
+ if (!seen.has(identity))
329
+ refuse('repository-map', 'invalid_repository_map', `/repositories/${entry.index}`);
330
+ }
331
+ for (const [identity, entry] of credentialEntries.byIdentity) {
332
+ if (!seen.has(identity))
333
+ refuse('credentials', 'invalid_credential_map', `/repositories/${entry.index}`);
334
+ }
335
+ const names = new Set();
336
+ for (const repo of repos) {
337
+ const name = `satis-${repo.hash.slice(0, 58)}`;
338
+ if (names.has(name))
339
+ refuse('satis', 'source_name_collision', `/repositories/${repo.index}`);
340
+ names.add(name);
341
+ }
342
+ const gitRepos = repos.filter((repo) => repo.kind !== 'composer');
343
+ const composerRepos = repos.filter((repo) => repo.kind === 'composer');
344
+ for (const repo of gitRepos)
345
+ checkGitRepo(repo, refuse);
346
+ for (const repo of composerRepos) {
347
+ const entry = repo.entry;
348
+ if (entry !== undefined &&
349
+ (entry.package !== undefined ||
350
+ entry.driver !== undefined ||
351
+ entry.installationId !== undefined)) {
352
+ refuse('repository-map', 'invalid_repository_map', `/repositories/${entry.index}`);
353
+ }
354
+ if (repo.credential?.kind === 'github_app') {
355
+ refuse('credentials', 'invalid_credential_map', `/repositories/${repo.credential.index}`);
356
+ }
357
+ if (composerRepos.length > 1 &&
358
+ entry?.packages === undefined &&
359
+ entry?.patterns === undefined) {
360
+ refuse('satis', 'selection_required', `/repositories/${repo.index}`);
361
+ }
362
+ }
363
+ if (requireAll && composerRepos.length > 0) {
364
+ refuse('satis', 'require_all_composer_unsupported', '/require-all');
365
+ }
366
+ if (!requireAll && exactRoots.size === 0 && filterRoots.size === 0 && diagnostics.length === 0) {
367
+ refuse('satis', 'empty_selection', '/require');
368
+ }
369
+ if (diagnostics.length > 0)
370
+ return fail();
371
+ const planned = selectSources({
372
+ gitRepos,
373
+ composerRepos,
374
+ exactRoots: sorted(exactRoots),
375
+ filterRoots: sorted(filterRoots),
376
+ requireAll,
377
+ refuse,
378
+ });
379
+ if (planned === null)
380
+ return fail();
381
+ if (planned.length === 0) {
382
+ refuse('satis', 'empty_selection', '/require');
383
+ return fail();
384
+ }
385
+ for (const source of planned) {
386
+ const config = source.item.config;
387
+ const pointer = `/repositories/${source.repo.index}`;
388
+ for (const list of [config.selection?.packages, config.selection?.patterns]) {
389
+ const cap = listCap(list);
390
+ if (cap !== null)
391
+ refuse('satis', 'single_run_limit_exceeded', pointer, cap);
392
+ }
393
+ if (source.names.some((pattern) => Buffer.byteLength(pattern) > MAX_PATTERN_BYTES)) {
394
+ refuse('satis', 'single_run_limit_exceeded', pointer, 'MAX_PATTERN_BYTES');
395
+ }
396
+ }
397
+ if (diagnostics.length > 0)
398
+ return fail();
399
+ const attachProject = (repo) => repo.entry?.project ?? input.project;
400
+ const itemsOut = [];
401
+ const summary = {
402
+ sources: [],
403
+ existingSources: [],
404
+ names: [],
405
+ attachments: [],
406
+ };
407
+ const baseline = input.baseline === undefined ? null : readBaseline(input.baseline, refuse);
408
+ if (diagnostics.length > 0)
409
+ return fail();
410
+ const baselineSources = baseline?.sources ?? new Map();
411
+ const baselineNames = baseline?.names ?? new Set();
412
+ const nameKey = (source, pattern) => `${source}\0${pattern}`;
413
+ const baselineAttachments = baseline?.attachments ?? new Set();
414
+ const plannedNames = new Set();
415
+ for (const source of planned) {
416
+ const name = source.item.name;
417
+ const ref = source.item.external_ref;
418
+ const known = baselineSources.get(name);
419
+ const sourceRef = known === undefined ? { ref } : { existing: { project: null, name } };
420
+ if (known === undefined) {
421
+ itemsOut.push(source.item);
422
+ summary.sources.push({
423
+ name,
424
+ kind: source.item.source_type,
425
+ target: describeTarget(source.item),
426
+ });
427
+ }
428
+ else {
429
+ if (known !== stable(source.item)) {
430
+ refuse('satis', 'existing_source_change', `/repositories/${source.repo.index}`);
431
+ continue;
432
+ }
433
+ summary.existingSources.push(name);
434
+ }
435
+ for (const pattern of source.names) {
436
+ plannedNames.add(nameKey(name, pattern));
437
+ if (known !== undefined) {
438
+ if (!baselineNames.has(nameKey(name, pattern))) {
439
+ refuse('satis', 'existing_source_change', `/repositories/${source.repo.index}`);
440
+ }
441
+ continue;
442
+ }
443
+ itemsOut.push({
444
+ type: 'name',
445
+ external_ref: `satis:name:${sha('satis-name-v1', pattern)}`,
446
+ source: sourceRef,
447
+ ecosystem: 'composer',
448
+ pattern,
449
+ });
450
+ summary.names.push(pattern);
451
+ }
452
+ const project = attachProject(source.repo);
453
+ if (project === undefined)
454
+ continue;
455
+ if (!PROJECT_SLUG_PATTERN.test(project)) {
456
+ refuse('repository-map', 'invalid_repository_map', `/repositories/${source.repo.entry?.index ?? 0}`);
457
+ continue;
458
+ }
459
+ if (baselineAttachments.has(`${project}\0${name}`))
460
+ continue;
461
+ itemsOut.push({
462
+ type: 'attachment',
463
+ external_ref: `satis:attachment:${sha('satis-attachment-v1', project, source.repo.hash)}`,
464
+ project: { existing: project },
465
+ source: sourceRef,
466
+ precedence: ATTACHMENT_PRECEDENCE,
467
+ });
468
+ summary.attachments.push({ project, source: name });
469
+ }
470
+ if (baseline !== null) {
471
+ for (const name of baselineSources.keys()) {
472
+ if (!planned.some((source) => source.item.name === name)) {
473
+ refuse('baseline', 'destructive_delta_unsupported', '');
474
+ }
475
+ }
476
+ for (const key of baselineNames) {
477
+ if (!plannedNames.has(key))
478
+ refuse('baseline', 'destructive_delta_unsupported', '');
479
+ }
480
+ }
481
+ if (diagnostics.length > 0)
482
+ return fail();
483
+ if (itemsOut.length > MAX_MANIFEST_ITEMS) {
484
+ refuse('satis', 'single_run_limit_exceeded', '', 'MAX_MANIFEST_ITEMS');
485
+ return fail();
486
+ }
487
+ const parsed = parseManifest({
488
+ version: 1,
489
+ source_system: 'satis',
490
+ items: itemsOut,
491
+ widening: [],
492
+ });
493
+ if (!parsed.ok) {
494
+ for (const detail of parsed.details)
495
+ refuse('plan', 'manifest_invalid', detail.path);
496
+ return fail();
497
+ }
498
+ const canonical = canonicalManifest(parsed.data);
499
+ if (Buffer.byteLength(canonical) > MAX_MANIFEST_BYTES) {
500
+ refuse('satis', 'single_run_limit_exceeded', '', 'MAX_MANIFEST_BYTES');
501
+ return fail();
502
+ }
503
+ const selectedIdentities = new Set(planned.map((source) => source.repo.identity));
504
+ return {
505
+ ok: true,
506
+ manifest: parsed.data,
507
+ canonical,
508
+ hash: await planHash(parsed.data),
509
+ warnings: sortedWarnings(warnings),
510
+ summary: {
511
+ sources: summary.sources.sort((a, b) => compare(a.name, b.name)),
512
+ existingSources: sorted(summary.existingSources),
513
+ names: sorted(summary.names),
514
+ attachments: summary.attachments.sort((a, b) => compare(a.project, b.project) || compare(a.source, b.source)),
515
+ unselectedRepositories: repos.filter((repo) => !selectedIdentities.has(repo.identity)).length,
516
+ ignoredPresentationKeys,
517
+ disabledDefaultRepositories,
518
+ noChanges: itemsOut.length === 0,
519
+ },
520
+ };
521
+ }
522
+ function sortedWarnings(warnings) {
523
+ const rank = { version_constraint_ignored: 0, blacklist_ignored: 1 };
524
+ return [...warnings].sort((a, b) => rank[a.code] - rank[b.code] || compare(a.package, b.package));
525
+ }
526
+ function describeTarget(item) {
527
+ const config = item.config;
528
+ return typeof item.url === 'string' ? item.url : (config.repository ?? '');
529
+ }
530
+ function sortKeys(value) {
531
+ if (Array.isArray(value))
532
+ return value.map(sortKeys);
533
+ if (typeof value !== 'object' || value === null)
534
+ return value;
535
+ return Object.fromEntries(Object.keys(value)
536
+ .sort(compare)
537
+ .map((key) => [key, sortKeys(value[key])]));
538
+ }
539
+ function stable(item) {
540
+ return JSON.stringify(sortKeys({
541
+ ecosystem: item.ecosystem,
542
+ source_type: item.source_type,
543
+ project: item.project,
544
+ url: item.url,
545
+ credential_ref: item.credential_ref,
546
+ config: sortConfigLists(item.config),
547
+ }));
548
+ }
549
+ function checkGitRepo(repo, refuse) {
550
+ const { entry, kind } = repo;
551
+ const pointer = `/repositories/${repo.index}`;
552
+ const driverOk = entry?.driver === 'git';
553
+ if (kind === 'vcs' && !driverOk)
554
+ return refuse('satis', 'repository_driver_required', pointer);
555
+ if (entry?.package === undefined)
556
+ return refuse('satis', 'repository_package_required', pointer);
557
+ if (entry.driver !== undefined && !driverOk) {
558
+ return refuse('satis', 'unsupported_driver_options', pointer);
559
+ }
560
+ if (entry.packages !== undefined || entry.patterns !== undefined || entry.distHosts.length > 0) {
561
+ refuse('repository-map', 'invalid_repository_map', `/repositories/${entry.index}`);
562
+ }
563
+ if (entry.installationId !== undefined && githubForm(repo.identity) === null) {
564
+ refuse('repository-map', 'invalid_repository_map', `/repositories/${entry.index}`);
565
+ }
566
+ const credential = repo.credential;
567
+ if (credential !== undefined &&
568
+ (credential.kind === 'github_app') !== (entry.installationId !== undefined)) {
569
+ refuse('credentials', 'invalid_credential_map', `/repositories/${credential.index}`);
570
+ }
571
+ if (entry.installationId !== undefined && credential === undefined) {
572
+ refuse('credentials', 'invalid_credential_map', '');
573
+ }
574
+ }
575
+ function selectSources(args) {
576
+ const { gitRepos, composerRepos, exactRoots, filterRoots, requireAll, refuse } = args;
577
+ const before = { failed: false };
578
+ const fail = (code, pointer, file = 'satis') => {
579
+ before.failed = true;
580
+ refuse(file, code, pointer);
581
+ };
582
+ const packageOf = (repo) => repo.entry?.package;
583
+ const byPackage = new Map();
584
+ for (const repo of gitRepos)
585
+ byPackage.set(packageOf(repo), [...(byPackage.get(packageOf(repo)) ?? []), repo]);
586
+ for (const owners of byPackage.values()) {
587
+ if (owners.length > 1)
588
+ fail('ambiguous_package_owner', '', 'repository-map');
589
+ }
590
+ const explicit = composerRepos.filter((repo) => repo.entry?.packages !== undefined || repo.entry?.patterns !== undefined);
591
+ const implicit = composerRepos.length === 1 && explicit.length === 0 ? composerRepos[0] : undefined;
592
+ const packagesOf = (repo) => repo.entry?.packages ?? [];
593
+ const patternsOf = (repo) => repo.entry?.patterns ?? [];
594
+ const claims = (repo, name) => packagesOf(repo).includes(name) || patternsOf(repo).some((filter) => covers(filter, name));
595
+ for (const repo of explicit) {
596
+ const pointer = `/repositories/${repo.entry?.index ?? 0}/selection`;
597
+ for (const name of packagesOf(repo)) {
598
+ const rootFilter = filterRoots.find((filter) => covers(filter, name));
599
+ if (!exactRoots.includes(name) && rootFilter === undefined) {
600
+ fail('selection_coverage', pointer, 'repository-map');
601
+ }
602
+ else if (rootFilter !== undefined && !patternsOf(repo).includes(rootFilter)) {
603
+ fail('ambiguous_package_owner', pointer, 'repository-map');
604
+ }
605
+ }
606
+ for (const filter of patternsOf(repo)) {
607
+ if (!filterRoots.includes(filter))
608
+ fail('selection_coverage', pointer, 'repository-map');
609
+ }
610
+ }
611
+ const gitMatches = (predicate) => gitRepos.filter((repo) => predicate(packageOf(repo)));
612
+ const selectedGit = new Set();
613
+ const implicitPackages = new Set();
614
+ const implicitFilters = new Set();
615
+ if (requireAll)
616
+ for (const repo of gitRepos)
617
+ selectedGit.add(repo);
618
+ for (const name of exactRoots) {
619
+ const git = gitMatches((candidate) => candidate === name);
620
+ const composer = explicit.filter((repo) => claims(repo, name));
621
+ if (git.length + composer.length > 1)
622
+ fail('ambiguous_package_owner', '/require');
623
+ else if (git.length === 1)
624
+ selectedGit.add(git[0]);
625
+ else if (composer.length === 0 && implicit === undefined)
626
+ fail('selection_coverage', '/require');
627
+ else if (composer.length === 0)
628
+ implicitPackages.add(name);
629
+ }
630
+ for (const filter of filterRoots) {
631
+ const git = gitMatches((candidate) => covers(filter, candidate));
632
+ const composer = explicit.filter((repo) => patternsOf(repo).includes(filter));
633
+ const foreign = explicit.filter((repo) => !patternsOf(repo).includes(filter) && packagesOf(repo).some((name) => covers(filter, name)));
634
+ if ((git.length > 0 && composer.length > 0) || composer.length > 1 || foreign.length > 0) {
635
+ fail('ambiguous_package_owner', '/require');
636
+ }
637
+ else if (git.length > 0)
638
+ for (const repo of git)
639
+ selectedGit.add(repo);
640
+ else if (composer.length === 0 && implicit === undefined)
641
+ fail('selection_coverage', '/require');
642
+ else if (composer.length === 0)
643
+ implicitFilters.add(filter);
644
+ }
645
+ if (before.failed)
646
+ return null;
647
+ const planned = [];
648
+ for (const repo of selectedGit)
649
+ planned.push(gitSource(repo));
650
+ for (const repo of composerRepos) {
651
+ const isImplicit = repo === implicit;
652
+ const filters = isImplicit ? [...implicitFilters] : patternsOf(repo);
653
+ const packages = (isImplicit ? [...implicitPackages] : packagesOf(repo)).filter((name) => !filters.some((filter) => covers(filter, name)));
654
+ if (packages.length === 0 && filters.length === 0)
655
+ continue;
656
+ planned.push(composerSource(repo, sorted(packages), sorted(filters)));
657
+ }
658
+ return planned;
659
+ }
660
+ function sourceBase(repo) {
661
+ return {
662
+ type: 'source',
663
+ external_ref: `satis:source:${repo.hash}`,
664
+ name: `satis-${repo.hash.slice(0, 58)}`,
665
+ ecosystem: 'composer',
666
+ project: null,
667
+ credential_ref: repo.credential?.ref ?? null,
668
+ };
669
+ }
670
+ function gitSource(repo) {
671
+ const packageName = repo.entry?.package;
672
+ const form = githubForm(repo.identity);
673
+ const app = repo.entry?.installationId;
674
+ const config = app !== undefined && form !== null
675
+ ? { provider: 'github', repository: `${form.owner}/${form.repo}`, installationId: app }
676
+ : {
677
+ provider: 'git',
678
+ repository: form === null ? repo.url : `${repo.identity}.git`,
679
+ installationId: null,
680
+ };
681
+ return {
682
+ repo,
683
+ names: [packageName],
684
+ item: {
685
+ ...sourceBase(repo),
686
+ source_type: 'vcs',
687
+ url: null,
688
+ config: { ...config, packageName, refs: { tags: true, branches: ['*'] } },
689
+ },
690
+ };
691
+ }
692
+ function composerSource(repo, packages, patterns) {
693
+ return {
694
+ repo,
695
+ names: [...packages, ...patterns],
696
+ item: {
697
+ ...sourceBase(repo),
698
+ source_type: 'composer_repository',
699
+ url: repo.url,
700
+ config: {
701
+ selection: { packages, patterns, expandPatterns: false },
702
+ distHosts: repo.entry?.distHosts ?? [],
703
+ mirrorsFallback: false,
704
+ },
705
+ },
706
+ };
707
+ }
708
+ function strictKeys(object, allowed) {
709
+ return Object.keys(object).every((key) => allowed.includes(key));
710
+ }
711
+ function stringList(value, accept) {
712
+ if (!Array.isArray(value))
713
+ return null;
714
+ const entries = [];
715
+ for (const entry of value) {
716
+ if (typeof entry !== 'string' || !accept(entry))
717
+ return null;
718
+ entries.push(entry);
719
+ }
720
+ return entries;
721
+ }
722
+ function readRepositoryMap(text, refuse) {
723
+ const byIdentity = new Map();
724
+ if (text === undefined)
725
+ return { byIdentity };
726
+ const bad = (pointer) => refuse('repository-map', 'invalid_repository_map', pointer);
727
+ const json = parseJson(text);
728
+ if (!json.ok || !isObj(json.value) || !strictKeys(json.value, ['version', 'repositories'])) {
729
+ bad('');
730
+ return { byIdentity };
731
+ }
732
+ const list = own(json.value, 'repositories');
733
+ if (own(json.value, 'version') !== 1 || !Array.isArray(list)) {
734
+ bad('');
735
+ return { byIdentity };
736
+ }
737
+ list.forEach((raw, index) => {
738
+ const pointer = `/repositories/${index}`;
739
+ if (!isObj(raw) ||
740
+ !strictKeys(raw, ['url', 'driver', 'package', 'github', 'selection', 'distHosts', 'project'])) {
741
+ return bad(pointer);
742
+ }
743
+ const url = own(raw, 'url');
744
+ const driver = own(raw, 'driver');
745
+ const packageName = own(raw, 'package');
746
+ const github = own(raw, 'github');
747
+ const selection = own(raw, 'selection');
748
+ const hosts = own(raw, 'distHosts');
749
+ const project = own(raw, 'project');
750
+ const normalizedPackage = typeof packageName === 'string' ? composerKey(packageName) : null;
751
+ const installation = isObj(github) && strictKeys(github, ['installationId'])
752
+ ? own(github, 'installationId')
753
+ : undefined;
754
+ const selectionObject = isObj(selection) && strictKeys(selection, ['packages', 'patterns']) ? selection : null;
755
+ const packages = selectionObject === null
756
+ ? null
757
+ : stringList(own(selectionObject, 'packages') ?? [], (name) => composerKey(name) === name);
758
+ const patterns = selectionObject === null
759
+ ? null
760
+ : stringList(own(selectionObject, 'patterns') ?? [], isVendorFilter);
761
+ const selectionLists = isObj(selection)
762
+ ? [own(selection, 'packages'), own(selection, 'patterns')]
763
+ : [];
764
+ for (const list of [hosts, ...selectionLists]) {
765
+ const cap = listCap(list);
766
+ if (cap !== null)
767
+ return refuse('repository-map', 'single_run_limit_exceeded', pointer, cap);
768
+ }
769
+ const distHosts = hosts === undefined ? [] : stringList(hosts, (host) => /^[a-z0-9.-]{1,253}$/.test(host));
770
+ if (typeof url !== 'string' ||
771
+ (driver !== undefined && typeof driver !== 'string') ||
772
+ (packageName !== undefined && normalizedPackage === null) ||
773
+ (github !== undefined &&
774
+ !(typeof installation === 'number' &&
775
+ Number.isSafeInteger(installation) &&
776
+ installation > 0)) ||
777
+ (selection !== undefined && (packages === null || patterns === null)) ||
778
+ distHosts === null ||
779
+ (project !== undefined &&
780
+ !(typeof project === 'string' && PROJECT_SLUG_PATTERN.test(project)))) {
781
+ return bad(pointer);
782
+ }
783
+ const identity = identityOf(url);
784
+ if (byIdentity.has(identity))
785
+ return bad(pointer);
786
+ byIdentity.set(identity, {
787
+ index,
788
+ identity,
789
+ driver: driver,
790
+ package: normalizedPackage ?? undefined,
791
+ installationId: typeof installation === 'number' ? installation : undefined,
792
+ packages: packages ?? undefined,
793
+ patterns: patterns ?? undefined,
794
+ distHosts,
795
+ project: project,
796
+ });
797
+ });
798
+ return { byIdentity };
799
+ }
800
+ function readCredentials(text, refuse) {
801
+ const byIdentity = new Map();
802
+ if (text === undefined)
803
+ return { byIdentity };
804
+ const bad = (pointer) => refuse('credentials', 'invalid_credential_map', pointer);
805
+ const json = parseJson(text);
806
+ if (!json.ok || !isObj(json.value) || !strictKeys(json.value, ['version', 'repositories'])) {
807
+ bad('');
808
+ return { byIdentity };
809
+ }
810
+ const list = own(json.value, 'repositories');
811
+ if (own(json.value, 'version') !== 1 || !Array.isArray(list)) {
812
+ bad('');
813
+ return { byIdentity };
814
+ }
815
+ list.forEach((raw, index) => {
816
+ const pointer = `/repositories/${index}`;
817
+ if (!isObj(raw) || !strictKeys(raw, ['url', 'kind', 'ref']))
818
+ return bad(pointer);
819
+ const url = own(raw, 'url');
820
+ const kind = own(raw, 'kind');
821
+ const ref = own(raw, 'ref');
822
+ if (typeof url !== 'string' ||
823
+ typeof kind !== 'string' ||
824
+ !CREDENTIAL_KINDS.includes(kind) ||
825
+ typeof ref !== 'string' ||
826
+ !isReference(ref)) {
827
+ return bad(pointer);
828
+ }
829
+ const identity = identityOf(url);
830
+ if (byIdentity.has(identity))
831
+ return bad(pointer);
832
+ byIdentity.set(identity, { index, kind, ref });
833
+ });
834
+ return { byIdentity };
835
+ }
836
+ function readBaseline(text, refuse) {
837
+ const json = parseJson(text);
838
+ const parsed = json.ok ? parseManifest(json.value) : null;
839
+ if (parsed === null || !parsed.ok || parsed.data.source_system !== 'satis') {
840
+ refuse('baseline', 'invalid_baseline', '');
841
+ return null;
842
+ }
843
+ const sources = new Map();
844
+ const names = new Set();
845
+ const attachments = new Set();
846
+ const sourceNames = new Map();
847
+ for (const item of parsed.data.items) {
848
+ if (item.type === 'source') {
849
+ sources.set(item.name, stable(item));
850
+ sourceNames.set(item.external_ref, item.name);
851
+ }
852
+ else if (item.type !== 'name' && item.type !== 'attachment') {
853
+ refuse('baseline', 'invalid_baseline', '');
854
+ return null;
855
+ }
856
+ }
857
+ for (const item of parsed.data.items) {
858
+ if (item.type !== 'name' && item.type !== 'attachment')
859
+ continue;
860
+ if (!('ref' in item.source)) {
861
+ refuse('baseline', 'incomplete_baseline', '');
862
+ return null;
863
+ }
864
+ if (item.type === 'name') {
865
+ names.add(`${sourceNames.get(item.source.ref) ?? ''}\0${item.pattern}`);
866
+ }
867
+ else if ('existing' in item.project) {
868
+ attachments.add(`${item.project.existing}\0${sourceNames.get(item.source.ref) ?? ''}`);
869
+ }
870
+ }
871
+ return { sources, names, attachments };
872
+ }
873
+ //# sourceMappingURL=satis.js.map