From d60c95cdb712be9b7132e36245e09a9784982a3e Mon Sep 17 00:00:00 2001 From: woksin Date: Tue, 29 Sep 2026 20:21:32 +0200 Subject: [PATCH 1/2] Add --skip-react-hooks to emit server-only proxies Generated commands and queries omit the @cratis/arc.react import and the static use/when hooks when skipReactHooks (CLI: --skip-react-hooks) is set. The default output is unchanged. Refs #112 --- .../Client/source-generation.test.mjs | 13 +++++++++ .../proxy-generation/configuration.md | 1 + Source/Tools/ProxyGenerator/cli.ts | 2 +- .../by_default.ts | 18 +++++++++++++ .../for_commands_and_queries.ts | 27 +++++++++++++++++++ .../ProxyGenerator/parseSourceOptions.ts | 5 ++-- Source/Tools/ProxyGenerator/renderSource.ts | 2 ++ .../ProxyGenerator/renderSourceCommand.ts | 5 +++- .../Tools/ProxyGenerator/renderSourceQuery.ts | 9 ++++--- 9 files changed, 74 insertions(+), 8 deletions(-) create mode 100644 Source/Tools/ProxyGenerator/for_renderSource/when_rendering_with_react_hooks/by_default.ts create mode 100644 Source/Tools/ProxyGenerator/for_renderSource/when_skipping_react_hooks/for_commands_and_queries.ts diff --git a/ContractTests/Client/source-generation.test.mjs b/ContractTests/Client/source-generation.test.mjs index f8e93417..bdbdc13a 100644 --- a/ContractTests/Client/source-generation.test.mjs +++ b/ContractTests/Client/source-generation.test.mjs @@ -276,6 +276,18 @@ export class Create { @fundamentals.field(Guid) id!: Guid; @field(Date) created! }, include: ['Features/**/*.ts', 'interfaces/**/*.ts'] })); const interfaceCompile = spawnSync(join(root, 'node_modules/.bin/tsc'), ['-p', join(src, 'tsconfig.json')], { cwd: root, encoding: 'utf8' }); assert.equal(interfaceCompile.status, 0, interfaceCompile.stdout + interfaceCompile.stderr); + const server = join(src, 'server'); await mkdir(server); + await generateFromSource({ project: join(src, 'tsconfig.json'), artifacts: features, output: server, skipReactHooks: true }); + for (const name of ['Checkout/Create.ts', 'Listing/All.ts', 'Listing/Paged.ts', 'Listing/Stream.ts', 'Listing/Changes.ts']) + assert.doesNotMatch(await readFile(join(server, 'Shop', name), 'utf8'), /arc\.react|static use/); + await writeFile(join(src, 'tsconfig.json'), JSON.stringify({ compilerOptions: { + target: 'ES2022', module: 'ESNext', moduleResolution: 'Bundler', strict: true, + skipLibCheck: false, experimentalDecorators: true, noEmit: true, types: ['node'] + }, include: ['Features/**/*.ts', 'server/**/*.ts'] })); + const serverCompile = spawnSync(join(root, 'node_modules/.bin/tsc'), ['-p', join(src, 'tsconfig.json'), '--listFilesOnly'], { cwd: root, encoding: 'utf8' }); + assert.equal(serverCompile.status, 0, serverCompile.stdout + serverCompile.stderr); + // React must not be pulled into the program at all by server-only proxies. + assert.doesNotMatch(serverCompile.stdout, /arc\.react|node_modules\/(@types\/)?react\//); }); test('edited generated barrels are not overwritten or deleted', async () => { @@ -435,6 +447,7 @@ test('CLI accepts equals options, help, root namespace and safe output switches' const help = spawnSync(process.execPath, [cli, '--help'], { cwd: root, encoding: 'utf8' }); assert.equal(help.status, 0, help.stderr); assert.match(help.stdout, /--emit-interfaces/); + assert.match(help.stdout, /--skip-react-hooks/); const generated = spawnSync(process.execPath, [cli, `--project=${project}`, `--artifacts=${artifacts}`, `--output=${output}`, '--root-namespace=App', '--api-prefix=v2', '--use-generated-metadata', '--skip-index-generation', '--skip-output-deletion'], { cwd: root, encoding: 'utf8' }); assert.equal(generated.status, 0, generated.stderr); diff --git a/Documentation/proxy-generation/configuration.md b/Documentation/proxy-generation/configuration.md index f333d901..87d47e1b 100644 --- a/Documentation/proxy-generation/configuration.md +++ b/Documentation/proxy-generation/configuration.md @@ -34,6 +34,7 @@ Match these to the server's [endpoint mapping](../core/endpoint-mapping.md), or | `--use-proxy-file-suffix` | Off for a dedicated output folder; required for co-located output | Name files `*.proxy.ts` instead of `*.ts` | | `--js-import-specifiers` | Off | Use `.js` extensions in local imports, for native Node ESM; extensionless imports suit Vite and other bundlers | | `--emit-interfaces` | Off | Emit undecorated interfaces instead of model classes; model constructors in proxies become `Object`, so choose this only when you do not need decorated model hydration | +| `--skip-react-hooks` | Off | Emit server-only proxies: no `@cratis/arc.react` import and no static `use*`/`when` hooks on commands and queries, so a project that only needs the typed HTTP client does not need React installed. The generated classes still work with `@cratis/arc` directly. Programmatic option: `skipReactHooks` | | `--skip-index-generation` | Off for a dedicated output folder; automatic for co-located output | Do not write `index.ts` barrels | | `--skip-output-deletion` | Off | Keep stale generated files instead of removing them | | `--metadata ` | Off | Generate server artifact metadata at the given absolute path and infer undecorated bindings | diff --git a/Source/Tools/ProxyGenerator/cli.ts b/Source/Tools/ProxyGenerator/cli.ts index f6d0912c..70f9073b 100644 --- a/Source/Tools/ProxyGenerator/cli.ts +++ b/Source/Tools/ProxyGenerator/cli.ts @@ -13,7 +13,7 @@ import { watchSource } from './watchSource.js'; const usage = 'Usage: arc-proxygenerator --project --artifacts --output ' + ' [--metadata | --use-generated-metadata] [--check-metadata | --watch]' + ' [--root-namespace ] [--api-prefix=] [--segments-to-skip ]' + - ' [--use-proxy-file-suffix] [--skip-index-generation] [--skip-output-deletion] [--emit-interfaces]'; + ' [--use-proxy-file-suffix] [--skip-index-generation] [--skip-output-deletion] [--emit-interfaces] [--skip-react-hooks]'; async function generateManifest(): Promise { const [manifestPath, outputRoot, extra] = process.argv.slice(2); diff --git a/Source/Tools/ProxyGenerator/for_renderSource/when_rendering_with_react_hooks/by_default.ts b/Source/Tools/ProxyGenerator/for_renderSource/when_rendering_with_react_hooks/by_default.ts new file mode 100644 index 00000000..f53a9c3b --- /dev/null +++ b/Source/Tools/ProxyGenerator/for_renderSource/when_rendering_with_react_hooks/by_default.ts @@ -0,0 +1,18 @@ +// Copyright (c) Cratis. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +import { renderSource } from '../../renderSource.js'; +import { a_captured_contract } from '../given/a_captured_contract.js'; + +describe('when rendering with react hooks by default', () => { + let files: ReadonlyMap; + beforeEach(() => { files = renderSource(new a_captured_contract().analysis); }); + it('should import the react command hook', () => { + files.get('differential/fixture/commands/CreateFixtures.ts')!.should.include("from '@cratis/arc.react/commands';"); + files.get('differential/fixture/commands/CreateFixtures.ts')!.should.include('static use(initialValues?: ICreateFixtures)'); + }); + it('should import the react query hooks', () => { + files.get('differential/fixture/models/All.ts')!.should.include("from '@cratis/arc.react/queries';"); + files.get('differential/fixture/models/All.ts')!.should.include('static use('); + files.get('differential/fixture/models/All.ts')!.should.include('static when('); + }); +}); diff --git a/Source/Tools/ProxyGenerator/for_renderSource/when_skipping_react_hooks/for_commands_and_queries.ts b/Source/Tools/ProxyGenerator/for_renderSource/when_skipping_react_hooks/for_commands_and_queries.ts new file mode 100644 index 00000000..30fb8e45 --- /dev/null +++ b/Source/Tools/ProxyGenerator/for_renderSource/when_skipping_react_hooks/for_commands_and_queries.ts @@ -0,0 +1,27 @@ +// Copyright (c) Cratis. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +import { renderSource } from '../../renderSource.js'; +import { a_captured_contract } from '../given/a_captured_contract.js'; + +describe('when skipping react hooks for commands and queries', () => { + let files: ReadonlyMap; + beforeEach(() => { files = renderSource(new a_captured_contract().analysis, { skipReactHooks: true }); }); + it('should not import react from any generated file', () => { + for (const text of files.values()) text.should.not.include('@cratis/arc.react'); + }); + it('should not emit use hooks on the command', () => { + files.get('differential/fixture/commands/CreateFixtures.ts')!.should.not.match(/static use/); + }); + it('should not emit hooks or when on the queries', () => { + for (const name of ['All', 'Observe', 'Search']) + files.get(`differential/fixture/models/${name}.ts`)!.should.not.match(/static (use|when)/); + }); + it('should keep the typed client classes', () => { + files.get('differential/fixture/commands/CreateFixtures.ts')!.should.include("import { Command } from '@cratis/arc/commands';"); + files.get('differential/fixture/commands/CreateFixtures.ts')!.should.include('export class CreateFixtures extends Command<'); + files.get('differential/fixture/models/Observe.ts')!.should.include('export class Observe extends ObservableQueryFor<'); + }); + it('should keep the sorting helpers for array queries', () => { + files.get('differential/fixture/models/All.ts')!.should.include('static get sortBy()'); + }); +}); diff --git a/Source/Tools/ProxyGenerator/parseSourceOptions.ts b/Source/Tools/ProxyGenerator/parseSourceOptions.ts index 9cd54b73..19c043b2 100644 --- a/Source/Tools/ProxyGenerator/parseSourceOptions.ts +++ b/Source/Tools/ProxyGenerator/parseSourceOptions.ts @@ -8,7 +8,7 @@ export function parseSourceOptions(values: readonly string[], usage: string): const options: Record = {}; const flags = ['--skip-command-name-in-route', '--skip-query-name-in-route', '--use-proxy-file-suffix', '--js-import-specifiers', '--skip-index-generation', '--skip-output-deletion', '--emit-interfaces', '--watch', '--check-metadata', - '--use-generated-metadata']; + '--use-generated-metadata', '--skip-react-hooks']; const arguments_ = ['--project', '--artifacts', '--output', '--metadata', '--segments-to-skip', '--api-prefix', '--root-namespace']; for (let index = 0; index < values.length; index++) { const [key, attached] = values[index]!.split(/=(.*)/s, 2); @@ -37,7 +37,8 @@ export function parseSourceOptions(values: readonly string[], usage: string): rootNamespace: typeof options['--root-namespace'] === 'string' ? options['--root-namespace'] : undefined, skipIndexGeneration: options['--skip-index-generation'] === true, skipOutputDeletion: options['--skip-output-deletion'] === true, - emitInterfaces: options['--emit-interfaces'] === true + emitInterfaces: options['--emit-interfaces'] === true, + skipReactHooks: options['--skip-react-hooks'] === true }; return { configuration, watch: options['--watch'] === true, checkMetadata: options['--check-metadata'] === true }; } diff --git a/Source/Tools/ProxyGenerator/renderSource.ts b/Source/Tools/ProxyGenerator/renderSource.ts index d77ace49..19421363 100644 --- a/Source/Tools/ProxyGenerator/renderSource.ts +++ b/Source/Tools/ProxyGenerator/renderSource.ts @@ -19,6 +19,8 @@ export interface SourceRenderOptions { readonly recordedRules?: ReadonlyMap; readonly onDiagnostic?: (message: string) => void; readonly emitInterfaces?: boolean; + /** Omit the `@cratis/arc.react` import and the static `use*` hooks from generated commands and queries. */ + readonly skipReactHooks?: boolean; } const notice = `/*--------------------------------------------------------------------------------------------- * **DO NOT EDIT** - This file is an automatically generated file. diff --git a/Source/Tools/ProxyGenerator/renderSourceCommand.ts b/Source/Tools/ProxyGenerator/renderSourceCommand.ts index ba0709e3..199396bc 100644 --- a/Source/Tools/ProxyGenerator/renderSourceCommand.ts +++ b/Source/Tools/ProxyGenerator/renderSourceCommand.ts @@ -18,5 +18,8 @@ export function renderCommand(operation: SourceOperation, path: string, destinat const fieldType = (field: SourceOperation['fields'][number]): string => `${field.type.text}${field.nullable ? ' | null' : ''}`; const fields = operation.fields.map(field => ` private _${wireName(field.name)}${field.optional ? '?' : '!'}: ${fieldType(field)};`).join('\n'); const properties = operation.fields.map(field => ` get ${wireName(field.name)}(): ${fieldType(field)}${field.optional ? ' | undefined' : ''} {\n return this._${wireName(field.name)};\n }\n\n set ${wireName(field.name)}(value: ${fieldType(field)}${field.optional ? ' | undefined' : ''}) {\n this._${wireName(field.name)} = value;\n this.propertyChanged(${quote(wireName(field.name))});\n }`).join('\n\n'); - return `import { Command${validation ? ', CommandValidator' : ''} } from '@cratis/arc/commands';\nimport { useCommand, type SetCommandValues, type ClearCommandValues } from '@cratis/arc.react/commands';\nimport { PropertyDescriptor } from '@cratis/arc/reflection';\n${imports.join('\n')}${imports.length ? '\n' : ''}\nexport interface I${name} {\n${operation.fields.map(field => ` ${wireName(field.name)}?: ${fieldType(field)};`).join('\n')}\n}\n\n${validation}export class ${name} extends Command implements I${name} {\n readonly route: string = ${quote(route)};\n${validation ? ` readonly validation: CommandValidator = new ${name}Validator();\n` : ''} readonly treatWarningsAsErrors: boolean = ${operation.treatWarningsAsErrors ?? false};\n readonly roles: string[] = [${operation.roles.map(quote).join(', ')}];\n readonly propertyDescriptors: PropertyDescriptor[] = [\n${descriptorFields}\n ];\n${fields ? `\n${fields}\n` : ''}\n constructor() {\n super(${result.void || options.emitInterfaces && result.model ? 'Object' : result.constructor}, ${result.enumerable});\n }\n\n get requestParameters(): string[] { return []; }\n\n${properties}${properties ? '\n\n' : ''} static use(initialValues?: I${name}): [${name}, SetCommandValues, ClearCommandValues] {\n return useCommand<${name}, I${name}>(${name}, initialValues);\n }\n}\n`; + const skipReactHooks = options.skipReactHooks === true; + const useHook = ` static use(initialValues?: I${name}): [${name}, SetCommandValues, ClearCommandValues] {\n return useCommand<${name}, I${name}>(${name}, initialValues);\n }`; + const body = [properties, ...(skipReactHooks ? [] : [useHook])].filter(Boolean).join('\n\n'); + return `import { Command${validation ? ', CommandValidator' : ''} } from '@cratis/arc/commands';${skipReactHooks ? '' : "\nimport { useCommand, type SetCommandValues, type ClearCommandValues } from '@cratis/arc.react/commands';"}\nimport { PropertyDescriptor } from '@cratis/arc/reflection';\n${imports.join('\n')}${imports.length ? '\n' : ''}\nexport interface I${name} {\n${operation.fields.map(field => ` ${wireName(field.name)}?: ${fieldType(field)};`).join('\n')}\n}\n\n${validation}export class ${name} extends Command implements I${name} {\n readonly route: string = ${quote(route)};\n${validation ? ` readonly validation: CommandValidator = new ${name}Validator();\n` : ''} readonly treatWarningsAsErrors: boolean = ${operation.treatWarningsAsErrors ?? false};\n readonly roles: string[] = [${operation.roles.map(quote).join(', ')}];\n readonly propertyDescriptors: PropertyDescriptor[] = [\n${descriptorFields}\n ];\n${fields ? `\n${fields}\n` : ''}\n constructor() {\n super(${result.void || options.emitInterfaces && result.model ? 'Object' : result.constructor}, ${result.enumerable});\n }\n\n get requestParameters(): string[] { return []; }\n${body ? `\n${body}\n` : ''}}\n`; } diff --git a/Source/Tools/ProxyGenerator/renderSourceQuery.ts b/Source/Tools/ProxyGenerator/renderSourceQuery.ts index 5d588ea7..18f557ea 100644 --- a/Source/Tools/ProxyGenerator/renderSourceQuery.ts +++ b/Source/Tools/ProxyGenerator/renderSourceQuery.ts @@ -40,11 +40,12 @@ export function renderSourceQuery(operation: SourceOperation, path: string, dest const use = (functionName: string, nameSuffix: string): string => ` static ${nameSuffix}(${args}${sorting}): ${resultTuple} {\n ${observable && !array ? `const [result] = ${call(functionName)};\n return [result];` : `return ${call(functionName)};`}\n }`; const pageTuple = `[QueryResultWithState<${result.text}>${observable ? '' : ', PerformQuery'}, SetSorting, SetPage, SetPageSize]`; const paging = (functionName: string, nameSuffix: string): string => ` static ${nameSuffix}(pageSize: number, ${args}${sorting}): ${pageTuple} {\n return ${functionName}<${result.text}, ${name}>(${name}, new Paging(0, pageSize), ${hookArguments}, sorting);\n }`; - const hooks = [use(hook, 'use'), ...(array ? [paging(`${hook}WithPaging`, 'useWithPaging')] : []), use(suspense, 'useSuspense'), + const skipReactHooks = options.skipReactHooks === true; + const hooks = skipReactHooks ? [] : [use(hook, 'use'), ...(array ? [paging(`${hook}WithPaging`, 'useWithPaging')] : []), use(suspense, 'useSuspense'), ...(array ? [paging(`${suspense}WithPaging`, 'useSuspenseWithPaging')] : [])]; - if (observable && array) hooks.push(` static useChangeStream(${args}getKey?: (item: ${model}) => unknown, sorting?: Sorting): ChangeSet<${model}> {\n return useChangeStream<${model}, ${name}${parameterType ? `, ${parameterType}` : ''}>(${name}, ${hookArguments}, getKey, sorting);\n }`); + if (!skipReactHooks && observable && array) hooks.push(` static useChangeStream(${args}getKey?: (item: ${model}) => unknown, sorting?: Sorting): ChangeSet<${model}> {\n return useChangeStream<${model}, ${name}${parameterType ? `, ${parameterType}` : ''}>(${name}, ${hookArguments}, getKey, sorting);\n }`); const when = observable ? 'ObservableQueryWhen' : 'QueryWhen'; - hooks.push(` static when(condition: boolean): ${when}<${name}, ${generic}> {\n return new ${when}<${name}, ${generic}>(${name}, condition);\n }`); + if (!skipReactHooks) hooks.push(` static when(condition: boolean): ${when}<${name}, ${generic}> {\n return new ${when}<${name}, ${generic}>(${name}, condition);\n }`); const coreImports = [base, 'QueryResultWithState', ...(validation ? ['QueryValidator'] : []), ...(array ? ['Sorting', 'Paging'] : []), ...(array && modelDefinition?.fields.length ? ['SortingActions', observable ? 'SortingActionsForObservableQuery' : 'SortingActionsForQuery'] : []), ...(observable && array ? ['ChangeSet'] : []), ...(operation.httpMethod ? ['QueryHttpMethod'] : [])]; @@ -54,5 +55,5 @@ export function renderSourceQuery(operation: SourceOperation, path: string, dest const sortingMembers = array ? `\n get sortBy(): ${name}SortBy { return this._sortBy; }\n static get sortBy(): ${name}SortByWithoutQuery { return this._sortBy; }\n` : ''; const sortingAction = observable ? 'SortingActionsForObservableQuery' : 'SortingActionsForQuery'; const sortClasses = array ? `class ${name}SortBy {\n${modelDefinition?.fields.map(field => ` readonly ${field.name}: ${sortingAction}<${result.text}>;`).join('\n') ?? ''}\n constructor(readonly query: ${name}) {\n${modelDefinition?.fields.map(field => ` this.${field.name} = new ${sortingAction}<${result.text}>(${quote(field.name)}, query);`).join('\n') ?? ''}\n }\n}\nclass ${name}SortByWithoutQuery {\n${modelDefinition?.fields.map(field => ` readonly ${field.name} = new SortingActions(${quote(field.name)});`).join('\n') ?? ''}\n}\n\n` : ''; - return `import { ${coreImports.map(item => ['QueryResultWithState', 'Sorting', 'ChangeSet'].includes(item) ? `type ${item}` : item).join(', ')} } from '@cratis/arc/queries';\nimport { ${reactImports.map(item => ['SetPage', 'SetPageSize', 'PerformQuery', 'SetSorting'].includes(item) ? `type ${item}` : item).join(', ')} } from '@cratis/arc.react/queries';\nimport { ParameterDescriptor } from '@cratis/arc/reflection';\n${imports.join('\n')}${imports.length ? '\n' : ''}\n${sortClasses}${params}${validation}export class ${name} extends ${base}<${generic}> {\n readonly route: string = ${quote(route)};\n readonly queryName: string = ${quote([operation.namespace, operation.owner, operation.name].filter(Boolean).join('.'))};\n${validation ? ` readonly validation: QueryValidator = new ${name}Validator();\n` : ''} readonly treatWarningsAsErrors: boolean = ${operation.treatWarningsAsErrors ?? false};\n readonly roles: string[] = [${operation.roles.map(quote).join(', ')}];\n readonly defaultValue: ${result.text} = ${array ? '[]' : `{} as ${result.text}`};\n${array ? ` private readonly _sortBy: ${name}SortBy;\n private static readonly _sortBy: ${name}SortByWithoutQuery = new ${name}SortByWithoutQuery();\n` : ''}\n constructor() {\n super(${options.emitInterfaces && result.model ? 'Object' : result.constructor}, ${array});${array ? `\n this._sortBy = new ${name}SortBy(this);` : ''}${operation.httpMethod ? `\n this.setHttpMethod(QueryHttpMethod.${Object.entries(QueryHttpMethod).find(([, value]) => value === operation.httpMethod)?.[0]});` : ''}\n }\n\n get requiredRequestParameters(): string[] {\n return [\n${request}\n ];\n }\n\n readonly parameterDescriptors: ParameterDescriptor[] = [\n${descriptors}\n ];\n${fields ? `\n${fields}\n` : ''}${sortingMembers}\n${hooks.join('\n\n')}\n}\n`; + return `import { ${coreImports.map(item => ['QueryResultWithState', 'Sorting', 'ChangeSet'].includes(item) ? `type ${item}` : item).join(', ')} } from '@cratis/arc/queries';\n${skipReactHooks ? '' : `import { ${reactImports.map(item => ['SetPage', 'SetPageSize', 'PerformQuery', 'SetSorting'].includes(item) ? `type ${item}` : item).join(', ')} } from '@cratis/arc.react/queries';\n`}import { ParameterDescriptor } from '@cratis/arc/reflection';\n${imports.join('\n')}${imports.length ? '\n' : ''}\n${sortClasses}${params}${validation}export class ${name} extends ${base}<${generic}> {\n readonly route: string = ${quote(route)};\n readonly queryName: string = ${quote([operation.namespace, operation.owner, operation.name].filter(Boolean).join('.'))};\n${validation ? ` readonly validation: QueryValidator = new ${name}Validator();\n` : ''} readonly treatWarningsAsErrors: boolean = ${operation.treatWarningsAsErrors ?? false};\n readonly roles: string[] = [${operation.roles.map(quote).join(', ')}];\n readonly defaultValue: ${result.text} = ${array ? '[]' : `{} as ${result.text}`};\n${array ? ` private readonly _sortBy: ${name}SortBy;\n private static readonly _sortBy: ${name}SortByWithoutQuery = new ${name}SortByWithoutQuery();\n` : ''}\n constructor() {\n super(${options.emitInterfaces && result.model ? 'Object' : result.constructor}, ${array});${array ? `\n this._sortBy = new ${name}SortBy(this);` : ''}${operation.httpMethod ? `\n this.setHttpMethod(QueryHttpMethod.${Object.entries(QueryHttpMethod).find(([, value]) => value === operation.httpMethod)?.[0]});` : ''}\n }\n\n get requiredRequestParameters(): string[] {\n return [\n${request}\n ];\n }\n\n readonly parameterDescriptors: ParameterDescriptor[] = [\n${descriptors}\n ];\n${fields ? `\n${fields}\n` : ''}${sortingMembers}${hooks.length ? `\n${hooks.join('\n\n')}\n` : ''}}\n`; } From f5ca5cd37a1c8891d9182795cbaabf7d1e00e9d1 Mon Sep 17 00:00:00 2001 From: woksin Date: Tue, 29 Sep 2026 20:22:53 +0200 Subject: [PATCH 2/2] Set version 0.51.0 --- ContractTests/Client/package.json | 2 +- Documentation/index.md | 2 +- Documentation/reference/packages.md | 2 +- README.md | 2 +- Source/Chronicle/package.json | 6 +++--- Source/CodeAnalysis/package.json | 2 +- Source/Core/package.json | 2 +- Source/Cratis/package.json | 2 +- Source/Drizzle/package.json | 4 ++-- Source/Express/package.json | 2 +- Source/Fastify/package.json | 2 +- Source/Hono/package.json | 2 +- Source/MongoDB/package.json | 4 ++-- Source/Testing/package.json | 2 +- Source/Tools/ProxyGenerator/package.json | 2 +- yarn.lock | 8 ++++---- 16 files changed, 23 insertions(+), 23 deletions(-) diff --git a/ContractTests/Client/package.json b/ContractTests/Client/package.json index 6755d51f..ee2b1ce6 100644 --- a/ContractTests/Client/package.json +++ b/ContractTests/Client/package.json @@ -1,6 +1,6 @@ { "name": "@cratis/arc.core-client-contract", - "version": "0.50.0", + "version": "0.51.0", "private": true, "type": "module", "dependencies": { diff --git a/Documentation/index.md b/Documentation/index.md index 0bd84f6a..0c2f0c0a 100644 --- a/Documentation/index.md +++ b/Documentation/index.md @@ -8,7 +8,7 @@ Arc for TypeScript is a Node.js server implementation of [Arc](/arc/), the Crati Without it, a Node.js backend for an Arc frontend means writing every route, request parser, validation response, and status code by hand, then keeping all of it in step with the frontend. With it, commands and queries run through one pipeline that owns those concerns, the wire behavior follows Arc on .NET, and the proxy generator writes the typed frontend client from your source. :::caution[Source preview, no full parity] -No package is published to npm; the manifests are at version 0.50.0 for a source preview. Arc for TypeScript does **not** have full parity with Arc on .NET, and package names and APIs can still change. The [capability reference](reference/capabilities.md) is the single place for status and evidence. +No package is published to npm; the manifests are at version 0.51.0 for a source preview. Arc for TypeScript does **not** have full parity with Arc on .NET, and package names and APIs can still change. The [capability reference](reference/capabilities.md) is the single place for status and evidence. ::: ## What it looks like diff --git a/Documentation/reference/packages.md b/Documentation/reference/packages.md index 299a94a7..905356b8 100644 --- a/Documentation/reference/packages.md +++ b/Documentation/reference/packages.md @@ -3,7 +3,7 @@ title: Packages description: The packages this repository builds, what each exports, their peer dependencies and Node.js requirements, and how they relate to the published @cratis/arc client. --- -Every package in this repository is at version 0.50.0, the version of the source preview. **None is published to npm.** They ship ES modules only. Clone this repository, run `yarn install` and `yarn build`, and then use the packages in one of two ways: +Every package in this repository is at version 0.51.0, the version of the source preview. **None is published to npm.** They ship ES modules only. Clone this repository, run `yarn install` and `yarn build`, and then use the packages in one of two ways: - **Inside the clone.** Put your application in a folder under `Samples/`, which the root `workspaces` list includes, and reference the packages with the `workspace:^` protocol, as [`Samples/Tasks/package.json`](https://github.com/Cratis/Arc.TypeScript/blob/main/Samples/Tasks/package.json) does. `workspace:^` resolves only inside this repository's Yarn workspace. - **In your own project.** Pack each package you need with `yarn workspace pack --out ` and install the tarballs with npm. Use `yarn pack`: it rewrites `workspace:^` dependencies to version ranges, and `npm pack` does not. `yarn check:consumers` installs packed packages this way to check NodeNext and Bundler consumers. diff --git a/README.md b/README.md index c82532c7..b7b77dec 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ export class TaskItem { | `@cratis/arc.chronicle` | [`Source/Chronicle`](Source/Chronicle) | **Experimental.** `builder.withChronicle` appends returned events and resolves registered read models by command key; nested command returns join one event-log batch. In-memory command assertions are available under `@cratis/arc.chronicle/testing`. SDK 6.19.0 imports natively and infers read models from projections/reducers; an opt-in kernel suite covers aggregate replay and reactor commands. Full .NET transaction parity remains unverified. | | `@cratis/cratis` | [`Source/Cratis`](Source/Cratis) | **Experimental source preview.** `CratisApplication.createBuilder()` and `builder.addCratis()` compose Arc and a Chronicle client without installing authentication; not yet published to npm. | -Every package manifest is at version 0.50.0. That is the version of this source preview, not an npm release, and the Chronicle package is experimental. The packages ship ES modules only, and schemas use Zod 4. The default core entry, host adapters, MongoDB, and Drizzle packages need Node.js 22 or later. The Fetch entry has a neutral bundle with `node:async_hooks` as its only Node import; its command, query, and SSE paths run in a Next.js App Router route handler on the Node.js runtime, with Bun and Deno smoke checks; Cloudflare Workers and the Next.js Edge runtime are not supported. See [Fetch API runtimes](Documentation/hosts/fetch-runtimes.md). The root workspace needs Node.js 22.19 or later, because it installs the Chronicle SDK; Node.js 24 LTS is recommended. +Every package manifest is at version 0.51.0. That is the version of this source preview, not an npm release, and the Chronicle package is experimental. The packages ship ES modules only, and schemas use Zod 4. The default core entry, host adapters, MongoDB, and Drizzle packages need Node.js 22 or later. The Fetch entry has a neutral bundle with `node:async_hooks` as its only Node import; its command, query, and SSE paths run in a Next.js App Router route handler on the Node.js runtime, with Bun and Deno smoke checks; Cloudflare Workers and the Next.js Edge runtime are not supported. See [Fetch API runtimes](Documentation/hosts/fetch-runtimes.md). The root workspace needs Node.js 22.19 or later, because it installs the Chronicle SDK; Node.js 24 LTS is recommended. ## Try it diff --git a/Source/Chronicle/package.json b/Source/Chronicle/package.json index be1c004e..9c6d2565 100644 --- a/Source/Chronicle/package.json +++ b/Source/Chronicle/package.json @@ -1,6 +1,6 @@ { "name": "@cratis/arc.chronicle", - "version": "0.50.0", + "version": "0.51.0", "publishConfig": { "access": "public" }, @@ -34,8 +34,8 @@ "README.md" ], "peerDependencies": { - "@cratis/arc.core": "^0.50.0", - "@cratis/arc.testing": "^0.50.0", + "@cratis/arc.core": "^0.51.0", + "@cratis/arc.testing": "^0.51.0", "@cratis/chronicle": "^6.17.0", "@cratis/fundamentals": "^7.19.6", "rxjs": "^7.8.2", diff --git a/Source/CodeAnalysis/package.json b/Source/CodeAnalysis/package.json index b441465f..773a1211 100644 --- a/Source/CodeAnalysis/package.json +++ b/Source/CodeAnalysis/package.json @@ -1,6 +1,6 @@ { "name": "@cratis/eslint-plugin-arc-core", - "version": "0.50.0", + "version": "0.51.0", "type": "module", "license": "MIT", "description": "ESLint diagnostics for Arc for TypeScript server artifacts", diff --git a/Source/Core/package.json b/Source/Core/package.json index 13cdc857..11ffd97a 100644 --- a/Source/Core/package.json +++ b/Source/Core/package.json @@ -1,6 +1,6 @@ { "name": "@cratis/arc.core", - "version": "0.50.0", + "version": "0.51.0", "type": "module", "license": "MIT", "publishConfig": { diff --git a/Source/Cratis/package.json b/Source/Cratis/package.json index 4429b801..23ca0f42 100644 --- a/Source/Cratis/package.json +++ b/Source/Cratis/package.json @@ -1,6 +1,6 @@ { "name": "@cratis/cratis", - "version": "0.50.0", + "version": "0.51.0", "type": "module", "license": "MIT", "description": "Arc and experimental Chronicle composition for Node.js", diff --git a/Source/Drizzle/package.json b/Source/Drizzle/package.json index 4202ec72..680a4b53 100644 --- a/Source/Drizzle/package.json +++ b/Source/Drizzle/package.json @@ -1,6 +1,6 @@ { "name": "@cratis/arc.drizzle", - "version": "0.50.0", + "version": "0.51.0", "type": "module", "license": "MIT", "publishConfig": { @@ -29,7 +29,7 @@ "README.md" ], "peerDependencies": { - "@cratis/arc.core": "^0.50.0", + "@cratis/arc.core": "^0.51.0", "@cratis/fundamentals": "^7.19.6", "drizzle-orm": "^0.45.0", "rxjs": "^7.8.2" diff --git a/Source/Express/package.json b/Source/Express/package.json index 02d0d170..1323b0d3 100644 --- a/Source/Express/package.json +++ b/Source/Express/package.json @@ -1,6 +1,6 @@ { "name": "@cratis/arc.express", - "version": "0.50.0", + "version": "0.51.0", "type": "module", "license": "MIT", "publishConfig": { diff --git a/Source/Fastify/package.json b/Source/Fastify/package.json index d176d88f..1009164c 100644 --- a/Source/Fastify/package.json +++ b/Source/Fastify/package.json @@ -1,6 +1,6 @@ { "name": "@cratis/arc.fastify", - "version": "0.50.0", + "version": "0.51.0", "type": "module", "license": "MIT", "publishConfig": { diff --git a/Source/Hono/package.json b/Source/Hono/package.json index b683cf43..f3c857e5 100644 --- a/Source/Hono/package.json +++ b/Source/Hono/package.json @@ -1,6 +1,6 @@ { "name": "@cratis/arc.hono", - "version": "0.50.0", + "version": "0.51.0", "type": "module", "license": "MIT", "publishConfig": { diff --git a/Source/MongoDB/package.json b/Source/MongoDB/package.json index e85e1647..3983eeb9 100644 --- a/Source/MongoDB/package.json +++ b/Source/MongoDB/package.json @@ -1,6 +1,6 @@ { "name": "@cratis/arc.mongodb", - "version": "0.50.0", + "version": "0.51.0", "type": "module", "license": "MIT", "publishConfig": { @@ -29,7 +29,7 @@ "README.md" ], "peerDependencies": { - "@cratis/arc.core": "^0.50.0", + "@cratis/arc.core": "^0.51.0", "@cratis/fundamentals": "^7.19.6", "@opentelemetry/api": "^1.9.0", "mongodb": "^6.21.0", diff --git a/Source/Testing/package.json b/Source/Testing/package.json index 09864e7b..18f90f1d 100644 --- a/Source/Testing/package.json +++ b/Source/Testing/package.json @@ -1,6 +1,6 @@ { "name": "@cratis/arc.testing", - "version": "0.50.0", + "version": "0.51.0", "type": "module", "license": "MIT", "publishConfig": { diff --git a/Source/Tools/ProxyGenerator/package.json b/Source/Tools/ProxyGenerator/package.json index 5d76092a..bbc9e8a2 100644 --- a/Source/Tools/ProxyGenerator/package.json +++ b/Source/Tools/ProxyGenerator/package.json @@ -1,6 +1,6 @@ { "name": "@cratis/arc.proxygenerator", - "version": "0.50.0", + "version": "0.51.0", "description": "TypeScript source analyzer and deterministic Arc client proxy generator", "repository": { "type": "git", diff --git a/yarn.lock b/yarn.lock index e0e03dcb..a4dc6170 100644 --- a/yarn.lock +++ b/yarn.lock @@ -110,8 +110,8 @@ __metadata: rxjs: "npm:^7.8.2" zod: "npm:^4.1.0" peerDependencies: - "@cratis/arc.core": ^0.50.0 - "@cratis/arc.testing": ^0.50.0 + "@cratis/arc.core": ^0.51.0 + "@cratis/arc.testing": ^0.51.0 "@cratis/chronicle": ^6.17.0 "@cratis/fundamentals": ^7.19.6 rxjs: ^7.8.2 @@ -216,7 +216,7 @@ __metadata: rxjs: "npm:^7.8.2" sql.js: "npm:^1.14.2" peerDependencies: - "@cratis/arc.core": ^0.50.0 + "@cratis/arc.core": ^0.51.0 "@cratis/fundamentals": ^7.19.6 drizzle-orm: ^0.45.0 rxjs: ^7.8.2 @@ -282,7 +282,7 @@ __metadata: mongodb: "npm:^6.21.0" rxjs: "npm:^7.8.2" peerDependencies: - "@cratis/arc.core": ^0.50.0 + "@cratis/arc.core": ^0.51.0 "@cratis/fundamentals": ^7.19.6 "@opentelemetry/api": ^1.9.0 mongodb: ^6.21.0