@teambit/schema 1.0.1079 → 1.0.1081

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.
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,547 @@
1
+ "use strict";
2
+
3
+ function _chai() {
4
+ const data = require("chai");
5
+ _chai = function () {
6
+ return data;
7
+ };
8
+ return data;
9
+ }
10
+ function _semanticsEntities() {
11
+ const data = require("@teambit/semantics.entities.semantic-schema");
12
+ _semanticsEntities = function () {
13
+ return data;
14
+ };
15
+ return data;
16
+ }
17
+ function _componentId() {
18
+ const data = require("@teambit/component-id");
19
+ _componentId = function () {
20
+ return data;
21
+ };
22
+ return data;
23
+ }
24
+ function _semanticsEntities2() {
25
+ const data = require("@teambit/semantics.entities.semantic-schema-diff");
26
+ _semanticsEntities2 = function () {
27
+ return data;
28
+ };
29
+ return data;
30
+ }
31
+ function _schemaMain() {
32
+ const data = require("./schema.main.runtime");
33
+ _schemaMain = function () {
34
+ return data;
35
+ };
36
+ return data;
37
+ }
38
+ const loc = {
39
+ filePath: 'index.ts',
40
+ line: 0,
41
+ character: 0
42
+ };
43
+ const compId = _componentId().ComponentID.fromString('org.scope/button');
44
+ function makeVar(name, type) {
45
+ return new (_semanticsEntities().VariableLikeSchema)(loc, name, `${name}: ${type}`, new (_semanticsEntities().KeywordTypeSchema)(loc, type), false);
46
+ }
47
+ function makeSchema(exports, internals = []) {
48
+ const internalModules = internals.length > 0 ? [new (_semanticsEntities().ModuleSchema)(loc, [], internals)] : [];
49
+ return new (_semanticsEntities().APISchema)(loc, new (_semanticsEntities().ModuleSchema)(loc, exports, []), internalModules, compId);
50
+ }
51
+ function makeAssessor() {
52
+ const assessor = new (_semanticsEntities2().ImpactAssessor)();
53
+ assessor.registerDefaultRules(_semanticsEntities2().DEFAULT_IMPACT_RULES);
54
+ return assessor;
55
+ }
56
+ describe('computeAPIDiff', () => {
57
+ describe('availability short-circuit', () => {
58
+ const unavailable = {
59
+ available: false,
60
+ reason: 'NOT_BUILT'
61
+ };
62
+ const available = {
63
+ available: true
64
+ };
65
+ it('should not fabricate a diff when the base schema is unavailable', () => {
66
+ const result = (0, _semanticsEntities2().computeAPIDiff)(makeSchema([]), makeSchema([makeVar('foo', 'string')]), makeAssessor(), {
67
+ base: unavailable,
68
+ compare: available
69
+ });
70
+ (0, _chai().expect)(result.status).to.equal('BASE_UNAVAILABLE');
71
+ (0, _chai().expect)(result.hasChanges).to.be.false;
72
+ (0, _chai().expect)(result.changes).to.have.length(0);
73
+ (0, _chai().expect)(result.base).to.deep.equal(unavailable);
74
+ (0, _chai().expect)(result.compare).to.deep.equal(available);
75
+ });
76
+ it('should not fabricate a diff when the compare schema is unavailable', () => {
77
+ const result = (0, _semanticsEntities2().computeAPIDiff)(makeSchema([makeVar('foo', 'string')]), makeSchema([]), makeAssessor(), {
78
+ base: available,
79
+ compare: unavailable
80
+ });
81
+ (0, _chai().expect)(result.status).to.equal('COMPARE_UNAVAILABLE');
82
+ (0, _chai().expect)(result.hasChanges).to.be.false;
83
+ (0, _chai().expect)(result.changes).to.have.length(0);
84
+ });
85
+ it('should report UNAVAILABLE when both schemas are unavailable', () => {
86
+ const result = (0, _semanticsEntities2().computeAPIDiff)(makeSchema([]), makeSchema([]), makeAssessor(), {
87
+ base: unavailable,
88
+ compare: {
89
+ available: false,
90
+ reason: 'NO_EXTRACTOR'
91
+ }
92
+ });
93
+ (0, _chai().expect)(result.status).to.equal('UNAVAILABLE');
94
+ (0, _chai().expect)(result.hasChanges).to.be.false;
95
+ (0, _chai().expect)(result.compare.reason).to.equal('NO_EXTRACTOR');
96
+ });
97
+ it('should default to available and report COMPUTED', () => {
98
+ const result = (0, _semanticsEntities2().computeAPIDiff)(makeSchema([]), makeSchema([makeVar('foo', 'string')]), makeAssessor());
99
+ (0, _chai().expect)(result.status).to.equal('COMPUTED');
100
+ (0, _chai().expect)(result.base.available).to.be.true;
101
+ (0, _chai().expect)(result.compare.available).to.be.true;
102
+ (0, _chai().expect)(result.hasChanges).to.be.true;
103
+ (0, _chai().expect)(result.added).to.equal(1);
104
+ });
105
+ });
106
+ describe('impact derivation', () => {
107
+ it('should derive impact from public changes only', () => {
108
+ // internal-only breaking change: internal var type narrowed string → number
109
+ const base = makeSchema([makeVar('Button', 'string')], [makeVar('helper', 'string')]);
110
+ const compare = makeSchema([makeVar('Button', 'string')], [makeVar('helper', 'number')]);
111
+ const result = (0, _semanticsEntities2().computeAPIDiff)(base, compare, makeAssessor());
112
+ (0, _chai().expect)(result.publicChanges).to.have.length(0);
113
+ (0, _chai().expect)(result.internalChanges).to.have.length(1);
114
+ (0, _chai().expect)(result.impact).to.equal('PATCH');
115
+ (0, _chai().expect)(result.internalImpact).to.equal('BREAKING');
116
+ (0, _chai().expect)(result.hasChanges).to.be.true;
117
+ });
118
+ it('should reflect public breaking changes in impact', () => {
119
+ const base = makeSchema([makeVar('Button', 'string')]);
120
+ const compare = makeSchema([]);
121
+ const result = (0, _semanticsEntities2().computeAPIDiff)(base, compare, makeAssessor());
122
+ (0, _chai().expect)(result.removed).to.equal(1);
123
+ (0, _chai().expect)(result.impact).to.equal('BREAKING');
124
+ (0, _chai().expect)(result.internalImpact).to.equal('PATCH');
125
+ });
126
+ });
127
+ });
128
+ describe('computeAPIDiff default/re-export aliases (extraction-inconsistency robustness)', () => {
129
+ // Repro of a real false positive: `class AttReact` + `export default AttReact`. Across two builds
130
+ // the default export was extracted inconsistently — an UnImplementedSchema one version, a
131
+ // `AttReact (default)` TypeRef alias the next — while only the docs changed. The diff must not
132
+ // report the default re-export alias as a newly added public API.
133
+ const classExport = () => new (_semanticsEntities().ExportSchema)(loc, 'AttReact', makeVar('AttReact', 'string'));
134
+ const defaultAsUnimplemented = () => new (_semanticsEntities().UnImplementedSchema)(loc, 'AttReact', 'Identifier');
135
+ const defaultAsTypeRefAlias = () => new (_semanticsEntities().ExportSchema)(loc, 'AttReact (default)', new (_semanticsEntities().TypeRefSchema)(loc, 'AttReact'), 'AttReact (default)');
136
+ it('should not report an added export when the default re-export alias changes representation', () => {
137
+ const base = makeSchema([classExport(), defaultAsUnimplemented()]);
138
+ const compare = makeSchema([classExport(), defaultAsTypeRefAlias()]);
139
+ const result = (0, _semanticsEntities2().computeAPIDiff)(base, compare, makeAssessor());
140
+ (0, _chai().expect)(result.hasChanges).to.equal(false);
141
+ (0, _chai().expect)(result.added).to.equal(0);
142
+ (0, _chai().expect)(result.publicChanges).to.have.lengthOf(0);
143
+ });
144
+ it('should drop a redundant default type-ref alias to an already-exported symbol', () => {
145
+ const base = makeSchema([classExport()]);
146
+ const compare = makeSchema([classExport(), defaultAsTypeRefAlias()]);
147
+ const result = (0, _semanticsEntities2().computeAPIDiff)(base, compare, makeAssessor());
148
+ (0, _chai().expect)(result.added).to.equal(0);
149
+ (0, _chai().expect)(result.hasChanges).to.equal(false);
150
+ });
151
+ it('should still report a genuinely new export alongside a redundant alias', () => {
152
+ const base = makeSchema([classExport()]);
153
+ const compare = makeSchema([classExport(), defaultAsTypeRefAlias(), makeVar('brandNew', 'string')]);
154
+ const result = (0, _semanticsEntities2().computeAPIDiff)(base, compare, makeAssessor());
155
+ (0, _chai().expect)(result.added).to.equal(1);
156
+ (0, _chai().expect)(result.publicChanges.map(c => c.exportName)).to.include('brandNew');
157
+ (0, _chai().expect)(result.publicChanges.map(c => c.exportName)).to.not.include('AttReact (default)');
158
+ });
159
+ });
160
+ describe('computeAPIDiff unresolved exports (extraction gaps)', () => {
161
+ const unresolved = name => new (_semanticsEntities().UnImplementedSchema)(loc, name, 'Identifier');
162
+ it('should not report a removal when the export is merely unresolved on the compare side', () => {
163
+ const base = makeSchema([makeVar('Foo', 'string')]);
164
+ const compare = makeSchema([unresolved('Foo')]);
165
+ const result = (0, _semanticsEntities2().computeAPIDiff)(base, compare, makeAssessor());
166
+ (0, _chai().expect)(result.removed).to.equal(0);
167
+ (0, _chai().expect)(result.hasChanges).to.equal(false);
168
+ (0, _chai().expect)(result.unresolvedExports).to.include('Foo');
169
+ });
170
+ it('should not report an addition when the export was merely unresolved on the base side', () => {
171
+ const base = makeSchema([unresolved('Foo')]);
172
+ const compare = makeSchema([makeVar('Foo', 'string')]);
173
+ const result = (0, _semanticsEntities2().computeAPIDiff)(base, compare, makeAssessor());
174
+ (0, _chai().expect)(result.added).to.equal(0);
175
+ (0, _chai().expect)(result.hasChanges).to.equal(false);
176
+ (0, _chai().expect)(result.unresolvedExports).to.include('Foo');
177
+ });
178
+ it('should surface an export unresolved on both sides without diffing it', () => {
179
+ const base = makeSchema([unresolved('Foo'), makeVar('Bar', 'string')]);
180
+ const compare = makeSchema([unresolved('Foo'), makeVar('Bar', 'string')]);
181
+ const result = (0, _semanticsEntities2().computeAPIDiff)(base, compare, makeAssessor());
182
+ (0, _chai().expect)(result.hasChanges).to.equal(false);
183
+ (0, _chai().expect)(result.unresolvedExports).to.include('Foo');
184
+ });
185
+ it('should NOT surface a name that is unresolved on one side but resolved elsewhere (real export wins)', () => {
186
+ // mirrors AttReact: a class export + an unresolved default that shares the class name.
187
+ const base = makeSchema([makeVar('AttReact', 'string'), unresolved('AttReact')]);
188
+ const compare = makeSchema([makeVar('AttReact', 'string')]);
189
+ const result = (0, _semanticsEntities2().computeAPIDiff)(base, compare, makeAssessor());
190
+ (0, _chai().expect)(result.hasChanges).to.equal(false);
191
+ (0, _chai().expect)(result.unresolvedExports).to.have.lengthOf(0);
192
+ });
193
+ });
194
+ describe('computeAPIDiff UnresolvedSchema placeholders (degraded extraction)', () => {
195
+ // Repro of a real false positive: one side's extraction degraded every re-export to an
196
+ // `UnresolvedSchema` placeholder (no structure, no doc) while the other side came from a healthy
197
+ // build. The diff reported every documented export as MODIFIED "documentation removed" labeled
198
+ // "Unresolved" instead of routing the name to `unresolvedExports`.
199
+ const docs = new (_semanticsEntities().DocSchema)(loc, '/** backend server interface. */', 'backend server interface.');
200
+ const documentedVar = name => new (_semanticsEntities().VariableLikeSchema)(loc, name, `${name}: string`, new (_semanticsEntities().KeywordTypeSchema)(loc, 'string'), false, docs);
201
+ it('should not diff a documented export against an UnresolvedSchema placeholder on the compare side', () => {
202
+ const base = makeSchema([documentedVar('BackendServer')]);
203
+ const compare = makeSchema([new (_semanticsEntities().UnresolvedSchema)(loc, 'BackendServer')]);
204
+ const result = (0, _semanticsEntities2().computeAPIDiff)(base, compare, makeAssessor());
205
+ (0, _chai().expect)(result.modified).to.equal(0);
206
+ (0, _chai().expect)(result.removed).to.equal(0);
207
+ (0, _chai().expect)(result.hasChanges).to.equal(false);
208
+ (0, _chai().expect)(result.unresolvedExports).to.include('BackendServer');
209
+ });
210
+ it('should not diff an UnresolvedSchema placeholder on the base side against a real export', () => {
211
+ const base = makeSchema([new (_semanticsEntities().UnresolvedSchema)(loc, 'BackendServer')]);
212
+ const compare = makeSchema([documentedVar('BackendServer')]);
213
+ const result = (0, _semanticsEntities2().computeAPIDiff)(base, compare, makeAssessor());
214
+ (0, _chai().expect)(result.modified).to.equal(0);
215
+ (0, _chai().expect)(result.added).to.equal(0);
216
+ (0, _chai().expect)(result.hasChanges).to.equal(false);
217
+ (0, _chai().expect)(result.unresolvedExports).to.include('BackendServer');
218
+ });
219
+ it('should handle an export-wrapped UnresolvedSchema like a bare one', () => {
220
+ const base = makeSchema([documentedVar('Middleware')]);
221
+ const compare = makeSchema([new (_semanticsEntities().ExportSchema)(loc, 'Middleware', new (_semanticsEntities().UnresolvedSchema)(loc, 'Middleware'))]);
222
+ const result = (0, _semanticsEntities2().computeAPIDiff)(base, compare, makeAssessor());
223
+ (0, _chai().expect)(result.hasChanges).to.equal(false);
224
+ (0, _chai().expect)(result.unresolvedExports).to.include('Middleware');
225
+ });
226
+ it('should suppress internal placeholder-vs-real pairs without surfacing them as unresolved exports', () => {
227
+ const base = makeSchema([], [documentedVar('helper')]);
228
+ const compare = makeSchema([], [new (_semanticsEntities().UnresolvedSchema)(loc, 'helper')]);
229
+ const result = (0, _semanticsEntities2().computeAPIDiff)(base, compare, makeAssessor());
230
+ (0, _chai().expect)(result.internalChanges).to.have.lengthOf(0);
231
+ (0, _chai().expect)(result.hasChanges).to.equal(false);
232
+ // internal extraction gaps are suppressed, not reported as unresolved *exports*
233
+ (0, _chai().expect)(result.unresolvedExports).to.have.lengthOf(0);
234
+ });
235
+ });
236
+ describe('computeAPIDiff visibility moves', () => {
237
+ it('should report a public→internal move as a public BREAKING change (drives impact, not internalImpact)', () => {
238
+ const base = makeSchema([makeVar('foo', 'string')], []);
239
+ const compare = makeSchema([], [makeVar('foo', 'string')]);
240
+ const result = (0, _semanticsEntities2().computeAPIDiff)(base, compare, makeAssessor());
241
+ (0, _chai().expect)(result.publicChanges).to.have.length(1);
242
+ (0, _chai().expect)(result.publicChanges[0].changes?.[0]?.changeKind).to.equal('visibility-public-to-internal');
243
+ (0, _chai().expect)(result.impact).to.equal('BREAKING');
244
+ });
245
+ });
246
+ describe('SchemaMain.getSchemaWithAvailability reason mapping', () => {
247
+ const succeedStatus = 'succeed';
248
+ const loggerStub = {
249
+ warn: () => {},
250
+ debug: () => {},
251
+ error: () => {},
252
+ info: () => {}
253
+ };
254
+ function makeComponent(buildStatus = 'pending') {
255
+ return {
256
+ id: _componentId().ComponentID.fromString('org.scope/button'),
257
+ buildStatus
258
+ };
259
+ }
260
+ function makeSchemaMain({
261
+ disabled = false,
262
+ workspace = {},
263
+ env = {},
264
+ artifacts = []
265
+ } = {}) {
266
+ const envs = {
267
+ getEnv: () => ({
268
+ env
269
+ })
270
+ };
271
+ const builder = {
272
+ getArtifactsVinylByAspectAndTaskName: async () => artifacts
273
+ };
274
+ const config = {
275
+ defaultParser: 'typescript',
276
+ disabled
277
+ };
278
+ return new (_schemaMain().SchemaMain)(undefined, undefined, envs, config, builder, workspace, loggerStub, new (_semanticsEntities2().ImpactAssessor)());
279
+ }
280
+ it('should report DISABLED when schema extraction is disabled by config', async () => {
281
+ const schemaMain = makeSchemaMain({
282
+ disabled: true
283
+ });
284
+ const {
285
+ availability
286
+ } = await schemaMain.getSchemaWithAvailability(makeComponent());
287
+ (0, _chai().expect)(availability).to.deep.equal({
288
+ available: false,
289
+ reason: 'DISABLED'
290
+ });
291
+ });
292
+ it('should report NO_EXTRACTOR when the env has no schema extractor', async () => {
293
+ const schemaMain = makeSchemaMain({
294
+ env: {
295
+ name: 'stub-env'
296
+ }
297
+ });
298
+ const {
299
+ availability
300
+ } = await schemaMain.getSchemaWithAvailability(makeComponent());
301
+ (0, _chai().expect)(availability).to.deep.equal({
302
+ available: false,
303
+ reason: 'NO_EXTRACTOR'
304
+ });
305
+ });
306
+ it('should report FAILED when extraction throws and no artifact exists', async () => {
307
+ const env = {
308
+ name: 'stub-env',
309
+ getSchemaExtractor: () => ({
310
+ extract: async () => {
311
+ throw new Error('boom');
312
+ },
313
+ dispose: () => {}
314
+ })
315
+ };
316
+ const schemaMain = makeSchemaMain({
317
+ env
318
+ });
319
+ const {
320
+ availability
321
+ } = await schemaMain.getSchemaWithAvailability(makeComponent());
322
+ (0, _chai().expect)(availability).to.deep.equal({
323
+ available: false,
324
+ reason: 'FAILED'
325
+ });
326
+ });
327
+ it('should report NOT_BUILT for a built/scope component without a schema artifact', async () => {
328
+ const schemaMain = makeSchemaMain({
329
+ workspace: undefined
330
+ });
331
+ const {
332
+ availability
333
+ } = await schemaMain.getSchemaWithAvailability(makeComponent(succeedStatus));
334
+ (0, _chai().expect)(availability).to.deep.equal({
335
+ available: false,
336
+ reason: 'NOT_BUILT'
337
+ });
338
+ });
339
+ it('should report available when a schema artifact exists', async () => {
340
+ const schemaJson = _semanticsEntities().APISchema.empty(_componentId().ComponentID.fromString('org.scope/button')).toObject();
341
+ const artifacts = [{
342
+ contents: Buffer.from(JSON.stringify(schemaJson))
343
+ }];
344
+ const schemaMain = makeSchemaMain({
345
+ workspace: undefined,
346
+ artifacts
347
+ });
348
+ const {
349
+ schema,
350
+ availability
351
+ } = await schemaMain.getSchemaWithAvailability(makeComponent(succeedStatus));
352
+ (0, _chai().expect)(availability).to.deep.equal({
353
+ available: true
354
+ });
355
+ (0, _chai().expect)(schema.module.exports).to.have.length(0);
356
+ });
357
+ it('should rethrow extraction errors when alwaysRunExtractor is set', async () => {
358
+ const env = {
359
+ name: 'stub-env',
360
+ getSchemaExtractor: () => ({
361
+ extract: async () => {
362
+ throw new Error('boom');
363
+ },
364
+ dispose: () => {}
365
+ })
366
+ };
367
+ const schemaMain = makeSchemaMain({
368
+ env
369
+ });
370
+ try {
371
+ await schemaMain.getSchemaWithAvailability(makeComponent(), false, true);
372
+ _chai().expect.fail('expected getSchemaWithAvailability to rethrow');
373
+ } catch (err) {
374
+ (0, _chai().expect)(err.message).to.equal('boom');
375
+ }
376
+ });
377
+ it('should flag a live-extracted schema so version-keyed consumers do not persist it', async () => {
378
+ const env = {
379
+ name: 'stub-env',
380
+ getSchemaExtractor: () => ({
381
+ extract: async () => makeSchema([makeVar('Foo', 'string')]),
382
+ dispose: () => {}
383
+ })
384
+ };
385
+ const schemaMain = makeSchemaMain({
386
+ env
387
+ });
388
+ const {
389
+ availability
390
+ } = await schemaMain.getSchemaWithAvailability(makeComponent());
391
+ (0, _chai().expect)(availability.available).to.equal(true);
392
+ (0, _chai().expect)(availability.live).to.equal(true);
393
+ });
394
+ });
395
+ describe('SchemaMain.computeAPIDiff schema sourcing', () => {
396
+ const loggerStub = {
397
+ warn: () => {},
398
+ debug: () => {},
399
+ error: () => {},
400
+ info: () => {}
401
+ };
402
+ function makeDiffSchemaMain({
403
+ workspace,
404
+ env,
405
+ artifacts
406
+ }) {
407
+ const envs = {
408
+ getEnv: () => ({
409
+ env
410
+ })
411
+ };
412
+ const builder = {
413
+ getArtifactsVinylByAspectAndTaskName: async () => artifacts
414
+ };
415
+ const config = {
416
+ defaultParser: 'typescript',
417
+ disabled: false
418
+ };
419
+ const impactRuleSlot = {
420
+ values: () => []
421
+ };
422
+ return new (_schemaMain().SchemaMain)(undefined, impactRuleSlot, envs, config, builder, workspace, loggerStub, new (_semanticsEntities2().ImpactAssessor)());
423
+ }
424
+ const makeComp = idStr => ({
425
+ id: _componentId().ComponentID.fromString(idStr),
426
+ buildStatus: 'pending'
427
+ });
428
+ const artifactOf = schema => [{
429
+ contents: Buffer.from(JSON.stringify(schema.toObject()))
430
+ }];
431
+ it('should read the built artifact — not live-extract — for an unmodified checkout, and memoize', async () => {
432
+ // repro: a checked-out CI-built snap reports buildStatus "pending", which used to force a
433
+ // tsserver extraction pass per component per diff (minutes on a cold lane-compare) whose
434
+ // degraded output was then cached under the immutable snap pair.
435
+ let extracted = 0;
436
+ const env = {
437
+ name: 'stub-env',
438
+ getSchemaExtractor: () => ({
439
+ extract: async () => {
440
+ extracted += 1;
441
+ return makeSchema([]);
442
+ },
443
+ dispose: () => {}
444
+ })
445
+ };
446
+ const workspace = {
447
+ getIdIfExist: id => id,
448
+ isModified: async () => false
449
+ };
450
+ const schemaMain = makeDiffSchemaMain({
451
+ workspace,
452
+ env,
453
+ artifacts: artifactOf(makeSchema([]))
454
+ });
455
+ const result = await schemaMain.computeAPIDiff(makeComp('org.scope/button@0.0.1'), makeComp('org.scope/button@0.0.2'));
456
+ (0, _chai().expect)(result?.status).to.equal('COMPUTED');
457
+ (0, _chai().expect)(extracted).to.equal(0);
458
+ (0, _chai().expect)(result?.base.live).to.equal(undefined);
459
+
460
+ // artifact-derived result is immutable even with buildStatus "pending" — memoized
461
+ const again = await schemaMain.computeAPIDiff(makeComp('org.scope/button@0.0.1'), makeComp('org.scope/button@0.0.2'));
462
+ (0, _chai().expect)(again).to.equal(result);
463
+ });
464
+ it('should live-extract for a modified checkout and never memoize that result', async () => {
465
+ let extracted = 0;
466
+ const env = {
467
+ name: 'stub-env',
468
+ getSchemaExtractor: () => ({
469
+ extract: async () => {
470
+ extracted += 1;
471
+ return makeSchema([makeVar('Foo', 'string')]);
472
+ },
473
+ dispose: () => {}
474
+ })
475
+ };
476
+ const workspace = {
477
+ getIdIfExist: id => id,
478
+ isModified: async () => true
479
+ };
480
+ const schemaMain = makeDiffSchemaMain({
481
+ workspace,
482
+ env,
483
+ artifacts: artifactOf(makeSchema([]))
484
+ });
485
+ const result = await schemaMain.computeAPIDiff(makeComp('org.scope/button@0.0.1'), makeComp('org.scope/button@0.0.2'));
486
+ (0, _chai().expect)(result?.status).to.equal('COMPUTED');
487
+ (0, _chai().expect)(extracted).to.equal(2); // both sides extracted live, artifact bypassed
488
+ (0, _chai().expect)(result?.base.live).to.equal(true);
489
+ (0, _chai().expect)(result?.compare.live).to.equal(true);
490
+ const again = await schemaMain.computeAPIDiff(makeComp('org.scope/button@0.0.1'), makeComp('org.scope/button@0.0.2'));
491
+ (0, _chai().expect)(again).to.not.equal(result); // live results are recomputed, never served from memo
492
+ (0, _chai().expect)(extracted).to.equal(4);
493
+ });
494
+ it('should read the artifact for a version that is not checked out in the workspace', async () => {
495
+ let extracted = 0;
496
+ const env = {
497
+ name: 'stub-env',
498
+ getSchemaExtractor: () => ({
499
+ extract: async () => {
500
+ extracted += 1;
501
+ return makeSchema([]);
502
+ },
503
+ dispose: () => {}
504
+ })
505
+ };
506
+ // checked out at a different version than either diff side
507
+ const workspace = {
508
+ getIdIfExist: id => id.changeVersion('9.9.9'),
509
+ isModified: async () => true
510
+ };
511
+ const schemaMain = makeDiffSchemaMain({
512
+ workspace,
513
+ env,
514
+ artifacts: artifactOf(makeSchema([]))
515
+ });
516
+ const result = await schemaMain.computeAPIDiff(makeComp('org.scope/button@0.0.1'), makeComp('org.scope/button@0.0.2'));
517
+ (0, _chai().expect)(result?.status).to.equal('COMPUTED');
518
+ (0, _chai().expect)(extracted).to.equal(0);
519
+ });
520
+ });
521
+ describe('computeAPIDiff self-referential returnType (degraded extraction)', () => {
522
+ // Repro of a real artifact: a component fn `Checkbox` extracted while React types were unresolved.
523
+ // quickinfo (and thus the stored signature) said `…): any`, but the extractor's definition-hunt
524
+ // resolved the function's own name and stored returnType = TypeRefSchema('Checkbox'). The diff
525
+ // must trust the signature in that case — showing "React.JSX.Element → Checkbox" while the
526
+ // signature snippet right above it shows "…): any" reads as nonsense.
527
+ const makeFn = (returnType, signatureReturn) => new (_semanticsEntities().FunctionLikeSchema)(loc, 'Checkbox', [], returnType, `function Checkbox(props: CheckboxProps): ${signatureReturn}`, []);
528
+ it('should prefer the signature return annotation when the returnType node is a self-reference', () => {
529
+ const base = makeSchema([makeFn(new (_semanticsEntities().InferenceTypeSchema)(loc, 'React.JSX.Element'), 'React.JSX.Element')]);
530
+ const compare = makeSchema([makeFn(new (_semanticsEntities().TypeRefSchema)(loc, 'Checkbox'), 'any')]);
531
+ const result = (0, _semanticsEntities2().computeAPIDiff)(base, compare, makeAssessor());
532
+ const fact = result.publicChanges[0]?.changes?.find(c => c.changeKind === 'return-type-changed');
533
+ (0, _chai().expect)(fact, 'expected a return-type-changed fact').to.exist;
534
+ (0, _chai().expect)(fact?.from).to.equal('React.JSX.Element');
535
+ (0, _chai().expect)(fact?.to).to.equal('any');
536
+ });
537
+ it('should keep the node rendering when a self-named return type agrees with the signature', () => {
538
+ // legit pattern: function merged with a same-named interface, genuinely returning `Checkbox`.
539
+ const base = makeSchema([makeFn(new (_semanticsEntities().InferenceTypeSchema)(loc, 'React.JSX.Element'), 'React.JSX.Element')]);
540
+ const compare = makeSchema([makeFn(new (_semanticsEntities().TypeRefSchema)(loc, 'Checkbox'), 'Checkbox')]);
541
+ const result = (0, _semanticsEntities2().computeAPIDiff)(base, compare, makeAssessor());
542
+ const fact = result.publicChanges[0]?.changes?.find(c => c.changeKind === 'return-type-changed');
543
+ (0, _chai().expect)(fact?.to).to.equal('Checkbox');
544
+ });
545
+ });
546
+
547
+ //# sourceMappingURL=api-diff-compute.spec.js.map