@contractkit/plugin-csharp 0.1.4 → 0.1.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contractkit/plugin-csharp",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "ContractKit built-in plugin: C#/.NET SDK client generation (System.Text.Json + HttpClient, no NuGet dependencies)",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -29,7 +29,7 @@
29
29
  ".": "./dist/index.js"
30
30
  },
31
31
  "dependencies": {
32
- "@contractkit/core": "0.30.1"
32
+ "@contractkit/core": "0.31.1"
33
33
  },
34
34
  "devDependencies": {
35
35
  "unplugin-swc": "^1.5.9",
@@ -1,4 +1,4 @@
1
- import type { ContractRootNode, ContractTypeNode, FieldNode, ModelNode, ScalarTypeNode } from '@contractkit/core';
1
+ import type { ContractRootNode, ContractTypeNode, FieldDefault, FieldNode, ModelNode, ScalarTypeNode } from '@contractkit/core';
2
2
  import { buildModelIndex, computeModelsWithInput, resolveEffectiveFields, topoSortModels } from '@contractkit/core';
3
3
  import type { HoistedDecl, HoistResult } from './hoist.js';
4
4
  import { quoteCSharpString, safeMemberName, toCSharpEnumMemberName, toCSharpPropertyName, xmlDocLines } from './naming.js';
@@ -272,6 +272,18 @@ function literalCSharpType(value: string | number | boolean, ctx: RenderContext)
272
272
 
273
273
  // ─── Default values ────────────────────────────────────────────────────────
274
274
 
275
+ const MIN_SAFE_BIGINT = BigInt(Number.MIN_SAFE_INTEGER);
276
+ const MAX_SAFE_BIGINT = BigInt(Number.MAX_SAFE_INTEGER);
277
+
278
+ /**
279
+ * Whether a `bigint` field's default is written as `new BigInteger(<literal>)` rather than parsed
280
+ * from a string. A number past 2**53 has already lost digits, so it cannot be written as a literal;
281
+ * a bigint keeps the same cutoff so a value renders one way whichever form it arrives in.
282
+ */
283
+ function isSafeInteger(value: number | bigint): boolean {
284
+ return typeof value === 'bigint' ? value >= MIN_SAFE_BIGINT && value <= MAX_SAFE_BIGINT : Number.isSafeInteger(value);
285
+ }
286
+
275
287
  /**
276
288
  * Render a contract default as a C# expression of the field's own type. Returns `undefined` when the
277
289
  * value cannot be expressed, so the field is emitted as `required` rather than with an initializer
@@ -284,7 +296,7 @@ function literalCSharpType(value: string | number | boolean, ctx: RenderContext)
284
296
  * `Nullable<Rating>`. An enum a member would shadow is written from the global namespace instead.
285
297
  */
286
298
  function renderDefault(
287
- value: string | number | boolean,
299
+ value: FieldDefault,
288
300
  type: ContractTypeNode,
289
301
  ctx: RenderContext,
290
302
  memberNames: ReadonlySet<string> = new Set(),
@@ -295,7 +307,8 @@ function renderDefault(
295
307
 
296
308
  if (typeof value === 'boolean') return String(value);
297
309
 
298
- if (typeof value === 'number') {
310
+ // A `bigint` value comes from a `bigint` field, whose case below writes it from its exact digits.
311
+ if (typeof value === 'number' || typeof value === 'bigint') {
299
312
  if (inner.kind === 'scalar') {
300
313
  switch (inner.name) {
301
314
  case 'int':
@@ -305,7 +318,7 @@ function renderDefault(
305
318
  case 'decimal':
306
319
  return `${value}m`;
307
320
  case 'bigint':
308
- return Number.isSafeInteger(value) ? `new BigInteger(${value})` : `BigInteger.Parse("${value}")`;
321
+ return isSafeInteger(value) ? `new BigInteger(${value})` : `BigInteger.Parse("${value}")`;
309
322
  }
310
323
  }
311
324
  return Number.isInteger(value) ? `${value}L` : `${value}d`;
@@ -133,6 +133,15 @@ describe('optional, nullable, default and literal', () => {
133
133
  expect(render(one('M', field('f', scalarType('bigint'), { default: 7 })))).toContain('= new BigInteger(7);');
134
134
  });
135
135
 
136
+ it('initializes a bigint default from its exact digits', () => {
137
+ // The AST carries a bigint default as a JS bigint: small ones keep the constructor a number
138
+ // default gets, and one past 2**53 is parsed from the digits a number could not hold.
139
+ expect(render(one('M', field('f', scalarType('bigint'), { default: 7n })))).toContain('= new BigInteger(7);');
140
+ expect(render(one('M', field('f', scalarType('bigint'), { default: 9007199254740993n })))).toContain(
141
+ '= BigInteger.Parse("9007199254740993");',
142
+ );
143
+ });
144
+
136
145
  it('leaves a field required when its default has no C# literal form', () => {
137
146
  const out = render(one('M', field('f', scalarType('uuid'), { default: 'not-a-literal' })));
138
147
  expect(out).toContain('public required Guid F { get; init; }');