@neocompose/cli 0.31.4 → 0.31.5

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/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.31.5] - 2026-08-15
4
+
5
+ ### Fixed
6
+
7
+ - Resolve callable members through generic class and interface constraints, so
8
+ an open generic value can call guaranteed APIs such as `Class.Clone()`.
9
+
3
10
  ## [0.31.4] - 2026-08-14
4
11
 
5
12
  ### Fixed
package/dist/neo.mjs CHANGED
@@ -1832,6 +1832,21 @@ var init_generated_csharp_identifiers = __esm({
1832
1832
  });
1833
1833
 
1834
1834
  // ../packages/neoscript-language/src/project.ts
1835
+ function neoScriptMemberLookupType(type) {
1836
+ if (type.kind !== "typeParameter") return type;
1837
+ const visited = /* @__PURE__ */ new Set();
1838
+ let current = type;
1839
+ let nullable = type.nullable === true;
1840
+ while (current.kind === "typeParameter") {
1841
+ if (visited.has(current.id) || current.constraint === void 0) {
1842
+ return type;
1843
+ }
1844
+ visited.add(current.id);
1845
+ nullable ||= current.constraint.nullable === true;
1846
+ current = current.constraint;
1847
+ }
1848
+ return nullable && current.nullable !== true ? { ...current, nullable: true } : current;
1849
+ }
1835
1850
  function neoScriptParameterIsOmittable(parameter4) {
1836
1851
  return parameter4.defaultValue !== void 0;
1837
1852
  }
@@ -3160,7 +3175,8 @@ function completionItemsForResolution(snapshot, resolved, word) {
3160
3175
  (member) => member.static === true && isMemberCompletionAccessible(snapshot, staticType, member)
3161
3176
  ).map((member) => symbolCompletion(member, word, snapshot));
3162
3177
  }
3163
- const owner = resolved.type.kind === "named" ? snapshot.project.typeById.get(resolved.type.typeId) : void 0;
3178
+ const memberType2 = neoScriptMemberLookupType(resolved.type);
3179
+ const owner = memberType2.kind === "named" ? snapshot.project.typeById.get(memberType2.typeId) : void 0;
3164
3180
  return membersForType(snapshot, resolved.type).filter(
3165
3181
  (symbol) => owner === void 0 || isMemberCompletionAccessible(snapshot, owner, symbol)
3166
3182
  ).map((symbol) => symbolCompletion(symbol, word, snapshot));
@@ -3174,6 +3190,17 @@ function isMemberCompletionAccessible(snapshot, owner, symbol) {
3174
3190
  }) === null;
3175
3191
  }
