@neocompose/cli 0.7.0 → 0.7.2
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 +15 -0
- package/dist/neo.mjs +107 -32
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,21 @@
|
|
|
7
7
|
- Document the final relation-only world layer-link contract and keep the
|
|
8
8
|
format-4 Hello World corpus free of the retired value-side binding field.
|
|
9
9
|
|
|
10
|
+
## [0.7.2] - 2026-07-22
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Preserve declaration-local `isReadOnly` on every override when an entire
|
|
15
|
+
inheritance chain converts to read-only in one source push.
|
|
16
|
+
|
|
17
|
+
## [0.7.1] - 2026-07-22
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- Support `abstract readonly` getter contracts and concrete `override readonly`
|
|
22
|
+
implementations, including inherited Immutable storage without a repeated
|
|
23
|
+
storage decorator.
|
|
24
|
+
|
|
10
25
|
## [0.7.0] - 2026-07-22
|
|
11
26
|
|
|
12
27
|
### Added
|
package/dist/neo.mjs
CHANGED
|
@@ -16240,6 +16240,7 @@ function createEnvironment2(documents) {
|
|
|
16240
16240
|
const declarations = /* @__PURE__ */ new Map();
|
|
16241
16241
|
const classes = /* @__PURE__ */ new Map();
|
|
16242
16242
|
const interfaces = /* @__PURE__ */ new Map();
|
|
16243
|
+
const memberOwners = /* @__PURE__ */ new Map();
|
|
16243
16244
|
const typeKinds = /* @__PURE__ */ new Map();
|
|
16244
16245
|
for (const [uri, document] of documents) {
|
|
16245
16246
|
for (const declaration of document.declarations) {
|
|
@@ -16264,6 +16265,9 @@ function createEnvironment2(documents) {
|
|
|
16264
16265
|
};
|
|
16265
16266
|
declarations.set(declaration.name, ref);
|
|
16266
16267
|
classes.set(declaration.name, ref);
|
|
16268
|
+
for (const member of declaration.members) {
|
|
16269
|
+
memberOwners.set(member, ref);
|
|
16270
|
+
}
|
|
16267
16271
|
typeKinds.set(declaration.name, {
|
|
16268
16272
|
kind: "class",
|
|
16269
16273
|
location: ref.location,
|
|
@@ -16286,7 +16290,7 @@ function createEnvironment2(documents) {
|
|
|
16286
16290
|
}
|
|
16287
16291
|
}
|
|
16288
16292
|
}
|
|
16289
|
-
return { declarations, classes, interfaces, typeKinds };
|
|
16293
|
+
return { declarations, classes, interfaces, memberOwners, typeKinds };
|
|
16290
16294
|
}
|
|
16291
16295
|
function validatePersistedNames(documents, diagnostics) {
|
|
16292
16296
|
for (const [uri, document] of documents) {
|
|
@@ -16921,16 +16925,7 @@ function validateClassMemberModifiers(ref, environment, diagnostics) {
|
|
|
16921
16925
|
`Read-only field '${member.name}' cannot be static.`
|
|
16922
16926
|
);
|
|
16923
16927
|
}
|
|
16924
|
-
if (modifiers.has("abstract")) {
|
|
16925
|
-
diagnose(
|
|
16926
|
-
diagnostics,
|
|
16927
|
-
ref.uri,
|
|
16928
|
-
member.nameRange,
|
|
16929
|
-
"abstract-readonly-member",
|
|
16930
|
-
`Read-only field '${member.name}' cannot be abstract.`
|
|
16931
|
-
);
|
|
16932
|
-
}
|
|
16933
|
-
if (member.initializer === void 0) {
|
|
16928
|
+
if (member.initializer === void 0 && !modifiers.has("abstract")) {
|
|
16934
16929
|
diagnose(
|
|
16935
16930
|
diagnostics,
|
|
16936
16931
|
ref.uri,
|
|
@@ -17102,7 +17097,38 @@ function validateClassOverrides(ref, environment, diagnostics) {
|
|
|
17102
17097
|
environment,
|
|
17103
17098
|
true
|
|
17104
17099
|
);
|
|
17100
|
+
const readOnlyContract = candidates.find(
|
|
17101
|
+
(candidate) => candidate.member.modifiers.includes("abstract") && candidate.member.modifiers.includes("readonly")
|
|
17102
|
+
);
|
|
17103
|
+
if (readOnlyContract !== void 0 && !member.modifiers.includes("readonly")) {
|
|
17104
|
+
diagnose(
|
|
17105
|
+
diagnostics,
|
|
17106
|
+
ref.uri,
|
|
17107
|
+
member.nameRange,
|
|
17108
|
+
"override-readonly-contract",
|
|
17109
|
+
`Override '${ref.declaration.name}.${member.name}' must remain readonly because '${readOnlyContract.owner.declaration.name}.${readOnlyContract.member.name}' is an abstract readonly contract.`,
|
|
17110
|
+
related(
|
|
17111
|
+
memberLocation(readOnlyContract),
|
|
17112
|
+
"The abstract readonly contract is declared here."
|
|
17113
|
+
)
|
|
17114
|
+
);
|
|
17115
|
+
continue;
|
|
17116
|
+
}
|
|
17105
17117
|
if (mismatch) {
|
|
17118
|
+
if (mismatch === "the implementation must provide a setter" && member.modifiers.includes("readonly")) {
|
|
17119
|
+
diagnose(
|
|
17120
|
+
diagnostics,
|
|
17121
|
+
ref.uri,
|
|
17122
|
+
member.nameRange,
|
|
17123
|
+
"override-readonly-setter",
|
|
17124
|
+
`Read-only override '${ref.declaration.name}.${member.name}' cannot satisfy the setter required by abstract member '${target.owner.declaration.name}.${target.member.name}'.`,
|
|
17125
|
+
related(
|
|
17126
|
+
memberLocation(target),
|
|
17127
|
+
"The settable abstract member is declared here."
|
|
17128
|
+
)
|
|
17129
|
+
);
|
|
17130
|
+
continue;
|
|
17131
|
+
}
|
|
17106
17132
|
diagnose(
|
|
17107
17133
|
diagnostics,
|
|
17108
17134
|
ref.uri,
|
|
@@ -17281,7 +17307,7 @@ function memberCompatibility(implementation, implementationSubstitution, target,
|
|
|
17281
17307
|
implementationSubstitution
|
|
17282
17308
|
);
|
|
17283
17309
|
const targetType = typeShape(target.member.type, target.substitution);
|
|
17284
|
-
const targetAccessors = target.member.kind === "function" ? { get: false, set: false } : valueAccessors(target.member);
|
|
17310
|
+
const targetAccessors = target.member.kind === "function" ? { get: false, set: false } : valueAccessors(target.member, environment);
|
|
17285
17311
|
const permitsCovariantGetter = allowStoredInterfaceImplementation && implementationFamily === "value" && targetAccessors.get && !targetAccessors.set;
|
|
17286
17312
|
const permitsStoredOverrideNullability = allowStoredOverrideNullability && implementation.kind === "field" && target.member.kind === "field" && typesEqual(
|
|
17287
17313
|
{ ...implementationType, nullable: false },
|
|
@@ -17291,7 +17317,7 @@ function memberCompatibility(implementation, implementationSubstitution, target,
|
|
|
17291
17317
|
return `type '${formatType2(implementationType)}' does not match '${formatType2(targetType)}'`;
|
|
17292
17318
|
}
|
|
17293
17319
|
if (implementation.kind !== "function" || target.member.kind !== "function") {
|
|
17294
|
-
const implementationAccessors = valueAccessors(implementation);
|
|
17320
|
+
const implementationAccessors = valueAccessors(implementation, environment);
|
|
17295
17321
|
if (targetAccessors.get && !implementationAccessors.get) {
|
|
17296
17322
|
return "the implementation must provide a getter";
|
|
17297
17323
|
}
|
|
@@ -17323,11 +17349,13 @@ function memberCompatibility(implementation, implementationSubstitution, target,
|
|
|
17323
17349
|
}
|
|
17324
17350
|
return null;
|
|
17325
17351
|
}
|
|
17326
|
-
function valueAccessors(member) {
|
|
17352
|
+
function valueAccessors(member, environment) {
|
|
17327
17353
|
if (member.kind === "field") {
|
|
17354
|
+
const owner = environment.memberOwners.get(member);
|
|
17355
|
+
const immutable = member.modifiers.includes("abstract") && owner !== void 0 && resolvedSourceMemberStorage(owner, member, environment) === "immutable";
|
|
17328
17356
|
return {
|
|
17329
17357
|
get: true,
|
|
17330
|
-
set: !member.modifiers.includes("readonly")
|
|
17358
|
+
set: !member.modifiers.includes("readonly") && !immutable
|
|
17331
17359
|
};
|
|
17332
17360
|
}
|
|
17333
17361
|
if (member.kind === "function") return { get: false, set: false };
|
|
@@ -22622,6 +22650,7 @@ function memberToDocument(member, baseData3, parentMember) {
|
|
|
22622
22650
|
const normalizedBaseData = normalizeDocumentFields("member", baseData3);
|
|
22623
22651
|
for (const [field, value] of Object.entries(fields)) {
|
|
22624
22652
|
if (MEMBER_IDENTITY_FIELDS.has(field)) continue;
|
|
22653
|
+
if (field === "isReadOnly") continue;
|
|
22625
22654
|
if (Object.prototype.hasOwnProperty.call(normalizedBaseData, field))
|
|
22626
22655
|
continue;
|
|
22627
22656
|
if (!Object.prototype.hasOwnProperty.call(inheritedFields, field)) continue;
|
|
@@ -25142,15 +25171,18 @@ function assertMember2(value, path) {
|
|
|
25142
25171
|
"must be null unless the member modifier is override."
|
|
25143
25172
|
);
|
|
25144
25173
|
}
|
|
25174
|
+
if ((member.modifier === "abstract" || member.modifier === "abstractOverride") && member.defaultValue !== null) {
|
|
25175
|
+
invalid(
|
|
25176
|
+
`${path}.defaultValue`,
|
|
25177
|
+
"must be null for an abstract member; concrete implementations own defaults."
|
|
25178
|
+
);
|
|
25179
|
+
}
|
|
25145
25180
|
assertSystem(member.system, `${path}.system`);
|
|
25146
25181
|
nullableStorage(member.storage, `${path}.storage`);
|
|
25147
25182
|
nullableNonEmptyString(member.storageKey, `${path}.storageKey`);
|
|
25148
25183
|
if (member.isReadOnly === true) {
|
|
25149
25184
|
if (owner.kind !== "classMember" && owner.kind !== "sharedClassMember") {
|
|
25150
|
-
invalid(
|
|
25151
|
-
`${path}.isReadOnly`,
|
|
25152
|
-
"is allowed only on a concrete class field."
|
|
25153
|
-
);
|
|
25185
|
+
invalid(`${path}.isReadOnly`, "is allowed only on a class field.");
|
|
25154
25186
|
}
|
|
25155
25187
|
if (member.isStatic === true) {
|
|
25156
25188
|
invalid(`${path}.isReadOnly`, "cannot be combined with isStatic.");
|
|
@@ -25161,13 +25193,10 @@ function assertMember2(value, path) {
|
|
|
25161
25193
|
`is not valid on value-less member kind ${JSON.stringify(kind)}.`
|
|
25162
25194
|
);
|
|
25163
25195
|
}
|
|
25164
|
-
if (member.
|
|
25165
|
-
invalid(`${path}.isReadOnly`, "cannot be combined with abstract.");
|
|
25166
|
-
}
|
|
25167
|
-
if (member.storage !== "immutable") {
|
|
25196
|
+
if (member.storage !== "immutable" && (member.storage !== null || member.overrideOf === null)) {
|
|
25168
25197
|
invalid(`${path}.storage`, "must be immutable when isReadOnly is true.");
|
|
25169
25198
|
}
|
|
25170
|
-
if (member.defaultValue === null) {
|
|
25199
|
+
if (member.defaultValue === null && member.modifier !== "abstract" && member.modifier !== "abstractOverride") {
|
|
25171
25200
|
invalid(`${path}.defaultValue`, "is required when isReadOnly is true.");
|
|
25172
25201
|
}
|
|
25173
25202
|
}
|
|
@@ -28209,6 +28238,7 @@ function lowerMember(context, ownerClass, declaration, id2, owner) {
|
|
|
28209
28238
|
}
|
|
28210
28239
|
function memberCommon(context, ownerClass, declaration, id2, owner, base) {
|
|
28211
28240
|
const storage = annotation(declaration.annotations, "storage");
|
|
28241
|
+
const inheritedId = declaration.modifiers.includes("override") || declaration.modifiers.includes("sealed") ? base?.overrideOf ?? inheritedMemberId(context, ownerClass, declaration.name) : null;
|
|
28212
28242
|
return {
|
|
28213
28243
|
id: id2,
|
|
28214
28244
|
source: sourceIdentity2(declaration, "member", declaration.name),
|
|
@@ -28225,12 +28255,41 @@ function memberCommon(context, ownerClass, declaration, id2, owner, base) {
|
|
|
28225
28255
|
locked: hasAnnotation(declaration.annotations, "locked"),
|
|
28226
28256
|
required: declaration.kind === "field" ? !declaration.type.nullable : base?.required ?? false,
|
|
28227
28257
|
defaultValue: null,
|
|
28228
|
-
overrideOf:
|
|
28258
|
+
overrideOf: inheritedId,
|
|
28229
28259
|
system: systemMetadata(declaration.annotations),
|
|
28230
|
-
storage: storage ? storageValue(storage, "allowed") : null,
|
|
28260
|
+
storage: storage ? storageValue(storage, "allowed") : declaration.modifiers.includes("readonly") ? base !== void 0 ? base.storage : inheritedId !== null && resolvedMemberStorage(context, inheritedId) === "immutable" ? "immutable" : null : null,
|
|
28231
28261
|
storageKey: storage ? stringArgument(storage, "key") : null
|
|
28232
28262
|
};
|
|
28233
28263
|
}
|
|
28264
|
+
function resolvedMemberStorage(context, memberId, visited = /* @__PURE__ */ new Set()) {
|
|
28265
|
+
if (visited.has(memberId)) return null;
|
|
28266
|
+
visited.add(memberId);
|
|
28267
|
+
const lowered = context.loweredMembers.get(memberId);
|
|
28268
|
+
if (lowered?.storage !== null && lowered?.storage !== void 0) {
|
|
28269
|
+
return lowered.storage;
|
|
28270
|
+
}
|
|
28271
|
+
const persisted = context.baseMembers.get(memberId);
|
|
28272
|
+
if (persisted !== void 0) {
|
|
28273
|
+
if (persisted.storage !== null) return persisted.storage;
|
|
28274
|
+
if (persisted.overrideOf !== null) {
|
|
28275
|
+
return resolvedMemberStorage(context, persisted.overrideOf, visited);
|
|
28276
|
+
}
|
|
28277
|
+
}
|
|
28278
|
+
const sourcePlacement = context.sourceMembersById.get(memberId);
|
|
28279
|
+
if (sourcePlacement !== void 0) {
|
|
28280
|
+
const { ownerClass: sourceClass, member: sourceMember } = sourcePlacement;
|
|
28281
|
+
const storage = annotation(sourceMember.annotations, "storage");
|
|
28282
|
+
const localStorage = storage ? storageValue(storage, "allowed") : null;
|
|
28283
|
+
if (localStorage !== null) return localStorage;
|
|
28284
|
+
const inheritedId = inheritedMemberId(
|
|
28285
|
+
context,
|
|
28286
|
+
sourceClass,
|
|
28287
|
+
sourceMember.name
|
|
28288
|
+
);
|
|
28289
|
+
return inheritedId === null ? null : resolvedMemberStorage(context, inheritedId, visited);
|
|
28290
|
+
}
|
|
28291
|
+
return null;
|
|
28292
|
+
}
|
|
28234
28293
|
function lowerFieldMember(context, ownerClass, declaration, id2, commonInput, base) {
|
|
28235
28294
|
const storedStatic = declaration.modifiers.includes("static") && !declaration.modifiers.includes("readonly");
|
|
28236
28295
|
const settings = annotation(declaration.annotations, "settings");
|
|
@@ -29513,6 +29572,26 @@ function lowerProjectSchemaV4(base, analysis, options = {}) {
|
|
|
29513
29572
|
)
|
|
29514
29573
|
);
|
|
29515
29574
|
}
|
|
29575
|
+
const sourceClasses = new Map(
|
|
29576
|
+
analysis.schema.classes.map((entry) => [
|
|
29577
|
+
materializedId(entry, "class", entry.name),
|
|
29578
|
+
entry
|
|
29579
|
+
])
|
|
29580
|
+
);
|
|
29581
|
+
const sourceMembersById = new Map(
|
|
29582
|
+
analysis.schema.classes.flatMap(
|
|
29583
|
+
(ownerClass) => ownerClass.members.map(
|
|
29584
|
+
(member) => [
|
|
29585
|
+
materializedId(
|
|
29586
|
+
member,
|
|
29587
|
+
"member",
|
|
29588
|
+
`${ownerClass.name}.${member.name}`
|
|
29589
|
+
),
|
|
29590
|
+
{ ownerClass, member }
|
|
29591
|
+
]
|
|
29592
|
+
)
|
|
29593
|
+
)
|
|
29594
|
+
);
|
|
29516
29595
|
const context = {
|
|
29517
29596
|
base,
|
|
29518
29597
|
baseClasses: new Map(base.classes.map((entry) => [entry.id, entry])),
|
|
@@ -29520,12 +29599,8 @@ function lowerProjectSchemaV4(base, analysis, options = {}) {
|
|
|
29520
29599
|
baseInterfaces: new Map(base.interfaces.map((entry) => [entry.id, entry])),
|
|
29521
29600
|
baseEnums: new Map(base.enums.map((entry) => [entry.id, entry])),
|
|
29522
29601
|
baseRelationsById: indexFirstById(base.internalRecordRelations),
|
|
29523
|
-
sourceClasses
|
|
29524
|
-
|
|
29525
|
-
materializedId(entry, "class", entry.name),
|
|
29526
|
-
entry
|
|
29527
|
-
])
|
|
29528
|
-
),
|
|
29602
|
+
sourceClasses,
|
|
29603
|
+
sourceMembersById,
|
|
29529
29604
|
classIdsByName,
|
|
29530
29605
|
interfaceIdsByName,
|
|
29531
29606
|
enumIdsByName,
|