gitnexus 1.6.10-rc.164 → 1.6.10-rc.166
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/ingestion/languages/typescript/captures.js +26 -3
- package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +14 -4
- package/dist/core/ingestion/scope-resolution/contract/scope-resolver.js +14 -4
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.d.ts +5 -3
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +89 -3
- package/dist/mcp/local/local-backend.d.ts +1 -0
- package/dist/mcp/local/local-backend.js +19 -19
- package/dist/storage/parse-cache.js +7 -1
- package/package.json +1 -1
|
@@ -629,8 +629,12 @@ export function emitTsScopeCaptures(sourceText, filePath, cachedTree) {
|
|
|
629
629
|
* matching arms (member_expression for extends, nested_type_identifier plain +
|
|
630
630
|
* generic-wrapped for implements).
|
|
631
631
|
*
|
|
632
|
-
* `interface_declaration`
|
|
633
|
-
*
|
|
632
|
+
* `interface_declaration` and `abstract_class_declaration` heritage IS emitted
|
|
633
|
+
* (#2842 review; both were silently skipped before, so `interface B extends A`
|
|
634
|
+
* and `abstract class X implements I` produced no edge and every dispatch walk
|
|
635
|
+
* dead-ended on a bodiless declaration). They reach their bases by different
|
|
636
|
+
* shapes: an abstract class carries the same `class_heritage` child a concrete
|
|
637
|
+
* one does, while an interface's bases hang off `extends_type_clause` directly. The EXTENDS-vs-IMPLEMENTS split is decided downstream from the
|
|
634
638
|
* resolved target's symbol kind in `preEmitInheritanceEdges` (class-extends →
|
|
635
639
|
* EXTENDS, implements-interface / interface-target → IMPLEMENTS), so all bases
|
|
636
640
|
* are emitted with the same `inherits` kind here. The base lookup name is
|
|
@@ -647,7 +651,26 @@ function synthesizeTsInheritanceReferences(root, out) {
|
|
|
647
651
|
if (child !== null)
|
|
648
652
|
stack.push(child);
|
|
649
653
|
}
|
|
650
|
-
|
|
654
|
+
// `interface B extends A, C` hangs its bases off an `extends_type_clause`
|
|
655
|
+
// DIRECTLY on the interface — there is no `class_heritage` wrapper, so the
|
|
656
|
+
// class path below cannot reach them (#2842 review). The clause's `type`
|
|
657
|
+
// field is `multiple: true`, so `childForFieldName('type')` would silently
|
|
658
|
+
// return only `A` and drop `C`; iterate the named children instead.
|
|
659
|
+
if (node.type === 'interface_declaration') {
|
|
660
|
+
for (const child of node.namedChildren) {
|
|
661
|
+
if (child === null || child.type !== 'extends_type_clause')
|
|
662
|
+
continue;
|
|
663
|
+
for (const base of child.namedChildren) {
|
|
664
|
+
emitTsInheritanceBase(base, out);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
continue;
|
|
668
|
+
}
|
|
669
|
+
// `abstract class X implements I` carries an identical `class_heritage`
|
|
670
|
+
// child, so the existing body handles it once the node type is admitted.
|
|
671
|
+
// Omitting it severed the only link between an interface and the concrete
|
|
672
|
+
// classes below an abstract base — a whole subtree, not a leaf.
|
|
673
|
+
if (node.type !== 'class_declaration' && node.type !== 'abstract_class_declaration')
|
|
651
674
|
continue;
|
|
652
675
|
// Find the `class_heritage` child (holds extends / implements clauses).
|
|
653
676
|
let heritage = null;
|
|
@@ -114,13 +114,23 @@
|
|
|
114
114
|
* languages that set `resolveThisViaEnclosingClass === true`;
|
|
115
115
|
* it intercepts every bare-`this` call/read/write site ahead of
|
|
116
116
|
* Case 4 and does NOT emit the interface-dispatch fan-out that
|
|
117
|
-
* Cases 0 and 4
|
|
118
|
-
*
|
|
119
|
-
*
|
|
117
|
+
* Cases 0, 3b and 4 all perform (Case 0 gained it in #2829 and
|
|
118
|
+
* Case 3b in #2832, leaving 0.5 the only INSTANCE-receiver case
|
|
119
|
+
* that folds or walks to a receiver type without fanning out.
|
|
120
|
+
* Read that narrowly: Case 2 also walks an MRO and its binding
|
|
121
|
+
* admits `Interface`, but its receiver IS the type name, so the
|
|
122
|
+
* site is static dispatch and a fan-out would be wrong; Cases 3
|
|
123
|
+
* and 5 resolve by direct lookup rather than a fold or MRO walk,
|
|
124
|
+
* and no language is known to reach Case 3 with an Interface —
|
|
125
|
+
* every one that could strips the namespace qualifier first,
|
|
126
|
+
* sending it to Case 4), so enabling the toggle
|
|
127
|
+
* for a language changes that language's `this` dispatch
|
|
128
|
+
* semantics (see the toggle's doc below)
|
|
120
129
|
* 4. Case 1 namespace-receiver
|
|
121
130
|
* 5. Case 2 class-name receiver
|
|
122
131
|
* 6. Case 3 dotted typeBinding for namespace prefix
|
|
123
|
-
* 7. Case 3b chain-typebinding (compound resolver
|
|
132
|
+
* 7. Case 3b chain-typebinding (compound resolver + interface-dispatch
|
|
133
|
+
* fan-out on an Interface fold, #2832)
|
|
124
134
|
* 8. Case 4 simple typeBinding (MRO walk + findOwnedMember)
|
|
125
135
|
* Reordering or merging cases changes resolution semantics. The
|
|
126
136
|
* numbering is part of the contract — keep the comments.
|
|
@@ -114,13 +114,23 @@
|
|
|
114
114
|
* languages that set `resolveThisViaEnclosingClass === true`;
|
|
115
115
|
* it intercepts every bare-`this` call/read/write site ahead of
|
|
116
116
|
* Case 4 and does NOT emit the interface-dispatch fan-out that
|
|
117
|
-
* Cases 0 and 4
|
|
118
|
-
*
|
|
119
|
-
*
|
|
117
|
+
* Cases 0, 3b and 4 all perform (Case 0 gained it in #2829 and
|
|
118
|
+
* Case 3b in #2832, leaving 0.5 the only INSTANCE-receiver case
|
|
119
|
+
* that folds or walks to a receiver type without fanning out.
|
|
120
|
+
* Read that narrowly: Case 2 also walks an MRO and its binding
|
|
121
|
+
* admits `Interface`, but its receiver IS the type name, so the
|
|
122
|
+
* site is static dispatch and a fan-out would be wrong; Cases 3
|
|
123
|
+
* and 5 resolve by direct lookup rather than a fold or MRO walk,
|
|
124
|
+
* and no language is known to reach Case 3 with an Interface —
|
|
125
|
+
* every one that could strips the namespace qualifier first,
|
|
126
|
+
* sending it to Case 4), so enabling the toggle
|
|
127
|
+
* for a language changes that language's `this` dispatch
|
|
128
|
+
* semantics (see the toggle's doc below)
|
|
120
129
|
* 4. Case 1 namespace-receiver
|
|
121
130
|
* 5. Case 2 class-name receiver
|
|
122
131
|
* 6. Case 3 dotted typeBinding for namespace prefix
|
|
123
|
-
* 7. Case 3b chain-typebinding (compound resolver
|
|
132
|
+
* 7. Case 3b chain-typebinding (compound resolver + interface-dispatch
|
|
133
|
+
* fan-out on an Interface fold, #2832)
|
|
124
134
|
* 8. Case 4 simple typeBinding (MRO walk + findOwnedMember)
|
|
125
135
|
* Reordering or merging cases changes resolution semantics. The
|
|
126
136
|
* numbering is part of the contract — keep the comments.
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
* toggle unset skip this case entirely; their `this` sites fall
|
|
19
19
|
* through to Case 4 via the synthesized `this` typeBinding (which
|
|
20
20
|
* emits the interface-dispatch fan-out that this case does not —
|
|
21
|
-
* as
|
|
22
|
-
* case without it).
|
|
21
|
+
* as do Cases 0 since #2829 and 3b since #2832; Case 0.5 remains
|
|
22
|
+
* the only fold-or-walk case without it).
|
|
23
23
|
* 4. **Case 1 (namespace)** — receiver in `namespaceTargets` → exported def
|
|
24
24
|
* 5. **Case 2 (class-name / static receiver)** — receiver resolves to a
|
|
25
25
|
* class-like binding (Class/Interface/Struct/Record/Enum/Trait) → MRO
|
|
@@ -29,7 +29,9 @@
|
|
|
29
29
|
* 6. **Case 3 (dotted typeBinding for namespace prefix)** —
|
|
30
30
|
* `typeRef.rawName` like `models.User`
|
|
31
31
|
* 7. **Case 3b (chain-typebinding)** — `typeRef.rawName` has a dot
|
|
32
|
-
* but not a namespace prefix → compound resolver
|
|
32
|
+
* but not a namespace prefix → compound resolver. Also emits the
|
|
33
|
+
* interface-dispatch fan-out when the folded receiver type is an
|
|
34
|
+
* Interface (#2832) — same call Cases 0 and 4 make.
|
|
33
35
|
* 8. **Case 4 (simple typeBinding)** — `typeRef.rawName` has no dot →
|
|
34
36
|
* MRO walk + `findOwnedMember`
|
|
35
37
|
* 9. **Case 5 (value-receiver bridge)** — receiver is a `Const`/`Variable`
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
* toggle unset skip this case entirely; their `this` sites fall
|
|
19
19
|
* through to Case 4 via the synthesized `this` typeBinding (which
|
|
20
20
|
* emits the interface-dispatch fan-out that this case does not —
|
|
21
|
-
* as
|
|
22
|
-
* case without it).
|
|
21
|
+
* as do Cases 0 since #2829 and 3b since #2832; Case 0.5 remains
|
|
22
|
+
* the only fold-or-walk case without it).
|
|
23
23
|
* 4. **Case 1 (namespace)** — receiver in `namespaceTargets` → exported def
|
|
24
24
|
* 5. **Case 2 (class-name / static receiver)** — receiver resolves to a
|
|
25
25
|
* class-like binding (Class/Interface/Struct/Record/Enum/Trait) → MRO
|
|
@@ -29,7 +29,9 @@
|
|
|
29
29
|
* 6. **Case 3 (dotted typeBinding for namespace prefix)** —
|
|
30
30
|
* `typeRef.rawName` like `models.User`
|
|
31
31
|
* 7. **Case 3b (chain-typebinding)** — `typeRef.rawName` has a dot
|
|
32
|
-
* but not a namespace prefix → compound resolver
|
|
32
|
+
* but not a namespace prefix → compound resolver. Also emits the
|
|
33
|
+
* interface-dispatch fan-out when the folded receiver type is an
|
|
34
|
+
* Interface (#2832) — same call Cases 0 and 4 make.
|
|
33
35
|
* 8. **Case 4 (simple typeBinding)** — `typeRef.rawName` has no dot →
|
|
34
36
|
* MRO walk + `findOwnedMember`
|
|
35
37
|
* 9. **Case 5 (value-receiver bridge)** — receiver is a `Const`/`Variable`
|
|
@@ -306,6 +308,49 @@ export function emitReceiverBoundCalls(graph, scopes, parsedFiles, nodeLookup, h
|
|
|
306
308
|
return false;
|
|
307
309
|
return graph.getNode(graphId)?.properties.isAbstract === true;
|
|
308
310
|
};
|
|
311
|
+
/**
|
|
312
|
+
* Can an INSTANCE-typed receiver reach this member? A static member cannot be,
|
|
313
|
+
* ever — `class C implements I { static save() {} }` does not satisfy `I`
|
|
314
|
+
* (TypeScript rejects it outright as TS2420, "Property 'save' is missing"), so
|
|
315
|
+
* an edge to it from an `I`-typed receiver names a target no dispatch can
|
|
316
|
+
* produce. Every comparable tool draws the same line: tsserver partitions
|
|
317
|
+
* static from instance results, clangd gates on `isVirtual()` (C++ forbids
|
|
318
|
+
* virtual statics), jdtls filters abstract-or-static, and class-hierarchy
|
|
319
|
+
* analysis expands only VIRTUAL call sites — a static call already has exactly
|
|
320
|
+
* one target and needs no fan-out.
|
|
321
|
+
*
|
|
322
|
+
* Two sources answer this, and the order matters:
|
|
323
|
+
*
|
|
324
|
+
* 1. `provider.isStaticOnly` when the language declares it. It is the
|
|
325
|
+
* precise answer, because a language that needs the distinction defines
|
|
326
|
+
* it exactly — Kotlin marks only COMPANION-promoted defs, so a Kotlin
|
|
327
|
+
* `object Impl : Iface { override fun handle() }` is correctly kept: an
|
|
328
|
+
* `object` is a singleton INSTANCE and its members really are reachable
|
|
329
|
+
* through an `Iface`-typed receiver.
|
|
330
|
+
* 2. Otherwise the graph node's `isStatic`. For every language that does not
|
|
331
|
+
* declare the hook, that flag comes from the member's own modifier (or,
|
|
332
|
+
* for Ruby, from `singleton_class` — `def self.foo`, which is likewise
|
|
333
|
+
* unreachable through an instance), so it means what we need here.
|
|
334
|
+
*
|
|
335
|
+
* Getting that order wrong is a live regression, not a hypothetical: the
|
|
336
|
+
* method extractor derives `isStatic` from the OWNER type as well as the
|
|
337
|
+
* member (`method-extractors/generic.ts`, `staticOwnerTypes`), and the JVM
|
|
338
|
+
* config lists `object_declaration`. Reading the flag first would delete
|
|
339
|
+
* Kotlin object implementations from the fan-out. A language that needs
|
|
340
|
+
* precision declares the hook; that is the upgrade path.
|
|
341
|
+
*
|
|
342
|
+
* Unresolvable defs fail open (treated as reachable), matching
|
|
343
|
+
* `isDeclarationOnly` and the rest of this pass.
|
|
344
|
+
*/
|
|
345
|
+
const isUnreachableByInstanceDispatch = (def) => {
|
|
346
|
+
const staticOnly = provider.isStaticOnly;
|
|
347
|
+
if (staticOnly !== undefined)
|
|
348
|
+
return staticOnly(def) === true;
|
|
349
|
+
const graphId = resolveDefGraphId(def.filePath, def, nodeLookup);
|
|
350
|
+
if (graphId === undefined)
|
|
351
|
+
return false;
|
|
352
|
+
return graph.getNode(graphId)?.properties.isStatic === true;
|
|
353
|
+
};
|
|
309
354
|
/**
|
|
310
355
|
* Emit secondary CALLS edges with reason='interface-dispatch' when the primary
|
|
311
356
|
* receiver-typed edge targeted an Interface's method.
|
|
@@ -357,6 +402,10 @@ export function emitReceiverBoundCalls(graph, scopes, parsedFiles, nodeLookup, h
|
|
|
357
402
|
// implementation — keep descending past it rather than emitting to it.
|
|
358
403
|
if (isDeclarationOnly(implMember))
|
|
359
404
|
continue;
|
|
405
|
+
// Nor is a static member: no instance-typed receiver can reach one, so
|
|
406
|
+
// an edge to it is a target dispatch cannot produce (#2842 review).
|
|
407
|
+
if (isUnreachableByInstanceDispatch(implMember))
|
|
408
|
+
continue;
|
|
360
409
|
targets.push(implMember);
|
|
361
410
|
}
|
|
362
411
|
}
|
|
@@ -958,6 +1007,43 @@ export function emitReceiverBoundCalls(graph, scopes, parsedFiles, nodeLookup, h
|
|
|
958
1007
|
const ok = tryEmitEdge(graph, scopes, nodeLookup, site, memberDef, memberDef.filePath !== parsed.filePath ? 'import-resolved' : 'global', seen, 0.85, collapse, calleeCapture);
|
|
959
1008
|
if (ok)
|
|
960
1009
|
emitted++;
|
|
1010
|
+
// Interface dispatch, exactly as Cases 0 and 4 do it (#2832). Case
|
|
1011
|
+
// 3b folds a chain to a receiver type through the SAME
|
|
1012
|
+
// `resolveCompoundReceiverClass` call and the same MRO walk Case 0
|
|
1013
|
+
// uses, so when that fold lands on an Interface the primary edge
|
|
1014
|
+
// above names the interface's own bodiless DECLARATION and nothing
|
|
1015
|
+
// reaches the implementations.
|
|
1016
|
+
//
|
|
1017
|
+
// Leaving 3b out made the fan-out a property of how the receiver
|
|
1018
|
+
// was SPELLED rather than of what it resolved to: `d.repo.save()`
|
|
1019
|
+
// took Case 0 and fanned out, while binding the identical field to
|
|
1020
|
+
// a local first (`const r = d.repo; r.save()`) took Case 3b and
|
|
1021
|
+
// did not. #2829 closed that gap for Case 0 and left this half of
|
|
1022
|
+
// it open (#2832).
|
|
1023
|
+
//
|
|
1024
|
+
// `ownerDef` is the receiver's own folded type — matching Case 0's
|
|
1025
|
+
// `currentClass` and Case 4's `ownerDef` — NOT the owner of the
|
|
1026
|
+
// member the MRO walk settled on. That distinction matters: a
|
|
1027
|
+
// receiver that folds to a concrete class merely INHERITING an
|
|
1028
|
+
// interface method must not fan out, because its runtime type is
|
|
1029
|
+
// that class. `emitInterfaceDispatchFor` self-gates on
|
|
1030
|
+
// `ownerDef.type !== 'Interface'`, so this is inert for every
|
|
1031
|
+
// concrete receiver and needs no language check of its own.
|
|
1032
|
+
//
|
|
1033
|
+
// That gate is deliberately narrower than "the primary landed on
|
|
1034
|
+
// something bodiless": a chain folding to an ABSTRACT class also
|
|
1035
|
+
// dead-ends on a declaration-only member and does NOT fan out
|
|
1036
|
+
// here. Widening it to `|| isDeclarationOnly(memberDef)` would
|
|
1037
|
+
// cover that, but it changes Cases 0 and 4 identically and for
|
|
1038
|
+
// every language, so it is not #2832's to make.
|
|
1039
|
+
//
|
|
1040
|
+
// Confidence mirrors THIS case's own primary emit above — the 0.85
|
|
1041
|
+
// literal — so a site's dispatch edges never claim more certainty
|
|
1042
|
+
// than the edge they hang off. Case 4 passes a site.kind-dependent
|
|
1043
|
+
// value instead because ITS primary varies that way; Case 3b's
|
|
1044
|
+
// primary, like Case 0's, does not, so there is no 1.0 arm here to
|
|
1045
|
+
// mirror.
|
|
1046
|
+
emitted += emitInterfaceDispatchFor(ownerDef, memberName, memberDef, site, 0.85, calleeCapture);
|
|
961
1047
|
// Always mark handled when the site was resolved, even
|
|
962
1048
|
// if the edge was deduplicated (collapse mode), so
|
|
963
1049
|
// `emitReferencesViaLookup` doesn't re-emit from the
|
|
@@ -155,6 +155,7 @@ interface RepoHandle {
|
|
|
155
155
|
* and cannot be changed mid-process.
|
|
156
156
|
*/
|
|
157
157
|
export declare function resolveWorktreeCwd(repoPath: string, launchCwd: string): string;
|
|
158
|
+
export declare function buildDetectChangesDiffArgs(scope: string, baseRef?: string): string[] | null;
|
|
158
159
|
/**
|
|
159
160
|
* Length of the path-derived suffix appended to a colliding repo id.
|
|
160
161
|
* Exported so tests can pin the suffix shape without re-deriving the
|
|
@@ -544,6 +544,20 @@ export function resolveWorktreeCwd(repoPath, launchCwd) {
|
|
|
544
544
|
}
|
|
545
545
|
return repoPath;
|
|
546
546
|
}
|
|
547
|
+
export function buildDetectChangesDiffArgs(scope, baseRef) {
|
|
548
|
+
const args = ['diff', '--ignore-cr-at-eol'];
|
|
549
|
+
switch (scope) {
|
|
550
|
+
case 'staged':
|
|
551
|
+
return [...args, '--staged', '-U0'];
|
|
552
|
+
case 'all':
|
|
553
|
+
return [...args, 'HEAD', '-U0'];
|
|
554
|
+
case 'compare':
|
|
555
|
+
return baseRef ? [...args, baseRef, '-U0'] : null;
|
|
556
|
+
case 'unstaged':
|
|
557
|
+
default:
|
|
558
|
+
return [...args, '-U0'];
|
|
559
|
+
}
|
|
560
|
+
}
|
|
547
561
|
/**
|
|
548
562
|
* Length of the path-derived suffix appended to a colliding repo id.
|
|
549
563
|
* Exported so tests can pin the suffix shape without re-deriving the
|
|
@@ -3942,25 +3956,11 @@ export class LocalBackend {
|
|
|
3942
3956
|
await this.ensureInitialized(repo);
|
|
3943
3957
|
const scope = params.scope || 'unstaged';
|
|
3944
3958
|
const { execFileSync } = await import('child_process');
|
|
3945
|
-
//
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
break;
|
|
3951
|
-
case 'all':
|
|
3952
|
-
diffArgs = ['diff', 'HEAD', '-U0'];
|
|
3953
|
-
break;
|
|
3954
|
-
case 'compare':
|
|
3955
|
-
if (!params.base_ref)
|
|
3956
|
-
return { error: 'base_ref is required for "compare" scope' };
|
|
3957
|
-
diffArgs = ['diff', params.base_ref, '-U0'];
|
|
3958
|
-
break;
|
|
3959
|
-
case 'unstaged':
|
|
3960
|
-
default:
|
|
3961
|
-
diffArgs = ['diff', '-U0'];
|
|
3962
|
-
break;
|
|
3963
|
-
}
|
|
3959
|
+
// Ignore CR-only EOL differences, while preserving meaningful whitespace changes.
|
|
3960
|
+
// execFileSync receives an argv array, so refs never pass through a shell.
|
|
3961
|
+
const diffArgs = buildDetectChangesDiffArgs(scope, params.base_ref);
|
|
3962
|
+
if (!diffArgs)
|
|
3963
|
+
return { error: 'base_ref is required for "compare" scope' };
|
|
3964
3964
|
let diffOutput;
|
|
3965
3965
|
try {
|
|
3966
3966
|
// Resolve the cwd for git diff.
|
|
@@ -219,7 +219,13 @@ import { fileURLToPath } from 'url';
|
|
|
219
219
|
// value is unchanged by the rebase — the reason it was chosen is simply now
|
|
220
220
|
// visible in the history above. RE-CHECK AGAINST origin/main IMMEDIATELY
|
|
221
221
|
// BEFORE MERGING; this file records eight prior collisions, two EXACT.
|
|
222
|
-
|
|
222
|
+
// Moved 43 -> 44 for #2842's TypeScript heritage capture, which now emits
|
|
223
|
+
// `@reference.inherits` for `interface_declaration` and
|
|
224
|
+
// `abstract_class_declaration`. That is PARSE-TIME emission, so a v43 warm
|
|
225
|
+
// cache would serve entries that are missing those matches entirely — the
|
|
226
|
+
// exact failure a bump exists to prevent. Verified against origin/main at
|
|
227
|
+
// a857f4c5a, which is still on 43, so 44 is free. RE-CHECK BEFORE MERGE.
|
|
228
|
+
const SCHEMA_BUMP = 44;
|
|
223
229
|
const GITNEXUS_PKG_VERSION = (() => {
|
|
224
230
|
try {
|
|
225
231
|
// package.json sits at gitnexus/package.json — two levels up from
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gitnexus",
|
|
3
|
-
"version": "1.6.10-rc.
|
|
3
|
+
"version": "1.6.10-rc.166",
|
|
4
4
|
"description": "Graph-powered code intelligence for AI agents. Index any codebase, query via MCP or CLI.",
|
|
5
5
|
"author": "Abhigyan Patwari",
|
|
6
6
|
"license": "PolyForm-Noncommercial-1.0.0",
|