3176
3192
  function membersForType(snapshot, type) {
3193
+ const memberType2 = neoScriptMemberLookupType(type);
3194
+ if (memberType2 !== type) {
3195
+ const members = [...membersForType(snapshot, memberType2)];
3196
+ if (type.kind !== "typeParameter" || type.nullable === true) return members;
3197
+ const named = memberType2.kind === "named" ? snapshot.project.typeById.get(memberType2.typeId) : void 0;
3198
+ if (named?.kind !== "class") return members;
3199
+ const cloneId = `builtin:${named.id}:Clone`;
3200
+ return members.map(
3201
+ (member) => member.id === cloneId ? cloneSymbol(named, type) : member
3202
+ );
3203
+ }
3177
3204
  if (type.kind === "named") {
3178
3205
  const named = snapshot.project.typeById.get(type.typeId);
3179
3206
  if (!named) return [];
@@ -3950,13 +3977,13 @@ function stringContainsSymbol() {
3950
3977
  "Returns true when the string contains the substring."
3951
3978
  );
3952
3979
  }
3953
- function cloneSymbol(type) {
3980
+ function cloneSymbol(type, returnType = { kind: "named", typeId: type.id }) {
3954
3981
  return {
3955
3982
  id: `builtin:${type.id}:Clone`,
3956
3983
  name: "Clone",
3957
3984
  kind: "method",
3958
- type: { kind: "named", typeId: type.id },
3959
- returnType: { kind: "named", typeId: type.id },
3985
+ type: returnType,
3986
+ returnType,
3960
3987
  parameters: [],
3961
3988
  documentation: `Deep-copy this ${type.name} into a new parentless writable value.`
3962
3989
  };
@@ -11871,11 +11898,12 @@ var init_strict_resolver = __esm({
11871
11898
  pos
11872
11899
  );
11873
11900
  }
11874
- if (receiver.type.kind === "named") {
11875
- const type = this.project.typeById.get(receiver.type.typeId);
11901
+ const memberReceiverType = neoScriptMemberLookupType(receiver.type);
11902
+ if (memberReceiverType.kind === "named") {
11903
+ const type = this.project.typeById.get(memberReceiverType.typeId);
11876
11904
  if (!type) {
11877
11905
  throw new CompileError(
11878
- `Type ${receiver.type.typeId} was not found in the project.`,
11906
+ `Type ${memberReceiverType.typeId} was not found in the project.`,
11879
11907
  pos
11880
11908
  );
11881
11909
  }
@@ -11909,7 +11937,7 @@ var init_strict_resolver = __esm({
11909
11937
  }
11910
11938
  const memberType2 = substituteTypeParameters(
11911
11939
  member.type,
11912
- receiver.type,
11940
+ memberReceiverType,
11913
11941
  type
11914
11942
  );
11915
11943
  if (member.computed === true) {
@@ -12350,8 +12378,9 @@ var init_strict_resolver = __esm({
12350
12378
  pos
12351
12379
  );
12352
12380
  }
12353
- if (receiver.type.kind === "named") {
12354
- const type = this.project.typeById.get(receiver.type.typeId);
12381
+ const memberReceiverType = neoScriptMemberLookupType(receiver.type);
12382
+ if (memberReceiverType.kind === "named") {
12383
+ const type = this.project.typeById.get(memberReceiverType.typeId);
12355
12384
  const member = type?.members.find(
12356
12385
  (candidate) => (candidate.kind === "function" || candidate.kind === "method") && candidate.name === callee.name
12357
12386
  );
@@ -12365,7 +12394,10 @@ var init_strict_resolver = __esm({
12365
12394
  }
12366
12395
  return this.resolveSymbolCall(
12367
12396
  member,
12368
- { kind: "instance", expression: receiver },
12397
+ {
12398
+ kind: "instance",
12399
+ expression: memberReceiverType === receiver.type ? receiver : { ...receiver, type: memberReceiverType }
12400
+ },
12369
12401
  argumentsList2,
12370
12402
  scope,
12371
12403
  pos,
@@ -12385,7 +12417,7 @@ var init_strict_resolver = __esm({
12385
12417
  pos
12386
12418
  );
12387
12419
  }
12388
- const schemaClass2 = toWireType(receiver.type, this.project);
12420
+ const schemaClass2 = toWireType(memberReceiverType, this.project);
12389
12421
  return {
12390
12422
  pointer: {
12391
12423
  type: "function" /* Function */,
@@ -110651,7 +110683,7 @@ var init_registry2 = __esm({
110651
110683
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
110652
110684
  formatVersion: 3,
110653
110685
  contractVersion: "3.12",
110654
- cliVersion: "0.31.4",
110686
+ cliVersion: "0.31.5",
110655
110687
  projectFileUploadBatchSize: 32,
110656
110688
  documentRecords: {
110657
110689
  member: {
@@ -117246,7 +117278,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
117246
117278
  async function main() {
117247
117279
  const args = parseArgs(process.argv.slice(2));
117248
117280
  if (args.command === "--version") {
117249
- console.log("0.31.4");
117281
+ console.log("0.31.5");
117250
117282
  return;
117251
117283
  }
117252
117284
  if (args.command === null || args.command === "help" || args.command === "--help" || args.command === "-h") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neocompose/cli",
3
- "version": "0.31.4",
3
+ "version": "0.31.5",
4
4
  "description": "Neo Compose native project-source CLI with bidirectional sync.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -9,7 +9,7 @@ description: >-
9
9
  `@neocompose/cli` or `node cli/bin/neo.mjs` in the neo-compose repository.
10
10
  ---
11
11
 
12
- <!-- reviewed-through-cli: 0.31.4 -->
12
+ <!-- reviewed-through-cli: 0.31.5 -->
13
13
 
14
14
  # Neo Compose CLI
15
15
 
@@ -83,7 +83,7 @@ wrappers.
83
83
  The marker near the top of `SKILL.md` must exactly match the package version:
84
84
 
85
85
  ```html
86
- <!-- reviewed-through-cli: 0.31.4 -->
86
+ <!-- reviewed-through-cli: 0.31.5 -->
87
87
  ```
88
88
 
89
89
  The quoted version above is checked too, so this instruction cannot go stale