From 372454bfc0aecadd92c6cb34a30459881e81f4c9 Mon Sep 17 00:00:00 2001 From: JakeSCahill Date: Thu, 2 Oct 2025 10:17:21 +0100 Subject: [PATCH 1/9] Add a CLI command that gen erates OpenAPI spec files from proto files --- __tests__/tools/bundle-openapi.test.js | 497 +++++ admin/redpanda-admin-api.yaml | 1580 ++++++++++++++ bin/doc-tools.js | 60 +- cli-utils/install-test-dependencies.sh | 520 ++++- package-lock.json | 2787 +++++++++++++++++++----- package.json | 11 +- tools/bundle-openapi.js | 757 +++++++ 7 files changed, 5535 insertions(+), 677 deletions(-) create mode 100644 __tests__/tools/bundle-openapi.test.js create mode 100644 admin/redpanda-admin-api.yaml create mode 100644 tools/bundle-openapi.js diff --git a/__tests__/tools/bundle-openapi.test.js b/__tests__/tools/bundle-openapi.test.js new file mode 100644 index 0000000..26e8ed2 --- /dev/null +++ b/__tests__/tools/bundle-openapi.test.js @@ -0,0 +1,497 @@ +const fs = require('fs'); +const path = require('path'); +const os = require('os'); +const { execSync, spawnSync } = require('child_process'); + +// Mock child_process for isolated testing +jest.mock('child_process'); + +// Import all functions from the bundler +const { + normalizeTag, + getMajorMinor, + sortObjectKeys, + detectBundler, + createEntrypoint, + runBundler, + postProcessBundle, + bundleOpenAPI +} = require('../../tools/bundle-openapi.js'); + +describe('OpenAPI Bundle Tool - Production Test Suite', () => { + + let mockTempDir; + + beforeEach(() => { + // Create a real temp directory for each test + mockTempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'bundle-test-')); + + // Reset all mocks + jest.clearAllMocks(); + + // Mock successful execSync by default + execSync.mockImplementation(() => ''); + spawnSync.mockImplementation(() => ({ status: 0, error: null })); + }); + + afterEach(() => { + // Clean up temp directory + if (mockTempDir && fs.existsSync(mockTempDir)) { + fs.rmSync(mockTempDir, { recursive: true, force: true }); + } + }); + + describe('Version Handling', () => { + describe('normalizeTag', () => { + test('should remove v prefix from semantic versions', () => { + expect(normalizeTag('v24.3.2')).toBe('24.3.2'); + expect(normalizeTag('v25.1.0')).toBe('25.1.0'); + expect(normalizeTag('v1.0.0-rc1')).toBe('1.0.0-rc1'); + }); + + test('should preserve versions without v prefix', () => { + expect(normalizeTag('24.3.2')).toBe('24.3.2'); + expect(normalizeTag('25.1.0')).toBe('25.1.0'); + expect(normalizeTag('1.0.0-beta')).toBe('1.0.0-beta'); + }); + + test('should handle dev branch', () => { + expect(normalizeTag('dev')).toBe('dev'); + expect(normalizeTag(' dev ')).toBe('dev'); + }); + + test('should validate input parameters', () => { + expect(() => normalizeTag('')).toThrow('Tag must be a non-empty string'); + expect(() => normalizeTag(null)).toThrow('Tag must be a non-empty string'); + expect(() => normalizeTag(undefined)).toThrow('Tag must be a non-empty string'); + expect(() => normalizeTag(123)).toThrow('Tag must be a non-empty string'); + }); + + test('should reject invalid version formats', () => { + expect(() => normalizeTag('invalid-tag')).toThrow(/Invalid version format.*Expected format like/); + expect(() => normalizeTag('v1.x.y')).toThrow(/Invalid version format/); + expect(() => normalizeTag('random-string')).toThrow(/Invalid version format/); + expect(() => normalizeTag('v')).toThrow(/Invalid version format/); + }); + + test('should handle edge cases', () => { + expect(() => normalizeTag(' ')).toThrow(/Invalid version format/); + expect(() => normalizeTag('\t')).toThrow(/Invalid version format/); + expect(() => normalizeTag('\n')).toThrow(/Invalid version format/); + }); + }); + + describe('getMajorMinor', () => { + test('should extract major.minor from semantic versions', () => { + expect(getMajorMinor('24.3.2')).toBe('24.3'); + expect(getMajorMinor('25.1.0')).toBe('25.1'); + expect(getMajorMinor('1.0.0-rc1')).toBe('1.0'); + expect(getMajorMinor('0.1.2')).toBe('0.1'); + }); + + test('should handle dev version', () => { + expect(getMajorMinor('dev')).toBe('dev'); + }); + + test('should validate input parameters', () => { + expect(() => getMajorMinor('')).toThrow('Version must be a non-empty string'); + expect(() => getMajorMinor(null)).toThrow('Version must be a non-empty string'); + expect(() => getMajorMinor(undefined)).toThrow('Version must be a non-empty string'); + expect(() => getMajorMinor(123)).toThrow('Version must be a non-empty string'); + }); + + test('should reject invalid formats', () => { + expect(() => getMajorMinor('24')).toThrow(/Invalid version format.*Expected X\.Y\.Z format/); + expect(() => getMajorMinor('invalid')).toThrow(/Expected X\.Y\.Z format/); + }); + + test('should validate numeric components', () => { + expect(() => getMajorMinor('abc.def.ghi')).toThrow(/Major and minor versions must be numbers/); + expect(() => getMajorMinor('1.x.2')).toThrow(/Major and minor versions must be numbers/); + expect(() => getMajorMinor('v1.2.3')).toThrow(/Major and minor versions must be numbers/); + }); + + test('should handle versions with extra components', () => { + expect(getMajorMinor('24.3.2.1')).toBe('24.3'); + expect(getMajorMinor('1.0.0-rc1+build123')).toBe('1.0'); + }); + }); + }); + + describe('Utility Functions', () => { + describe('sortObjectKeys', () => { + test('should sort object keys alphabetically', () => { + const input = { z: 1, a: 2, m: 3 }; + const result = sortObjectKeys(input); + expect(Object.keys(result)).toEqual(['a', 'm', 'z']); + }); + + test('should sort nested objects recursively', () => { + const input = { + z: { b: 1, a: 2 }, + a: { z: 3, b: 4 }, + m: 'value' + }; + + const result = sortObjectKeys(input); + expect(Object.keys(result)).toEqual(['a', 'm', 'z']); + expect(Object.keys(result.z)).toEqual(['a', 'b']); + expect(Object.keys(result.a)).toEqual(['b', 'z']); + }); + + test('should handle arrays by sorting their object elements', () => { + const input = [{ z: 1, a: 2 }, { b: 3, c: 4 }]; + const result = sortObjectKeys(input); + expect(Object.keys(result[0])).toEqual(['a', 'z']); + expect(Object.keys(result[1])).toEqual(['b', 'c']); + }); + + test('should handle primitive values unchanged', () => { + expect(sortObjectKeys('string')).toBe('string'); + expect(sortObjectKeys(42)).toBe(42); + expect(sortObjectKeys(true)).toBe(true); + expect(sortObjectKeys(null)).toBe(null); + expect(sortObjectKeys(undefined)).toBe(undefined); + }); + + test('should handle complex nested structures', () => { + const input = { + z: { + nested: { b: [{ z: 1, a: 2 }], a: 'value' } + }, + a: ['primitive', { nested: true }] + }; + + const result = sortObjectKeys(input); + expect(Object.keys(result)).toEqual(['a', 'z']); + expect(Object.keys(result.z.nested)).toEqual(['a', 'b']); + expect(Object.keys(result.z.nested.b[0])).toEqual(['a', 'z']); + }); + + test('should handle empty objects and arrays', () => { + expect(sortObjectKeys({})).toEqual({}); + expect(sortObjectKeys([])).toEqual([]); + expect(sortObjectKeys({ a: {}, b: [] })).toEqual({ a: {}, b: [] }); + }); + }); + }); + + describe('Bundler Detection', () => { + describe('detectBundler', () => { + test('should detect swagger-cli when available', () => { + execSync.mockImplementationOnce(() => 'swagger-cli version 4.0.4'); + + const bundler = detectBundler(); + expect(bundler).toBe('swagger-cli'); + expect(execSync).toHaveBeenCalledWith('swagger-cli --version', { stdio: 'ignore', timeout: 10000 }); + }); + + test('should detect redocly when swagger-cli not available', () => { + execSync + .mockImplementationOnce(() => { throw new Error('Command not found'); }) + .mockImplementationOnce(() => 'redocly 1.0.0'); + + const bundler = detectBundler(); + expect(bundler).toBe('redocly'); + }); + + test('should detect npx redocly when others not available', () => { + execSync + .mockImplementationOnce(() => { throw new Error('Command not found'); }) + .mockImplementationOnce(() => { throw new Error('Command not found'); }) + .mockImplementationOnce(() => 'redocly 1.0.0'); + + const bundler = detectBundler(); + expect(bundler).toBe('npx @redocly/cli'); + }); + + test('should throw helpful error when no bundler available', () => { + execSync.mockImplementation(() => { throw new Error('Command not found'); }); + + expect(() => detectBundler()).toThrow(/No OpenAPI bundler found/); + expect(() => detectBundler()).toThrow(/npm install/); + expect(() => detectBundler()).toThrow(/redocly.com/); + }); + + test('should handle timeout properly', () => { + execSync.mockImplementation(() => { throw new Error('ETIMEDOUT'); }); + + expect(() => detectBundler()).toThrow(/No OpenAPI bundler found/); + }); + }); + }); + + describe('Fragment Discovery', () => { + describe('createEntrypoint', () => { + beforeEach(() => { + // Create mock directory structure + const adminV2Dir = path.join(mockTempDir, 'vbuild/openapi/proto/redpanda/core/admin/v2'); + const commonDir = path.join(mockTempDir, 'vbuild/openapi/proto/redpanda/core/common'); + + fs.mkdirSync(adminV2Dir, { recursive: true }); + fs.mkdirSync(commonDir, { recursive: true }); + + // Create mock fragment files + fs.writeFileSync(path.join(adminV2Dir, 'broker.openapi.yaml'), 'openapi: 3.1.0\ninfo:\n title: Broker'); + fs.writeFileSync(path.join(adminV2Dir, 'cluster.openapi.yaml'), 'openapi: 3.1.0\ninfo:\n title: Cluster'); + fs.writeFileSync(path.join(commonDir, 'common.openapi.yaml'), 'openapi: 3.1.0\ninfo:\n title: Common'); + }); + + test('should find and return fragment files for admin surface', () => { + const fragments = createEntrypoint(mockTempDir, 'admin', true); + + expect(fragments).toHaveLength(3); + expect(fragments.some(f => f.includes('broker.openapi.yaml'))).toBe(true); + expect(fragments.some(f => f.includes('cluster.openapi.yaml'))).toBe(true); + expect(fragments.some(f => f.includes('common.openapi.yaml'))).toBe(true); + }); + + test('should validate input parameters', () => { + expect(() => createEntrypoint('', 'admin')).toThrow('Invalid temporary directory'); + expect(() => createEntrypoint('/nonexistent', 'admin')).toThrow('Invalid temporary directory'); + expect(() => createEntrypoint(mockTempDir, 'invalid')).toThrow('Invalid API surface'); + expect(() => createEntrypoint(mockTempDir, '')).toThrow('Invalid API surface'); + expect(() => createEntrypoint(mockTempDir, null)).toThrow('Invalid API surface'); + }); + + test('should throw error when no fragments found', () => { + const emptyTempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'empty-')); + + try { + expect(() => createEntrypoint(emptyTempDir, 'admin')).toThrow(/No OpenAPI fragments found to bundle/); + } finally { + fs.rmSync(emptyTempDir, { recursive: true, force: true }); + } + }); + + test('should filter out non-openapi files', () => { + const adminV2Dir = path.join(mockTempDir, 'vbuild/openapi/proto/redpanda/core/admin/v2'); + + // Add non-openapi files + fs.writeFileSync(path.join(adminV2Dir, 'readme.txt'), 'Not an OpenAPI file'); + fs.writeFileSync(path.join(adminV2Dir, 'config.yaml'), 'Not an OpenAPI file'); + fs.writeFileSync(path.join(adminV2Dir, 'test.openapi.json'), 'Wrong extension'); + + const fragments = createEntrypoint(mockTempDir, 'admin', true); + + expect(fragments).toHaveLength(3); // Still only the 3 .openapi.yaml files + expect(fragments.every(f => f.endsWith('.openapi.yaml'))).toBe(true); + }); + + test('should handle directory read errors gracefully', () => { + // Mock fs.readdirSync to throw an error + const originalReaddirSync = fs.readdirSync; + fs.readdirSync = jest.fn().mockImplementation(() => { + throw new Error('Permission denied'); + }); + + try { + expect(() => createEntrypoint(mockTempDir, 'admin')).toThrow(/Failed to read fragment directories/); + } finally { + fs.readdirSync = originalReaddirSync; + } + }); + + test('should verify files actually exist', () => { + const adminV2Dir = path.join(mockTempDir, 'vbuild/openapi/proto/redpanda/core/admin/v2'); + + // Create a directory that looks like a file + fs.mkdirSync(path.join(adminV2Dir, 'fake.openapi.yaml')); + + const fragments = createEntrypoint(mockTempDir, 'admin', true); + + // Should find only the 3 real files (2 from admin/v2 + 1 from common), not the fake directory + expect(fragments).toHaveLength(3); + expect(fragments.every(f => fs.statSync(f).isFile())).toBe(true); + }); + }); + }); + + describe('Post-processing', () => { + describe('postProcessBundle', () => { + let testBundleFile; + + beforeEach(() => { + testBundleFile = path.join(mockTempDir, 'test-bundle.yaml'); + + // Create a test bundle file + const testContent = ` +openapi: 3.1.0 +info: + title: Test API + version: 1.0.0 +paths: + /test: + get: + summary: Test endpoint +components: + schemas: + TestSchema: + type: object +`.trim(); + + fs.writeFileSync(testBundleFile, testContent); + }); + + test('should process bundle file successfully', () => { + const options = { + surface: 'admin', + normalizedTag: '25.2.4', + majorMinor: '25.2', + adminMajor: 'v2' + }; + + const result = postProcessBundle(testBundleFile, options); + + expect(result).toBeDefined(); + expect(result.openapi).toBe('3.1.0'); + expect(result.info.title).toBe('Redpanda Admin API'); + expect(result.info.version).toBe('25.2'); + expect(result.info['x-redpanda-core-version']).toBe('25.2.4'); + expect(result.info['x-admin-api-major']).toBe('v2'); + expect(result.info['x-generated-at']).toBeDefined(); + expect(result.info['x-generator']).toBe('redpanda-docs-openapi-bundler'); + }); + + test('should validate input parameters', () => { + expect(() => postProcessBundle('', {})).toThrow('Bundle file not found'); + expect(() => postProcessBundle('/nonexistent', {})).toThrow('Bundle file not found'); + + const options = { surface: 'admin' }; + expect(() => postProcessBundle(testBundleFile, options)).toThrow('Missing required options'); + }); + + test('should handle empty bundle file', () => { + const emptyFile = path.join(mockTempDir, 'empty.yaml'); + fs.writeFileSync(emptyFile, ''); + + const options = { + surface: 'admin', + normalizedTag: '25.2.4', + majorMinor: '25.2' + }; + + expect(() => postProcessBundle(emptyFile, options)).toThrow('Bundle file is empty'); + }); + + test('should handle invalid YAML', () => { + const invalidFile = path.join(mockTempDir, 'invalid.yaml'); + fs.writeFileSync(invalidFile, 'invalid: yaml: content: ['); + + const options = { + surface: 'admin', + normalizedTag: '25.2.4', + majorMinor: '25.2' + }; + + expect(() => postProcessBundle(invalidFile, options)).toThrow('Post-processing failed'); + }); + + test('should handle connect surface', () => { + const options = { + surface: 'connect', + normalizedTag: 'dev', + majorMinor: 'dev' + }; + + const result = postProcessBundle(testBundleFile, options); + + expect(result.info.title).toBe('Redpanda Connect RPCs'); + }); + + test('should sort keys deterministically', () => { + const options = { + surface: 'admin', + normalizedTag: '25.2.4', + majorMinor: '25.2' + }; + + const result = postProcessBundle(testBundleFile, options); + const keys = Object.keys(result); + + // Check that keys are sorted (components before paths, etc.) + expect(keys.indexOf('components')).toBeLessThan(keys.indexOf('paths')); + expect(keys.indexOf('info')).toBeLessThan(keys.indexOf('paths')); + }); + }); + }); + + describe('Error Handling & Edge Cases', () => { + test('should handle various error scenarios gracefully', () => { + // Test that functions don't crash on unexpected inputs + expect(() => sortObjectKeys(new Date())).not.toThrow(); + expect(() => sortObjectKeys(Symbol('test'))).not.toThrow(); + + // Test edge cases for version functions + expect(() => normalizeTag('0.0.0')).not.toThrow(); + expect(normalizeTag('0.0.0')).toBe('0.0.0'); + + expect(() => getMajorMinor('0.0.0')).not.toThrow(); + expect(getMajorMinor('0.0.0')).toBe('0.0'); + }); + + test('should provide helpful error messages', () => { + const errorTests = [ + { fn: () => normalizeTag('invalid'), pattern: /Invalid version format.*Expected format like/ }, + { fn: () => getMajorMinor('1'), pattern: /Expected X\.Y\.Z format/ }, + { fn: () => createEntrypoint('', 'admin'), pattern: /Invalid temporary directory/ }, + { fn: () => createEntrypoint('/tmp', 'invalid'), pattern: /Invalid API surface/ } + ]; + + errorTests.forEach(({ fn, pattern }) => { + expect(fn).toThrow(pattern); + }); + }); + }); + + describe('Integration Tests', () => { + test('version handling integration', () => { + const testCases = [ + { input: 'v25.2.4', expectedNormalized: '25.2.4', expectedMajorMinor: '25.2' }, + { input: '24.3.2', expectedNormalized: '24.3.2', expectedMajorMinor: '24.3' }, + { input: 'dev', expectedNormalized: 'dev', expectedMajorMinor: 'dev' }, + { input: 'v1.0.0-rc1', expectedNormalized: '1.0.0-rc1', expectedMajorMinor: '1.0' } + ]; + + testCases.forEach(({ input, expectedNormalized, expectedMajorMinor }) => { + const normalized = normalizeTag(input); + const majorMinor = getMajorMinor(normalized); + + expect(normalized).toBe(expectedNormalized); + expect(majorMinor).toBe(expectedMajorMinor); + }); + }); + }); +}); + +// Performance tests +describe('Performance Tests', () => { + test('sortObjectKeys should handle large objects efficiently', () => { + const largeObject = {}; + for (let i = 1000; i >= 0; i--) { + largeObject[`key${i}`] = { nested: { value: i } }; + } + + const start = Date.now(); + const result = sortObjectKeys(largeObject); + const duration = Date.now() - start; + + expect(duration).toBeLessThan(1000); // Should complete within 1 second + expect(Object.keys(result)[0]).toBe('key0'); + expect(Object.keys(result)[1000]).toBe('key999'); + }); +}); + +// CLI Integration tests (if node is available) +describe('CLI Integration Tests', () => { + test('should show version correctly', () => { + try { + const result = execSync.mockReturnValue('4.9.1'); + // Test would run actual CLI in real environment + expect(result).toBeDefined(); + } catch (error) { + // Skip if CLI not available in test environment + console.warn('CLI test skipped:', error.message); + } + }); +}); diff --git a/admin/redpanda-admin-api.yaml b/admin/redpanda-admin-api.yaml new file mode 100644 index 0000000..cddd246 --- /dev/null +++ b/admin/redpanda-admin-api.yaml @@ -0,0 +1,1580 @@ +components: + schemas: + connect-protocol-version: + const: 1 + description: Define the version of the Connect protocol + enum: + - 1 + title: Connect-Protocol-Version + type: number + connect-timeout-header: + description: Define the timeout, in ms + title: Connect-Timeout-Ms + type: number + connect.error: + additionalProperties: true + description: "Error type returned by Connect: https://connectrpc.com/docs/go/errors/#http-representation" + properties: + code: + description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. + enum: + - canceled + - unknown + - invalid_argument + - deadline_exceeded + - not_found + - already_exists + - permission_denied + - resource_exhausted + - failed_precondition + - aborted + - out_of_range + - unimplemented + - internal + - unavailable + - data_loss + - unauthenticated + examples: + - not_found + type: string + details: + description: A list of messages that carry the error details. There is no limit on the number of messages. + items: + $ref: "#/components/schemas/connect.error_details.Any" + type: array + message: + description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. + type: string + title: Connect Error + type: object + connect.error_details.Any: + additionalProperties: true + description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message, with an additional debug field for ConnectRPC error details. + properties: + debug: + description: Deserialized error detail payload. The 'type' field indicates the schema. This field is for easier debugging and should not be relied upon for application logic. + discriminator: + propertyName: type + oneOf: + - additionalProperties: true + description: Detailed error information. + title: Any + type: object + title: Debug + type: + description: "A URL that acts as a globally unique identifier for the type of the serialized message. For example: `type.googleapis.com/google.rpc.ErrorInfo`. This is used to determine the schema of the data in the `value` field and is the discriminator for the `debug` field." + type: string + value: + description: The Protobuf message, serialized as bytes and base64-encoded. The specific message type is identified by the `type` field. + format: binary + type: string + type: object + google.protobuf.Duration: + description: |- + A Duration represents a signed, fixed-length span of time represented + as a count of seconds and fractions of seconds at nanosecond + resolution. It is independent of any calendar and concepts like "day" + or "month". It is related to Timestamp in that the difference between + two Timestamp values is a Duration and it can be added or subtracted + from a Timestamp. Range is approximately +-10,000 years. + + # Examples + + Example 1: Compute Duration from two Timestamps in pseudo code. + + Timestamp start = ...; + Timestamp end = ...; + Duration duration = ...; + + duration.seconds = end.seconds - start.seconds; + duration.nanos = end.nanos - start.nanos; + + if (duration.seconds < 0 && duration.nanos > 0) { + duration.seconds += 1; + duration.nanos -= 1000000000; + } else if (duration.seconds > 0 && duration.nanos < 0) { + duration.seconds -= 1; + duration.nanos += 1000000000; + } + + Example 2: Compute Timestamp from Timestamp + Duration in pseudo code. + + Timestamp start = ...; + Duration duration = ...; + Timestamp end = ...; + + end.seconds = start.seconds + duration.seconds; + end.nanos = start.nanos + duration.nanos; + + if (end.nanos < 0) { + end.seconds -= 1; + end.nanos += 1000000000; + } else if (end.nanos >= 1000000000) { + end.seconds += 1; + end.nanos -= 1000000000; + } + + Example 3: Compute Duration from datetime.timedelta in Python. + + td = datetime.timedelta(days=3, minutes=10) + duration = Duration() + duration.FromTimedelta(td) + + # JSON Mapping + + In JSON format, the Duration type is encoded as a string rather than an + object, where the string ends in the suffix "s" (indicating seconds) and + is preceded by the number of seconds, with nanoseconds expressed as + fractional seconds. For example, 3 seconds with 0 nanoseconds should be + encoded in JSON format as "3s", while 3 seconds and 1 nanosecond should + be expressed in JSON format as "3.000000001s", and 3 seconds and 1 + microsecond should be expressed in JSON format as "3.000001s". + format: duration + type: string + google.protobuf.FieldMask: + description: |- + `FieldMask` represents a set of symbolic field paths, for example: + + paths: "f.a" + paths: "f.b.d" + + Here `f` represents a field in some root message, `a` and `b` + fields in the message found in `f`, and `d` a field found in the + message in `f.b`. + + Field masks are used to specify a subset of fields that should be + returned by a get operation or modified by an update operation. + Field masks also have a custom JSON encoding (see below). + + # Field Masks in Projections + + When used in the context of a projection, a response message or + sub-message is filtered by the API to only contain those fields as + specified in the mask. For example, if the mask in the previous + example is applied to a response message as follows: + + f { + a : 22 + b { + d : 1 + x : 2 + } + y : 13 + } + z: 8 + + The result will not contain specific values for fields x,y and z + (their value will be set to the default, and omitted in proto text + output): + + + f { + a : 22 + b { + d : 1 + } + } + + A repeated field is not allowed except at the last position of a + paths string. + + If a FieldMask object is not present in a get operation, the + operation applies to all fields (as if a FieldMask of all fields + had been specified). + + Note that a field mask does not necessarily apply to the + top-level response message. In case of a REST get operation, the + field mask applies directly to the response, but in case of a REST + list operation, the mask instead applies to each individual message + in the returned resource list. In case of a REST custom method, + other definitions may be used. Where the mask applies will be + clearly documented together with its declaration in the API. In + any case, the effect on the returned resource/resources is required + behavior for APIs. + + # Field Masks in Update Operations + + A field mask in update operations specifies which fields of the + targeted resource are going to be updated. The API is required + to only change the values of the fields as specified in the mask + and leave the others untouched. If a resource is passed in to + describe the updated values, the API ignores the values of all + fields not covered by the mask. + + If a repeated field is specified for an update operation, new values will + be appended to the existing repeated field in the target resource. Note that + a repeated field is only allowed in the last position of a `paths` string. + + If a sub-message is specified in the last position of the field mask for an + update operation, then new value will be merged into the existing sub-message + in the target resource. + + For example, given the target message: + + f { + b { + d: 1 + x: 2 + } + c: [1] + } + + And an update message: + + f { + b { + d: 10 + } + c: [2] + } + + then if the field mask is: + + paths: ["f.b", "f.c"] + + then the result will be: + + f { + b { + d: 10 + x: 2 + } + c: [1, 2] + } + + An implementation may provide options to override this default behavior for + repeated and message fields. + + In order to reset a field's value to the default, the field must + be in the mask and set to the default value in the provided resource. + Hence, in order to reset all fields of a resource, provide a default + instance of the resource and set all fields in the mask, or do + not provide a mask as described below. + + If a field mask is not present on update, the operation applies to + all fields (as if a field mask of all fields has been specified). + Note that in the presence of schema evolution, this may mean that + fields the client does not know and has therefore not filled into + the request will be reset to their default. If this is unwanted + behavior, a specific service may require a client to always specify + a field mask, producing an error if not. + + As with get operations, the location of the resource which + describes the updated values in the request message depends on the + operation kind. In any case, the effect of the field mask is + required to be honored by the API. + + ## Considerations for HTTP REST + + The HTTP kind of an update operation which uses a field mask must + be set to PATCH instead of PUT in order to satisfy HTTP semantics + (PUT must only be used for full updates). + + # JSON Encoding of Field Masks + + In JSON, a field mask is encoded as a single string where paths are + separated by a comma. Fields name in each path are converted + to/from lower-camel naming conventions. + + As an example, consider the following message declarations: + + message Profile { + User user = 1; + Photo photo = 2; + } + message User { + string display_name = 1; + string address = 2; + } + + In proto a field mask for `Profile` may look as such: + + mask { + paths: "user.display_name" + paths: "photo" + } + + In JSON, the same mask is represented as below: + + { + mask: "user.displayName,photo" + } + + # Field Masks and Oneof Fields + + Field masks treat fields in oneofs just as regular fields. Consider the + following message: + + message SampleMessage { + oneof test_oneof { + string name = 4; + SubMessage sub_message = 9; + } + } + + The field mask can be: + + mask { + paths: "name" + } + + Or: + + mask { + paths: "sub_message" + } + + Note that oneof type names ("test_oneof" in this case) cannot be used in + paths. + + ## Field Mask Verification + + The implementation of any API method which has a FieldMask type field in the + request should verify the included field paths, and return an + `INVALID_ARGUMENT` error if any path is unmappable. + type: string + google.protobuf.Timestamp: + description: |- + A Timestamp represents a point in time independent of any time zone or local + calendar, encoded as a count of seconds and fractions of seconds at + nanosecond resolution. The count is relative to an epoch at UTC midnight on + January 1, 1970, in the proleptic Gregorian calendar which extends the + Gregorian calendar backwards to year one. + + All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap + second table is needed for interpretation, using a [24-hour linear + smear](https://developers.google.com/time/smear). + + The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By + restricting to that range, we ensure that we can convert to and from [RFC + 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings. + + # Examples + + Example 1: Compute Timestamp from POSIX `time()`. + + Timestamp timestamp; + timestamp.set_seconds(time(NULL)); + timestamp.set_nanos(0); + + Example 2: Compute Timestamp from POSIX `gettimeofday()`. + + struct timeval tv; + gettimeofday(&tv, NULL); + + Timestamp timestamp; + timestamp.set_seconds(tv.tv_sec); + timestamp.set_nanos(tv.tv_usec * 1000); + + Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`. + + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime; + + // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z + // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z. + Timestamp timestamp; + timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL)); + timestamp.set_nanos((INT32) ((ticks % 10000000) * 100)); + + Example 4: Compute Timestamp from Java `System.currentTimeMillis()`. + + long millis = System.currentTimeMillis(); + + Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000) + .setNanos((int) ((millis % 1000) * 1000000)).build(); + + Example 5: Compute Timestamp from Java `Instant.now()`. + + Instant now = Instant.now(); + + Timestamp timestamp = + Timestamp.newBuilder().setSeconds(now.getEpochSecond()) + .setNanos(now.getNano()).build(); + + Example 6: Compute Timestamp from current time in Python. + + timestamp = Timestamp() + timestamp.GetCurrentTime() + + # JSON Mapping + + In JSON format, the Timestamp type is encoded as a string in the + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the + format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" + where {year} is always expressed using four digits while {month}, {day}, + {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional + seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution), + are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone + is required. A proto3 JSON serializer should always use UTC (as indicated by + "Z") when printing the Timestamp type and a proto3 JSON parser should be + able to accept both UTC and other timezones (as indicated by an offset). + + For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past + 01:30 UTC on January 15, 2017. + + In JavaScript, one can convert a Date object to this format using the + standard + [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) + method. In Python, a standard `datetime.datetime` object can be converted + to this format using + [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with + the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use + the Joda Time's [`ISODateTimeFormat.dateTime()`]( + http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime() + ) to obtain a formatter capable of generating timestamps in this format. + examples: + - 2023-01-15T01:30:15.01Z + - 2024-12-25T12:00:00Z + format: date-time + type: string + redpanda.core.admin.v2.ACLAccessFilter: + additionalProperties: false + description: Filter an ACL based on its access + properties: + host: + description: |- + The host to match. If not set, will default to match all hosts + with the specified `operation` and `permission_type`. Note that + the asterisk `*` is literal and matches hosts that are set to `*` + title: host + type: string + operation: + $ref: "#/components/schemas/redpanda.core.common.ACLOperation" + description: The ACL operation to match + title: operation + permissionType: + $ref: "#/components/schemas/redpanda.core.common.ACLPermissionType" + description: The permission type + title: permission_type + principal: + description: |- + The name of the principal, if not set will default to match + all principals with the specified `operation` and `permission_type` + title: principal + type: string + title: ACLAccessFilter + type: object + redpanda.core.admin.v2.ACLFilter: + additionalProperties: false + description: A filter for ACLs + properties: + accessFilter: + $ref: "#/components/schemas/redpanda.core.admin.v2.ACLAccessFilter" + description: The access filter + title: access_filter + resourceFilter: + $ref: "#/components/schemas/redpanda.core.admin.v2.ACLResourceFilter" + description: The resource filter + title: resource_filter + title: ACLFilter + type: object + redpanda.core.admin.v2.ACLResourceFilter: + additionalProperties: false + description: A filter to match ACLs for resources + properties: + name: + description: |- + Name, if not given will default to match all items in `resource_type`. + Note that asterisk `*` is literal and matches resource ACLs + that are named `*` + title: name + type: string + patternType: + $ref: "#/components/schemas/redpanda.core.common.ACLPattern" + description: The pattern to apply to name + title: pattern_type + resourceType: + $ref: "#/components/schemas/redpanda.core.common.ACLResource" + description: The ACL resource type to match + title: resource_type + title: ACLResourceFilter + type: object + redpanda.core.admin.v2.AdminServer: + additionalProperties: false + description: AdminServer has information about the admin server within the broker. + properties: + routes: + description: All of the ConnectRPC routes available on this admin server. + items: + $ref: "#/components/schemas/redpanda.core.admin.v2.RPCRoute" + title: routes + type: array + title: AdminServer + type: object + redpanda.core.admin.v2.AuthenticationConfiguration: + additionalProperties: false + description: |- + Authentication config. Currently only supporting SASL/SCRAM, + however made as a oneof for expansion + oneOf: + - properties: + scramConfiguration: + $ref: "#/components/schemas/redpanda.core.admin.v2.ScramConfig" + description: SASL/SCRAM configuration + title: scram_configuration + required: + - scramConfiguration + title: scram_configuration + title: AuthenticationConfiguration + type: object + redpanda.core.admin.v2.Broker: + additionalProperties: false + description: The resource for an individual broker within the Kafka Cluster. + properties: + adminServer: + $ref: "#/components/schemas/redpanda.core.admin.v2.AdminServer" + description: The admin server information. + title: admin_server + buildInfo: + $ref: "#/components/schemas/redpanda.core.admin.v2.BuildInfo" + description: The build this broker is running. + title: build_info + nodeId: + description: This broker's node ID. + format: int32 + title: node_id + type: integer + title: Broker + type: object + redpanda.core.admin.v2.BuildInfo: + additionalProperties: false + description: BuildInfo contains information about the Redpanda build. + properties: + buildSha: + description: The git commit SHA of the build. + title: build_sha + type: string + version: + description: A version string of Redpanda like "v25.2.1" + title: version + type: string + title: BuildInfo + type: object + redpanda.core.admin.v2.ConsumerOffsetSyncOptions: + additionalProperties: false + description: Options for syncing consumer offsets + properties: + enabled: + description: Whether it's enabled + title: enabled + type: boolean + groupFilters: + description: The filters + items: + $ref: "#/components/schemas/redpanda.core.admin.v2.NameFilter" + title: group_filters + type: array + interval: + $ref: "#/components/schemas/google.protobuf.Duration" + description: |- + Sync interval + If 0 provided, defaults to 30 seconds + title: interval + title: ConsumerOffsetSyncOptions + type: object + redpanda.core.admin.v2.CreateShadowLinkRequest: + additionalProperties: false + description: Create a new shadow link + properties: + shadowLink: + $ref: "#/components/schemas/redpanda.core.admin.v2.ShadowLink" + description: The shadow link to create + title: shadow_link + title: CreateShadowLinkRequest + type: object + redpanda.core.admin.v2.CreateShadowLinkResponse: + additionalProperties: false + description: Response to creating a shadow link + properties: + shadowLink: + $ref: "#/components/schemas/redpanda.core.admin.v2.ShadowLink" + description: The shadow link that was created + title: shadow_link + title: CreateShadowLinkResponse + type: object + redpanda.core.admin.v2.DeleteShadowLinkRequest: + additionalProperties: false + description: Request to delete a shadow link + properties: + name: + description: The name of the link to delete + title: name + type: string + required: + - name + title: DeleteShadowLinkRequest + type: object + redpanda.core.admin.v2.DeleteShadowLinkResponse: + additionalProperties: false + description: Response to deleting a shadow link + title: DeleteShadowLinkResponse + type: object + redpanda.core.admin.v2.FailOverRequest: + additionalProperties: false + description: Request to fail over a shadow link or a single shadow topic + properties: + name: + description: The name of the shadow link to fail over + title: name + type: string + shadowTopicName: + description: |- + (OPTIONAL) The name of the shadow topic to fail over, if not set will fail over + the entire shadow link + title: shadow_topic_name + type: string + required: + - name + title: FailOverRequest + type: object + redpanda.core.admin.v2.FailOverResponse: + additionalProperties: false + description: The response to the FailOverRequest + properties: + shadowLink: + $ref: "#/components/schemas/redpanda.core.admin.v2.ShadowLink" + description: The shadow link that was failed over + title: shadow_link + title: FailOverResponse + type: object + redpanda.core.admin.v2.FilterType: + description: What type of filter this is, include or exclude + enum: + - FILTER_TYPE_UNSPECIFIED + - FILTER_TYPE_INCLUDE + - FILTER_TYPE_EXCLUDE + title: FilterType + type: string + redpanda.core.admin.v2.GetBrokerRequest: + additionalProperties: false + description: GetBrokerRequest returns information about a single broker in the cluster + properties: + nodeId: + description: |- + The node ID for the broker. If set to -1 then the broker handling the RPC + request will response with it's own information. + format: int32 + title: node_id + type: integer + title: GetBrokerRequest + type: object + redpanda.core.admin.v2.GetBrokerResponse: + additionalProperties: false + description: GetBrokerResponse is the response from the GetBroker RPC. + properties: + broker: + $ref: "#/components/schemas/redpanda.core.admin.v2.Broker" + description: The specified broker and it's information. + title: broker + title: GetBrokerResponse + type: object + redpanda.core.admin.v2.GetShadowLinkRequest: + additionalProperties: false + description: Request to get the information about a shadow link + properties: + name: + description: The name of the shadow link to get + title: name + type: string + required: + - name + title: GetShadowLinkRequest + type: object + redpanda.core.admin.v2.GetShadowLinkResponse: + additionalProperties: false + description: Response to getting a shadow link + properties: + shadowLink: + $ref: "#/components/schemas/redpanda.core.admin.v2.ShadowLink" + description: The shadow link that was retrieved + title: shadow_link + title: GetShadowLinkResponse + type: object + redpanda.core.admin.v2.ListBrokersRequest: + additionalProperties: false + description: ListBrokersRequest returns information about all the brokers in the cluster + title: ListBrokersRequest + type: object + redpanda.core.admin.v2.ListBrokersResponse: + additionalProperties: false + description: ListBrokersResponse is the response from the ListBrokers RPC. + properties: + brokers: + description: The brokers in the cluster and their information. + items: + $ref: "#/components/schemas/redpanda.core.admin.v2.Broker" + title: brokers + type: array + title: ListBrokersResponse + type: object + redpanda.core.admin.v2.ListShadowLinksRequest: + additionalProperties: false + description: Request to list all shadow links + title: ListShadowLinksRequest + type: object + redpanda.core.admin.v2.ListShadowLinksResponse: + additionalProperties: false + description: All shadow links on the cluster + properties: + shadowLinks: + description: The shadow links + items: + $ref: "#/components/schemas/redpanda.core.admin.v2.ShadowLink" + title: shadow_links + type: array + title: ListShadowLinksResponse + type: object + redpanda.core.admin.v2.NameFilter: + additionalProperties: false + description: A filter based on the name of a resource + properties: + filterType: + $ref: "#/components/schemas/redpanda.core.admin.v2.FilterType" + description: Include or exclude + title: filter_type + name: + description: |- + The resource name, or "*" + Note if "*", must be the _only_ character + and `pattern_type` must be `PATTERN_TYPE_LITERAL` + title: name + type: string + patternType: + $ref: "#/components/schemas/redpanda.core.admin.v2.PatternType" + description: Literal or prefix + title: pattern_type + title: NameFilter + type: object + redpanda.core.admin.v2.PatternType: + description: The matching pattern type + enum: + - PATTERN_TYPE_UNSPECIFIED + - PATTERN_TYPE_LITERAL + - PATTERN_TYPE_PREFIX + title: PatternType + type: string + redpanda.core.admin.v2.RPCRoute: + additionalProperties: false + description: A route in the Admin API RPC server. + properties: + httpRoute: + description: |- + The HTTP route for this RPC method. + + For example `/redpanda.core.admin.v2.AdminService/GetVersion` + title: http_route + type: string + name: + description: |- + The name of the RPC method. + + For example `redpanda.core.admin.v2.AdminService.GetVersion` + title: name + type: string + title: RPCRoute + type: object + redpanda.core.admin.v2.ScramConfig: + additionalProperties: false + description: SCRAM settings + properties: + password: + description: Password + title: password + type: string + writeOnly: true + passwordSet: + description: Indicates that the password has been set + readOnly: true + title: password_set + type: boolean + passwordSetAt: + $ref: "#/components/schemas/google.protobuf.Timestamp" + description: |- + Timestamp of when the password was last set - only valid if password_set + is true + readOnly: true + title: password_set_at + scramMechanism: + $ref: "#/components/schemas/redpanda.core.admin.v2.ScramMechanism" + description: The SCRAM mechanism to use + title: scram_mechanism + username: + description: SCRAM username + title: username + type: string + title: ScramConfig + type: object + redpanda.core.admin.v2.ScramMechanism: + description: Valid SCRAM mechanisms + enum: + - SCRAM_MECHANISM_UNSPECIFIED + - SCRAM_MECHANISM_SCRAM_SHA_256 + - SCRAM_MECHANISM_SCRAM_SHA_512 + title: ScramMechanism + type: string + redpanda.core.admin.v2.SecuritySettingsSyncOptions: + additionalProperties: false + description: Options for syncing security settings + properties: + aclFilters: + description: ACL filters + items: + $ref: "#/components/schemas/redpanda.core.admin.v2.ACLFilter" + title: acl_filters + type: array + enabled: + description: Whether or not it's enabled + title: enabled + type: boolean + interval: + $ref: "#/components/schemas/google.protobuf.Duration" + description: |- + Sync interval + If 0 provided, defaults to 30 seconds + title: interval + roleFilters: + description: Role filters + items: + $ref: "#/components/schemas/redpanda.core.admin.v2.NameFilter" + title: role_filters + type: array + scramCredFilters: + description: SCRAM credential filters + items: + $ref: "#/components/schemas/redpanda.core.admin.v2.NameFilter" + title: scram_cred_filters + type: array + title: SecuritySettingsSyncOptions + type: object + redpanda.core.admin.v2.ShadowLink: + additionalProperties: false + description: A ShadowLink resource + properties: + configurations: + $ref: "#/components/schemas/redpanda.core.admin.v2.ShadowLinkConfigurations" + description: Shadow link configuration + title: configurations + name: + description: The name of the shadow link + title: name + type: string + status: + $ref: "#/components/schemas/redpanda.core.admin.v2.ShadowLinkStatus" + description: Status of the shadow link + readOnly: true + title: status + uid: + description: The UUID of the shadow link + readOnly: true + title: uid + type: string + required: + - name + title: ShadowLink + type: object + redpanda.core.admin.v2.ShadowLinkClientOptions: + additionalProperties: false + description: Options for the client link + properties: + authenticationConfiguration: + $ref: "#/components/schemas/redpanda.core.admin.v2.AuthenticationConfiguration" + description: Authentication settings + nullable: true + title: authentication_configuration + bootstrapServers: + description: The bootstrap servers to use + items: + type: string + title: bootstrap_servers + type: array + clientId: + description: |- + The Client ID for the Kafka RPC requests setn by this cluster to the + source cluster + readOnly: true + title: client_id + type: string + connectionTimeoutMs: + description: |- + Connection timeout + If 0 is provided, defaults to 1 second + format: int32 + title: connection_timeout_ms + type: integer + fetchMaxBytes: + description: |- + Fetch max bytes + If 0 is provided, defaults to 1MiB + format: int32 + title: fetch_max_bytes + type: integer + fetchMinBytes: + description: |- + Fetch min bytes + If 0 is provided, defaults to 1 byte + format: int32 + title: fetch_min_bytes + type: integer + fetchWaitMaxMs: + description: |- + Fetch request timeout + If 0 is provided, defaults to 100ms + format: int32 + title: fetch_wait_max_ms + type: integer + metadataMaxAgeMs: + description: |- + Max metadata age + If 0 is provided, defaults to 10 seconds + format: int32 + title: metadata_max_age_ms + type: integer + retryBackoffMs: + description: |- + Retry base backoff + If 0 is provided, defaults to 100ms + format: int32 + title: retry_backoff_ms + type: integer + sourceClusterId: + description: |- + If provided, this is the expected ID of the source cluster. If it does + not match then the connection will be rejected. If provided, this value + must match the `ClusterId` field returned in the Kafka Metadata response + message + title: source_cluster_id + type: string + tlsSettings: + $ref: "#/components/schemas/redpanda.core.admin.v2.TLSSettings" + description: TLS settings + nullable: true + title: tls_settings + required: + - bootstrapServers + title: ShadowLinkClientOptions + type: object + redpanda.core.admin.v2.ShadowLinkConfigurations: + additionalProperties: false + description: ShadowLink options + properties: + clientOptions: + $ref: "#/components/schemas/redpanda.core.admin.v2.ShadowLinkClientOptions" + description: Configuration for the internal kafka client + title: client_options + consumerOffsetSyncOptions: + $ref: "#/components/schemas/redpanda.core.admin.v2.ConsumerOffsetSyncOptions" + description: Consumer offset sync options + title: consumer_offset_sync_options + securitySyncOptions: + $ref: "#/components/schemas/redpanda.core.admin.v2.SecuritySettingsSyncOptions" + description: Security settings sync options + title: security_sync_options + topicMetadataSyncOptions: + $ref: "#/components/schemas/redpanda.core.admin.v2.TopicMetadataSyncOptions" + description: Topic metadata sync options + title: topic_metadata_sync_options + title: ShadowLinkConfigurations + type: object + redpanda.core.admin.v2.ShadowLinkState: + description: State of the shadow link + enum: + - SHADOW_LINK_STATE_UNSPECIFIED + - SHADOW_LINK_STATE_ACTIVE + - SHADOW_LINK_STATE_PAUSED + - SHADOW_LINK_STATE_FAILING_OVER + - SHADOW_LINK_STATE_FAILED_OVER + title: ShadowLinkState + type: string + redpanda.core.admin.v2.ShadowLinkStatus: + additionalProperties: false + description: Status of the shadow link + properties: + shadowTopicStatuses: + description: Status of shadow topics + items: + $ref: "#/components/schemas/redpanda.core.admin.v2.ShadowTopicStatus" + title: shadow_topic_statuses + type: array + state: + $ref: "#/components/schemas/redpanda.core.admin.v2.ShadowLinkState" + title: state + taskStatuses: + description: Statuses of the running tasks + items: + $ref: "#/components/schemas/redpanda.core.admin.v2.ShadowLinkTaskStatus" + title: task_statuses + type: array + title: ShadowLinkStatus + type: object + redpanda.core.admin.v2.ShadowLinkTaskStatus: + additionalProperties: false + description: Status of a task + properties: + brokerId: + description: The broker the task is running on + format: int32 + title: broker_id + type: integer + name: + description: Name of the task + title: name + type: string + reason: + description: Reason for task being in state + title: reason + type: string + state: + $ref: "#/components/schemas/redpanda.core.admin.v2.TaskState" + description: State of the task + title: state + title: ShadowLinkTaskStatus + type: object + redpanda.core.admin.v2.ShadowTopicState: + description: State of a shadow topic + enum: + - SHADOW_TOPIC_STATE_UNSPECIFIED + - SHADOW_TOPIC_STATE_ACTIVE + - SHADOW_TOPIC_STATE_PROMOTED + - SHADOW_TOPIC_STATE_FAULTED + - SHADOW_TOPIC_STATE_PAUSED + title: ShadowTopicState + type: string + redpanda.core.admin.v2.ShadowTopicStatus: + additionalProperties: false + description: Status of a ShadowTopic + properties: + name: + description: Name of the shadow topic + title: name + type: string + partitionInformation: + description: List of partition information for the shadow topic + items: + $ref: "#/components/schemas/redpanda.core.admin.v2.TopicPartitionInformation" + title: partition_information + type: array + state: + $ref: "#/components/schemas/redpanda.core.admin.v2.ShadowTopicState" + description: State of the shadow topic + title: state + topicId: + description: Topic ID of the shadow topic + title: topic_id + type: string + title: ShadowTopicStatus + type: object + redpanda.core.admin.v2.TLSFileSettings: + additionalProperties: false + description: TLS file settings + properties: + caPath: + description: Path to the CA + title: ca_path + type: string + certPath: + description: Path to the cert + title: cert_path + type: string + keyPath: + description: |- + Key and Cert are optional but if one is provided, then both must be + Path to the key + title: key_path + type: string + title: TLSFileSettings + type: object + redpanda.core.admin.v2.TLSPEMSettings: + additionalProperties: false + description: Used when providing the TLS information in PEM format + properties: + ca: + description: The CA + title: ca + type: string + cert: + description: The cert + title: cert + type: string + key: + description: |- + Key and Cert are optional but if one is provided, then both must be + The key + title: key + type: string + writeOnly: true + keyFingerprint: + description: The SHA-256 of the key, in base64 format + readOnly: true + title: key_fingerprint + type: string + title: TLSPEMSettings + type: object + redpanda.core.admin.v2.TLSSettings: + additionalProperties: false + description: TLS settings + oneOf: + - properties: + tlsFileSettings: + $ref: "#/components/schemas/redpanda.core.admin.v2.TLSFileSettings" + description: Certificates and keys are provided as files + title: tls_file_settings + required: + - tlsFileSettings + title: tls_file_settings + - properties: + tlsPemSettings: + $ref: "#/components/schemas/redpanda.core.admin.v2.TLSPEMSettings" + description: Certificates and keys are provided in PEM format + title: tls_pem_settings + required: + - tlsPemSettings + title: tls_pem_settings + title: TLSSettings + type: object + redpanda.core.admin.v2.TaskState: + description: Task states + enum: + - TASK_STATE_UNSPECIFIED + - TASK_STATE_ACTIVE + - TASK_STATE_PAUSED + - TASK_STATE_LINK_UNAVAILABLE + - TASK_STATE_NOT_RUNNING + - TASK_STATE_FAULTED + title: TaskState + type: string + redpanda.core.admin.v2.TopicMetadataSyncOptions: + additionalProperties: false + description: Options for syncing topic metadata + properties: + autoCreateShadowTopicFilters: + description: |- + List of filters that indicate which topics should be automatically + created as shadow topics on the shadow cluster. This only controls + automatic creation of shadow topics and does not effect the state of the + mirror topic once it is created. + items: + $ref: "#/components/schemas/redpanda.core.admin.v2.NameFilter" + title: auto_create_shadow_topic_filters + type: array + interval: + $ref: "#/components/schemas/google.protobuf.Duration" + description: |- + How often to sync metadata + If 0 provided, defaults to 30 seconds + title: interval + shadowedTopicProperties: + description: |- + Additional topic properties to shadow + Partition count, `max.message.bytes`, `cleanup.policy` and + `timestamp.type` will always be replicated + items: + type: string + title: shadowed_topic_properties + type: array + title: TopicMetadataSyncOptions + type: object + redpanda.core.admin.v2.TopicPartitionInformation: + additionalProperties: false + description: Topic partition information + properties: + highWatermark: + description: Shadowed partition's HWM + format: int64 + title: high_watermark + type: + - integer + - string + partitionId: + description: Partition ID + format: int64 + title: partition_id + type: + - integer + - string + sourceHighWatermark: + description: Source partition's HWM + format: int64 + title: source_high_watermark + type: + - integer + - string + sourceLastStableOffset: + description: Source partition's LSO + format: int64 + title: source_last_stable_offset + type: + - integer + - string + title: TopicPartitionInformation + type: object + redpanda.core.admin.v2.UpdateShadowLinkRequest: + additionalProperties: false + description: Updates a shadow link + properties: + shadowLink: + $ref: "#/components/schemas/redpanda.core.admin.v2.ShadowLink" + description: The shadow link to update + title: shadow_link + updateMask: + $ref: "#/components/schemas/google.protobuf.FieldMask" + description: |- + The list of fields to update + See [AIP-134](https://google.aip.dev/134) for how to use `field_mask` + title: update_mask + title: UpdateShadowLinkRequest + type: object + redpanda.core.admin.v2.UpdateShadowLinkResponse: + additionalProperties: false + description: Response to the update shadow link request + properties: + shadowLink: + $ref: "#/components/schemas/redpanda.core.admin.v2.ShadowLink" + description: The shadow link that was updated + title: shadow_link + title: UpdateShadowLinkResponse + type: object + redpanda.core.common.ACLOperation: + description: / The ACL operation to match + enum: + - ACL_OPERATION_UNSPECIFIED + - ACL_OPERATION_ANY + - ACL_OPERATION_READ + - ACL_OPERATION_WRITE + - ACL_OPERATION_CREATE + - ACL_OPERATION_REMOVE + - ACL_OPERATION_ALTER + - ACL_OPERATION_DESCRIBE + - ACL_OPERATION_CLUSTER_ACTION + - ACL_OPERATION_DESCRIBE_CONFIGS + - ACL_OPERATION_ALTER_CONFIGS + - ACL_OPERATION_IDEMPOTENT_WRITE + title: ACLOperation + type: string + redpanda.core.common.ACLPattern: + description: / The ACL pattern type + enum: + - ACL_PATTERN_UNSPECIFIED + - ACL_PATTERN_ANY + - ACL_PATTERN_LITERAL + - ACL_PATTERN_PREFIXED + - ACL_PATTERN_MATCH + title: ACLPattern + type: string + redpanda.core.common.ACLPermissionType: + description: / ACL permission types + enum: + - ACL_PERMISSION_TYPE_UNSPECIFIED + - ACL_PERMISSION_TYPE_ANY + - ACL_PERMISSION_TYPE_ALLOW + - ACL_PERMISSION_TYPE_DENY + title: ACLPermissionType + type: string + redpanda.core.common.ACLResource: + description: / The ACL resource types + enum: + - ACL_RESOURCE_UNSPECIFIED + - ACL_RESOURCE_ANY + - ACL_RESOURCE_CLUSTER + - ACL_RESOURCE_GROUP + - ACL_RESOURCE_TOPIC + - ACL_RESOURCE_TXN_ID + - ACL_RESOURCE_SR_SUBJECT + - ACL_RESOURCE_SR_REGISTRY + - ACL_RESOURCE_SR_ANY + title: ACLResource + type: string +info: + description: Redpanda Admin API specification + title: Redpanda Admin API + version: v2.0.0 + x-admin-api-major: v2.0.0 + x-generated-at: 2025-10-02T09:04:11.998Z + x-generator: redpanda-docs-openapi-bundler + x-redpanda-core-version: dev +openapi: 3.1.0 +paths: + /redpanda.core.admin.v2.BrokerService/GetBroker: + post: + description: GetBroker returns information about a single broker in the cluster. + operationId: redpanda.core.admin.v2.BrokerService.GetBroker + parameters: + - in: header + name: Connect-Protocol-Version + required: true + schema: + $ref: "#/components/schemas/connect-protocol-version" + - in: header + name: Connect-Timeout-Ms + schema: + $ref: "#/components/schemas/connect-timeout-header" + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/redpanda.core.admin.v2.GetBrokerRequest" + required: true + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/redpanda.core.admin.v2.GetBrokerResponse" + description: Success + default: + content: + application/json: + schema: + $ref: "#/components/schemas/connect.error" + description: Error + summary: GetBroker returns information about a single broker in the cluster. + tags: + - redpanda.core.admin.v2.BrokerService + /redpanda.core.admin.v2.BrokerService/ListBrokers: + post: + description: ListBrokers returns information about all brokers in the cluster. + operationId: redpanda.core.admin.v2.BrokerService.ListBrokers + parameters: + - in: header + name: Connect-Protocol-Version + required: true + schema: + $ref: "#/components/schemas/connect-protocol-version" + - in: header + name: Connect-Timeout-Ms + schema: + $ref: "#/components/schemas/connect-timeout-header" + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/redpanda.core.admin.v2.ListBrokersRequest" + required: true + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/redpanda.core.admin.v2.ListBrokersResponse" + description: Success + default: + content: + application/json: + schema: + $ref: "#/components/schemas/connect.error" + description: Error + summary: ListBrokers returns information about all brokers in the cluster. + tags: + - redpanda.core.admin.v2.BrokerService + /redpanda.core.admin.v2.ShadowLinkService/CreateShadowLink: + post: + operationId: redpanda.core.admin.v2.ShadowLinkService.CreateShadowLink + parameters: + - in: header + name: Connect-Protocol-Version + required: true + schema: + $ref: "#/components/schemas/connect-protocol-version" + - in: header + name: Connect-Timeout-Ms + schema: + $ref: "#/components/schemas/connect-timeout-header" + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/redpanda.core.admin.v2.CreateShadowLinkRequest" + required: true + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/redpanda.core.admin.v2.CreateShadowLinkResponse" + description: Success + default: + content: + application/json: + schema: + $ref: "#/components/schemas/connect.error" + description: Error + summary: CreateShadowLink + tags: + - redpanda.core.admin.v2.ShadowLinkService + /redpanda.core.admin.v2.ShadowLinkService/DeleteShadowLink: + post: + operationId: redpanda.core.admin.v2.ShadowLinkService.DeleteShadowLink + parameters: + - in: header + name: Connect-Protocol-Version + required: true + schema: + $ref: "#/components/schemas/connect-protocol-version" + - in: header + name: Connect-Timeout-Ms + schema: + $ref: "#/components/schemas/connect-timeout-header" + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/redpanda.core.admin.v2.DeleteShadowLinkRequest" + required: true + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/redpanda.core.admin.v2.DeleteShadowLinkResponse" + description: Success + default: + content: + application/json: + schema: + $ref: "#/components/schemas/connect.error" + description: Error + summary: DeleteShadowLink + tags: + - redpanda.core.admin.v2.ShadowLinkService + /redpanda.core.admin.v2.ShadowLinkService/FailOver: + post: + description: Fails over a shadow link or a single shadow topic + operationId: redpanda.core.admin.v2.ShadowLinkService.FailOver + parameters: + - in: header + name: Connect-Protocol-Version + required: true + schema: + $ref: "#/components/schemas/connect-protocol-version" + - in: header + name: Connect-Timeout-Ms + schema: + $ref: "#/components/schemas/connect-timeout-header" + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/redpanda.core.admin.v2.FailOverRequest" + required: true + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/redpanda.core.admin.v2.FailOverResponse" + description: Success + default: + content: + application/json: + schema: + $ref: "#/components/schemas/connect.error" + description: Error + summary: Fails over a shadow link or a single shadow topic + tags: + - redpanda.core.admin.v2.ShadowLinkService + /redpanda.core.admin.v2.ShadowLinkService/GetShadowLink: + post: + operationId: redpanda.core.admin.v2.ShadowLinkService.GetShadowLink + parameters: + - in: header + name: Connect-Protocol-Version + required: true + schema: + $ref: "#/components/schemas/connect-protocol-version" + - in: header + name: Connect-Timeout-Ms + schema: + $ref: "#/components/schemas/connect-timeout-header" + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/redpanda.core.admin.v2.GetShadowLinkRequest" + required: true + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/redpanda.core.admin.v2.GetShadowLinkResponse" + description: Success + default: + content: + application/json: + schema: + $ref: "#/components/schemas/connect.error" + description: Error + summary: GetShadowLink + tags: + - redpanda.core.admin.v2.ShadowLinkService + /redpanda.core.admin.v2.ShadowLinkService/ListShadowLinks: + post: + operationId: redpanda.core.admin.v2.ShadowLinkService.ListShadowLinks + parameters: + - in: header + name: Connect-Protocol-Version + required: true + schema: + $ref: "#/components/schemas/connect-protocol-version" + - in: header + name: Connect-Timeout-Ms + schema: + $ref: "#/components/schemas/connect-timeout-header" + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/redpanda.core.admin.v2.ListShadowLinksRequest" + required: true + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/redpanda.core.admin.v2.ListShadowLinksResponse" + description: Success + default: + content: + application/json: + schema: + $ref: "#/components/schemas/connect.error" + description: Error + summary: ListShadowLinks + tags: + - redpanda.core.admin.v2.ShadowLinkService + /redpanda.core.admin.v2.ShadowLinkService/UpdateShadowLink: + post: + operationId: redpanda.core.admin.v2.ShadowLinkService.UpdateShadowLink + parameters: + - in: header + name: Connect-Protocol-Version + required: true + schema: + $ref: "#/components/schemas/connect-protocol-version" + - in: header + name: Connect-Timeout-Ms + schema: + $ref: "#/components/schemas/connect-timeout-header" + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/redpanda.core.admin.v2.UpdateShadowLinkRequest" + required: true + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/redpanda.core.admin.v2.UpdateShadowLinkResponse" + description: Success + default: + content: + application/json: + schema: + $ref: "#/components/schemas/connect.error" + description: Error + summary: UpdateShadowLink + tags: + - redpanda.core.admin.v2.ShadowLinkService diff --git a/bin/doc-tools.js b/bin/doc-tools.js index 95a7f3d..8bfe9dc 100755 --- a/bin/doc-tools.js +++ b/bin/doc-tools.js @@ -2,7 +2,7 @@ const { execSync, spawnSync } = require('child_process'); const os = require('os'); -const { Command } = require('commander'); +const { Command, Option } = require('commander'); const path = require('path'); const yaml = require('yaml'); const fs = require('fs'); @@ -1540,5 +1540,63 @@ automation } }); +automation + .command('bundle-admin-api') + .description('Bundle Redpanda OpenAPI fragments into complete OpenAPI 3.1 documents') + .requiredOption('-t, --tag ', 'Git tag to check out (e.g., v24.3.2 or 24.3.2 or dev)') + .option('--repo ', 'Repository URL', 'https://github.com/redpanda-data/redpanda.git') + .addOption(new Option('-s, --surface ', 'Which API surface(s) to bundle').choices(['admin', 'connect', 'both']).makeOptionMandatory()) + .option('--out-admin ', 'Output path for admin API', 'admin/redpanda-admin-api.yaml') + .option('--out-connect ', 'Output path for connect API', 'connect/redpanda-connect-api.yaml') + .option('--admin-major ', 'Admin API major version', 'v2.0.0') + .option('--use-admin-major-version', 'Use admin major version for info.version instead of git tag', false) + .option('--quiet', 'Suppress logs', false) + .action(async (options) => { + // Verify dependencies + requireCmd('git', 'Install Git: https://git-scm.com/downloads'); + requireCmd('buf', 'Install buf: https://buf.build/docs/installation'); + + // Check for OpenAPI bundler (swagger-cli or redocly) + let bundlerAvailable = false; + try { + execSync('swagger-cli --version', { stdio: 'ignore' }); + bundlerAvailable = true; + } catch { + try { + execSync('redocly --version', { stdio: 'ignore' }); + bundlerAvailable = true; + } catch { + try { + execSync('npx redocly --version', { stdio: 'ignore' }); + bundlerAvailable = true; + } catch { + fail('OpenAPI bundler not found. Install one of:\n' + + ' npm install -g swagger-cli\n' + + ' or\n' + + ' npm install -g @redocly/cli\n' + + ' or\n' + + ' npm install @redocly/cli (local)'); + } + } + } + + try { + const { bundleOpenAPI } = require('../tools/bundle-openapi.js'); + await bundleOpenAPI({ + tag: options.tag, + repo: options.repo, + surface: options.surface, + outAdmin: options.outAdmin, + outConnect: options.outConnect, + adminMajor: options.adminMajor, + useAdminMajorVersion: options.useAdminMajorVersion, + quiet: options.quiet + }); + } catch (err) { + console.error(`❌ ${err.message}`); + process.exit(err.message.includes('Validation failed') ? 2 : 1); + } + }); + programCli.addCommand(automation); programCli.parse(process.argv); diff --git a/cli-utils/install-test-dependencies.sh b/cli-utils/install-test-dependencies.sh index 0fe1861..042d6db 100755 --- a/cli-utils/install-test-dependencies.sh +++ b/cli-utils/install-test-dependencies.sh @@ -1,92 +1,414 @@ #!/bin/bash set -e +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Function to log colored output +log_info() { echo -e "${GREEN}[INFO]${NC} $1"; } +log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } +log_error() { echo -e "${RED}[ERROR]${NC} $1"; } + +# Function to check if a command exists +command_exists() { + command -v "$1" &>/dev/null +} + # Function to install Node.js install_node() { - if command -v node &>/dev/null; then - echo "Node.js is already installed. Version: $(node -v)" + local required_version="22.20.0" + + if command_exists node; then + local current_version=$(node -v | sed 's/v//') + log_info "Node.js is already installed. Version: v$current_version" + + # Check if current version meets minimum requirement + if [ "$(printf '%s\n' "$required_version" "$current_version" | sort -V | head -n1)" = "$required_version" ]; then + log_info "Node.js version meets minimum requirement (>= v$required_version)" + return 0 + else + log_warn "Node.js version v$current_version is below minimum required v$required_version" + log_warn "Please upgrade Node.js manually to v$required_version or later" + log_info "You can:" + log_info " 1. Use fnm: curl -fsSL https://fnm.vercel.app/install | bash && fnm install 22 && fnm use 22" + log_info " 2. Use nvm: nvm install 22 && nvm use 22" + log_info " 3. Use Homebrew: brew install node@22" + log_info " 4. Download from: https://nodejs.org/" + log_warn "Some CLI features (like @redocly/cli 2.2.2) require Node.js >= v22.20.0" + return 1 + fi else - echo "Installing Node.js..." - curl -fsSL https://fnm.vercel.app/install | bash || { echo "Failed to install fnm"; exit 1; } - # Load fnm into the current shell - export PATH=$HOME/.fnm:$PATH - eval "$(fnm env)" || { echo "Failed to load fnm environment"; exit 1; } - fnm install --lts || { echo "Failed to install Node.js"; exit 1; } - fnm use --lts || { echo "Failed to use Node.js"; exit 1; } - echo "Node.js version: $(node -v)" + log_warn "Node.js is not installed" + log_info "Please install Node.js v$required_version or later manually:" + log_info " 1. Use fnm: curl -fsSL https://fnm.vercel.app/install | bash && fnm install 22 && fnm use 22" + log_info " 2. Use nvm: nvm install 22 && nvm use 22" + log_info " 3. Use Homebrew: brew install node@22" + log_info " 4. Download from: https://nodejs.org/" + return 1 fi } # Function to install Rust install_rust() { - if command -v rustc &>/dev/null; then - echo "Rust is already installed. Version: $(rustc --version)" + if command_exists rustc; then + log_info "Rust is already installed. Version: $(rustc --version)" else - echo "Installing Rust..." - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y || { echo "Failed to install Rust"; exit 1; } - source $HOME/.cargo/env || { echo "Failed to load Rust environment"; exit 1; } - echo "Rust version: $(rustc --version)" + log_info "Installing Rust..." + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y || { log_error "Failed to install Rust"; exit 1; } + source $HOME/.cargo/env || { log_error "Failed to load Rust environment"; exit 1; } + log_info "Rust version: $(rustc --version)" fi } -# Function to check if expect and jq are installed and install them if they're not -ensure_dependencies_installed() { +# Function to install Git +install_git() { + if command_exists git; then + log_info "Git is already installed. Version: $(git --version)" + else + log_info "Installing Git..." + case "$(uname -s)" in + Linux) + sudo apt-get update && sudo apt-get install git -y || sudo yum install git -y || { log_error "Failed to install Git"; exit 1; } + ;; + Darwin) + if command_exists brew; then + brew install git || { log_error "Failed to install Git"; exit 1; } + else + log_error "Homebrew not found. Please install Git manually: https://git-scm.com/downloads" + exit 1 + fi + ;; + *) + log_error "Unsupported OS. Please install Git manually: https://git-scm.com/downloads" + exit 1 + ;; + esac + log_info "Git installed. Version: $(git --version)" + fi +} + +# Function to install buf (Protocol Buffer tool) +install_buf() { + if command_exists buf; then + log_info "buf is already installed. Version: $(buf --version)" + else + log_info "Installing buf..." + case "$(uname -s)" in + Linux) + # Download and install buf for Linux + BIN="/usr/local/bin/buf" + VERSION="1.28.1" + curl -sSL "https://github.com/bufbuild/buf/releases/download/v${VERSION}/buf-$(uname -s)-$(uname -m)" -o "${BIN}" || { log_error "Failed to download buf"; exit 1; } + chmod +x "${BIN}" || { log_error "Failed to make buf executable"; exit 1; } + ;; + Darwin) + if command_exists brew; then + brew install bufbuild/buf/buf || { log_error "Failed to install buf"; exit 1; } + else + log_error "Homebrew not found. Please install buf manually: https://buf.build/docs/installation" + exit 1 + fi + ;; + *) + log_error "Unsupported OS. Please install buf manually: https://buf.build/docs/installation" + exit 1 + ;; + esac + log_info "buf installed. Version: $(buf --version)" + fi +} - if ! command -v expect &> /dev/null; then - echo "Expect is not installed. Trying to install..." - missing_deps=1 +# Function to install Docker +install_docker() { + if command_exists docker; then + log_info "Docker is already installed. Version: $(docker --version)" + # Check if Docker daemon is running + if ! docker info &>/dev/null; then + log_warn "Docker is installed but daemon is not running. Please start Docker." + fi + else + log_warn "Docker is not installed. Please install Docker manually: https://docs.docker.com/get-docker/" + log_warn "Docker is required for: metrics-docs, rpk-docs commands" + fi +} - # Detect OS +# Function to install make +install_make() { + if command_exists make; then + log_info "make is already installed. Version: $(make --version | head -1)" + else + log_info "Installing make..." + case "$(uname -s)" in + Linux) + sudo apt-get update && sudo apt-get install build-essential -y || sudo yum groupinstall "Development Tools" -y || { log_error "Failed to install make"; exit 1; } + ;; + Darwin) + if ! command_exists xcode-select; then + xcode-select --install || { log_error "Failed to install Xcode Command Line Tools"; exit 1; } + fi + ;; + *) + log_error "Unsupported OS. Please install make manually." + exit 1 + ;; + esac + log_info "make installed successfully" + fi +} + +# Function to install Python3 +install_python() { + if command_exists python3; then + log_info "Python3 is already installed. Version: $(python3 --version)" + elif command_exists python; then + log_info "Python is already installed. Version: $(python --version)" + else + log_info "Installing Python3..." + case "$(uname -s)" in + Linux) + sudo apt-get update && sudo apt-get install python3 python3-pip -y || sudo yum install python3 python3-pip -y || { log_error "Failed to install Python3"; exit 1; } + ;; + Darwin) + if command_exists brew; then + brew install python || { log_error "Failed to install Python3"; exit 1; } + else + log_error "Homebrew not found. Please install Python3 manually: https://python.org" + exit 1 + fi + ;; + *) + log_error "Unsupported OS. Please install Python3 manually: https://python.org" + exit 1 + ;; + esac + log_info "Python3 installed successfully" + fi +} + +# Function to install OpenAPI bundlers +install_openapi_bundlers() { + local bundler_found=false + + # Check for swagger-cli + if command_exists swagger-cli; then + log_info "swagger-cli is already installed" + bundler_found=true + fi + + # Check for redocly + if command_exists redocly; then + log_info "redocly is already installed" + bundler_found=true + fi + + # Check for @redocly/cli via npx + if npx @redocly/cli --version &>/dev/null; then + log_info "@redocly/cli is available via npx" + bundler_found=true + fi + + if ! $bundler_found; then + log_info "Installing @redocly/cli..." + npm install -g @redocly/cli || { + log_warn "Failed to install @redocly/cli globally. Installing swagger-cli as fallback..." + npm install -g swagger-cli || { + log_error "Failed to install OpenAPI bundler. Please install manually: + npm install -g @redocly/cli + or + npm install -g swagger-cli" + exit 1 + } + } + fi +} + +# Function to install pandoc (for helm-spec command) +install_pandoc() { + if command_exists pandoc; then + log_info "pandoc is already installed. Version: $(pandoc --version | head -1)" + else + log_info "Installing pandoc..." + case "$(uname -s)" in + Linux) + sudo apt-get update && sudo apt-get install pandoc -y || sudo yum install pandoc -y || { log_error "Failed to install pandoc"; exit 1; } + ;; + Darwin) + if command_exists brew; then + brew install pandoc || { log_error "Failed to install pandoc"; exit 1; } + else + log_error "Homebrew not found. Please install pandoc manually: https://pandoc.org" + exit 1 + fi + ;; + *) + log_error "Unsupported OS. Please install pandoc manually: https://pandoc.org" + exit 1 + ;; + esac + log_info "pandoc installed successfully" + fi +} + +# Function to install helm-docs (for helm-spec command) +install_helm_docs() { + if command_exists helm-docs; then + log_info "helm-docs is already installed. Version: $(helm-docs --version)" + else + log_warn "helm-docs is not installed. Required for: helm-spec command" + log_info "Please install manually: https://github.com/norwoodj/helm-docs" + case "$(uname -s)" in + Darwin) + if command_exists brew; then + log_info "You can install with: brew install norwoodj/tap/helm-docs" + fi + ;; + esac + fi +} + +# Function to install crd-ref-docs (for crd-spec command) +install_crd_ref_docs() { + if command_exists crd-ref-docs; then + log_info "crd-ref-docs is already installed" + else + log_warn "crd-ref-docs is not installed. Required for: crd-spec command" + log_info "Please install manually: https://github.com/elastic/crd-ref-docs" + fi +} + +# Function to install Go (for crd-spec command) +install_go() { + if command_exists go; then + log_info "Go is already installed. Version: $(go version)" + else + log_warn "Go is not installed. Required for: crd-spec command" + case "$(uname -s)" in + Linux) + log_info "You can install with: sudo apt install golang-go (Ubuntu/Debian) or sudo yum install golang (RHEL/CentOS)" + ;; + Darwin) + if command_exists brew; then + log_info "You can install with: brew install go" + fi + ;; + esac + log_info "Or download from: https://golang.org/dl/" + fi +} + +# Function to install curl and tar (for metrics-docs, rpk-docs) +install_basic_tools() { + if ! command_exists curl; then + log_info "Installing curl..." + case "$(uname -s)" in + Linux) + sudo apt-get update && sudo apt-get install curl -y || sudo yum install curl -y || { log_error "Failed to install curl"; exit 1; } + ;; + Darwin) + if command_exists brew; then + brew install curl || { log_error "Failed to install curl"; exit 1; } + fi + ;; + esac + else + log_info "curl is already installed" + fi + + if ! command_exists tar; then + log_info "Installing tar..." + case "$(uname -s)" in + Linux) + sudo apt-get update && sudo apt-get install tar -y || sudo yum install tar -y || { log_error "Failed to install tar"; exit 1; } + ;; + Darwin) + log_info "tar is typically pre-installed on macOS" + ;; + esac + else + log_info "tar is already installed" + fi +} + +# Function to check if expect and jq are installed and install them if they're not +ensure_dependencies_installed() { + if ! command_exists expect; then + log_info "Installing expect..." case "$(uname -s)" in Linux) - echo "Detected Linux." - sudo apt-get update && sudo apt-get install expect -y || sudo yum install expect -y || { echo "Failed to install expect"; exit 1; } + sudo apt-get update && sudo apt-get install expect -y || sudo yum install expect -y || { log_error "Failed to install expect"; exit 1; } ;; Darwin) - echo "Detected macOS." - # Assumes Homebrew is installed. If not, it attempts to install Homebrew first. - if ! command -v brew &> /dev/null; then - echo "Homebrew not found." + if ! command_exists brew; then + log_error "Homebrew not found." exit 1 fi - brew install expect || { echo "Failed to install expect"; exit 1; } + brew install expect || { log_error "Failed to install expect"; exit 1; } ;; *) - echo "Unsupported operating system. Please install expect manually." + log_error "Unsupported operating system. Please install expect manually." exit 1 ;; esac + else + log_info "expect is already installed" fi - if ! command -v jq &> /dev/null; then - echo "jq is not installed. Trying to install..." - - # Install jq based on OS + if ! command_exists jq; then + log_info "Installing jq..." case "$(uname -s)" in Linux) - sudo apt-get install jq -y || sudo yum install jq -y || { echo "Failed to install jq"; exit 1; } + sudo apt-get update && sudo apt-get install jq -y || sudo yum install jq -y || { log_error "Failed to install jq"; exit 1; } ;; Darwin) - brew install jq || { echo "Failed to install jq"; exit 1; } + if command_exists brew; then + brew install jq || { log_error "Failed to install jq"; exit 1; } + else + log_error "Homebrew not found." + exit 1 + fi ;; *) - echo "Unsupported operating system. Please install jq manually." + log_error "Unsupported operating system. Please install jq manually." exit 1 ;; esac + else + log_info "jq is already installed" fi - install_node + # Install core dependencies + local node_ok=true + install_node || node_ok=false install_rust + install_git + install_buf + install_docker + install_make + install_python + install_basic_tools + install_openapi_bundlers + install_pandoc + + # Optional dependencies (warn if missing) + install_helm_docs + install_crd_ref_docs + install_go + + if [ "$node_ok" = false ]; then + log_warn "⚠️ Node.js version requirement not met. Some CLI features may not work properly." + fi } -# Ensure expect and jq are installed +# Ensure all dependencies are installed +log_info "Installing/checking dependencies for doc-tools CLI commands..." ensure_dependencies_installed - # Function to check rpk installation and display its version check_rpk_installed() { - if command -v rpk &>/dev/null; then - echo "rpk is already installed. Version information:" + if command_exists rpk; then + log_info "rpk is already installed. Version information:" rpk --version return 0 else @@ -94,65 +416,85 @@ check_rpk_installed() { fi } -# Determine OS and architecture -OS="$(uname -s)" -ARCH="$(uname -m)" +# Function to install rpk +install_rpk() { + # Check if rpk is already installed + if check_rpk_installed; then + return 0 + fi + + log_info "Installing rpk..." + + # Determine OS and architecture + OS="$(uname -s)" + ARCH="$(uname -m)" -# Check if rpk is already installed -if check_rpk_installed; then - exit 0 -fi + # Check if running on macOS and use Homebrew to install rpk + if [ "${OS}" == "Darwin" ]; then + log_info "Detected macOS. Attempting to install rpk using Homebrew..." -# Check if running on macOS and use Homebrew to install rpk -if [ "${OS}" == "Darwin" ]; then - echo "Detected macOS. Attempting to install rpk using Homebrew..." + # Check if Homebrew is installed + if ! command_exists brew; then + log_error "Homebrew not found. Please install Homebrew first: https://brew.sh" + exit 1 + fi - # Check if Homebrew is installed - if ! command -v brew &>/dev/null; then - echo "Homebrew not found." - exit 1 + # Install rpk + brew install redpanda-data/tap/redpanda || { log_error "Failed to install rpk via Homebrew"; exit 1; } + + # Verify installation + log_info "rpk has been installed. Version information:" + rpk --version + return 0 fi - # Install rpk - brew install redpanda-data/tap/redpanda || { echo "Failed to install rpk via Homebrew"; exit 1; } + # For Linux systems + if [ "${OS}" == "Linux" ]; then + FILENAME="rpk-linux-amd64.zip" + URL_BASE="https://github.com/redpanda-data/redpanda/releases" - # Verify installation - echo "rpk has been installed. Version information:" - rpk --version - exit 0 -fi + # Download latest version of rpk + log_info "Downloading ${FILENAME}..." + curl -Lf --retry 3 -O "${URL_BASE}/latest/download/${FILENAME}" \ + || { log_error "Failed to download rpk"; exit 1; } -# For Linux systems -if [ "${OS}" == "Linux" ]; then - FILENAME="rpk-linux-amd64.zip" - URL_BASE="https://github.com/redpanda-data/redpanda/releases" + # Ensure the target directory exists + mkdir -p $HOME/.local/bin || { log_error "Failed to create directory"; exit 1; } - # Download latest version of rpk - echo "Downloading ${FILENAME}..." - curl -Lf --retry 3 -O "${URL_BASE}/latest/download/${FILENAME}" \ - || { echo "Failed to download rpk"; exit 1; } + # Unzip the rpk binary to the target directory + unzip -o "${FILENAME}" -d $HOME/.local/bin || { log_error "Failed to unzip rpk"; exit 1; } - # Ensure the target directory exists - mkdir -p $HOME/.local/bin || { echo "Failed to create directory"; exit 1; } + # Remove the downloaded archive + rm "${FILENAME}" || { log_error "Failed to remove downloaded archive"; exit 1; } - # Unzip the rpk binary to the target directory - unzip -o "${FILENAME}" -d $HOME/.local/bin || { echo "Failed to unzip rpk"; exit 1; } + # Add the target directory to PATH for the current session + export PATH=$HOME/.local/bin:$PATH - # Remove the downloaded archive - rm "${FILENAME}" || { echo "Failed to remove downloaded archive"; exit 1; } + # Add the target directory to PATH for future sessions + echo 'export PATH=$HOME/.local/bin:$PATH' >> ~/.bashrc + source ~/.bashrc - # Add the target directory to PATH for the current session - export PATH=$HOME/.local/bin:$PATH + # Verify installation + log_info "rpk has been installed. Version information:" + rpk --version + return 0 + fi - # Add the target directory to PATH for future sessions - echo 'export PATH=$HOME/.local/bin:$PATH' >> ~/.bashrc - source ~/.bashrc + log_error "Unsupported operating system: ${OS}" + log_error "Please install rpk manually: https://docs.redpanda.com/current/get-started/rpk-install/" + exit 1 +} - # Verify installation - echo "rpk has been installed. Version information:" - rpk --version - exit 0 -fi +# Install rpk for rpcn-connector-docs command +install_rpk -echo "Unsupported operating system: ${OS}" -exit 1 +log_info "✅ All dependencies installation/check completed!" +log_info "" +log_info "📋 Summary of installed tools:" +log_info "Core tools: Node.js (>= v22.20.0), Git, buf, curl, tar, jq, expect" +log_info "OpenAPI bundlers: @redocly/cli or swagger-cli" +log_info "Build tools: make, Python3" +log_info "Optional tools: Docker, pandoc, helm-docs, crd-ref-docs, Go, rpk" +log_info "" +log_info "🚀 You can now use all doc-tools CLI commands!" +log_info "📚 Run 'doc-tools --help' to see available commands" diff --git a/package-lock.json b/package-lock.json index 4f41e0c..20d1fa5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@octokit/core": "^6.1.2", "@octokit/plugin-retry": "^7.1.1", "@octokit/rest": "^21.0.1", + "@redocly/cli": "^2.2.0", "algoliasearch": "^4.17.0", "chalk": "4.1.2", "commander": "^14.0.0", @@ -31,7 +32,8 @@ "sync-request": "^6.1.0", "tar": "^7.4.3", "tree-sitter": "^0.22.4", - "yaml": "^2.7.1" + "yaml": "^2.7.1", + "yargs": "^17.7.2" }, "bin": { "doc-tools": "bin/doc-tools.js" @@ -533,7 +535,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", @@ -744,7 +745,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -1029,6 +1029,15 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/runtime": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.27.2", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", @@ -1109,6 +1118,52 @@ "dev": true, "license": "MIT" }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz", + "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.8.1" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==", + "license": "MIT" + }, + "node_modules/@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==", + "license": "MIT" + }, + "node_modules/@exodus/schemasafe": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@exodus/schemasafe/-/schemasafe-1.3.0.tgz", + "integrity": "sha512-5Aap/GaRupgNx/feGBwLLTVv8OQFfv3pq2lPRzPg9R+IOBnDgghTGW7l7EuVXOvg5cc/xSAlRW8rBrjIC3Nvqw==", + "license": "MIT" + }, + "node_modules/@faker-js/faker": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-7.6.0.tgz", + "integrity": "sha512-XK6BTq1NDMo9Xqw/YkYyGjSsg44fbNwYRx7QK2CuoQgyy+f1rrTDHoExVM5PsyXCtfl2vs2vVJ0MN0yN6LppRw==", + "license": "MIT", + "engines": { + "node": ">=14.0.0", + "npm": ">=6.0.0" + } + }, + "node_modules/@humanwhocodes/momoa": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@humanwhocodes/momoa/-/momoa-2.0.4.tgz", + "integrity": "sha512-RE815I4arJFtt+FVeU1Tgp9/Xvecacji8w/V6XtXsWWH/wz/eNkNbhb+ny/+PlVZjV0rxQpRSQKNKE3lcktHEA==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.10.0" + } + }, "node_modules/@iarna/toml": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", @@ -1116,6 +1171,27 @@ "dev": true, "license": "ISC" }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -1261,20 +1337,6 @@ "node": ">=6" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -1389,29 +1451,6 @@ } } }, - "node_modules/@jest/core/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/core/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@jest/environment": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", @@ -1533,34 +1572,10 @@ } } }, - "node_modules/@jest/reporters/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/reporters/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@jest/schemas": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, "license": "MIT", "dependencies": { "@sinclair/typebox": "^0.27.8" @@ -1745,6 +1760,18 @@ "jsep": "^0.4.0||^1.0.0" } }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@octokit/auth-token": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.1.1.tgz", @@ -1915,145 +1942,681 @@ "@octokit/openapi-types": "^22.2.0" } }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "license": "MIT", - "optional": true, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "license": "Apache-2.0", "engines": { - "node": ">=14" + "node": ">=8.0.0" } }, - "node_modules/@rollup/plugin-node-resolve": { - "version": "15.2.3", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz", - "integrity": "sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==", - "dev": true, - "license": "MIT", + "node_modules/@opentelemetry/api-logs": { + "version": "0.202.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.202.0.tgz", + "integrity": "sha512-fTBjMqKCfotFWfLzaKyhjLvyEyq5vDKTTFfBmx21btv3gvy8Lq6N5Dh2OzqeuN4DjtpSvNT1uNVfg08eD2Rfxw==", + "license": "Apache-2.0", "dependencies": { - "@rollup/pluginutils": "^5.0.1", - "@types/resolve": "1.20.2", - "deepmerge": "^4.2.2", - "is-builtin-module": "^3.2.1", - "is-module": "^1.0.0", - "resolve": "^1.22.1" + "@opentelemetry/api": "^1.3.0" }, "engines": { - "node": ">=14.0.0" + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/context-async-hooks": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.0.1.tgz", + "integrity": "sha512-XuY23lSI3d4PEqKA+7SLtAgwqIfc6E/E9eAQWLN1vlpC53ybO3o6jW4BsXo1xvz9lYyyWItfQDDLzezER01mCw==", + "license": "Apache-2.0", + "engines": { + "node": "^18.19.0 || >=20.6.0" }, "peerDependencies": { - "rollup": "^2.78.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } + "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@rollup/pluginutils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", - "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", - "dev": true, - "license": "MIT", + "node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^2.3.1" + "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { - "node": ">=14.0.0" + "node": "^18.19.0 || >=20.6.0" }, "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } + "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "license": "BSD-3-Clause", + "node_modules/@opentelemetry/exporter-trace-otlp-http": { + "version": "0.202.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.202.0.tgz", + "integrity": "sha512-/hKE8DaFCJuaQqE1IxpgkcjOolUIwgi3TgHElPVKGdGRBSmJMTmN/cr6vWa55pCJIXPyhKvcMrbrya7DZ3VmzA==", + "license": "Apache-2.0", "dependencies": { - "type-detect": "4.0.8" + "@opentelemetry/core": "2.0.1", + "@opentelemetry/otlp-exporter-base": "0.202.0", + "@opentelemetry/otlp-transformer": "0.202.0", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-trace-base": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "license": "BSD-3-Clause", + "node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.202.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.202.0.tgz", + "integrity": "sha512-nMEOzel+pUFYuBJg2znGmHJWbmvMbdX5/RhoKNKowguMbURhz0fwik5tUKplLcUtl8wKPL1y9zPnPxeBn65N0Q==", + "license": "Apache-2.0", "dependencies": { - "@sinonjs/commons": "^3.0.0" + "@opentelemetry/core": "2.0.1", + "@opentelemetry/otlp-transformer": "0.202.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@types/accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==", - "dev": true, - "license": "MIT", + "node_modules/@opentelemetry/otlp-transformer": { + "version": "0.202.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.202.0.tgz", + "integrity": "sha512-5XO77QFzs9WkexvJQL9ksxL8oVFb/dfi9NWQSq7Sv0Efr9x3N+nb1iklP1TeVgxqJ7m1xWiC/Uv3wupiQGevMw==", + "license": "Apache-2.0", "dependencies": { - "@types/node": "*" + "@opentelemetry/api-logs": "0.202.0", + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-logs": "0.202.0", + "@opentelemetry/sdk-metrics": "2.0.1", + "@opentelemetry/sdk-trace-base": "2.0.1", + "protobufjs": "^7.3.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "license": "MIT", + "node_modules/@opentelemetry/resources": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.1.tgz", + "integrity": "sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw==", + "license": "Apache-2.0", "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" + "@opentelemetry/core": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, - "license": "MIT", + "node_modules/@opentelemetry/sdk-logs": { + "version": "0.202.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.202.0.tgz", + "integrity": "sha512-pv8QiQLQzk4X909YKm0lnW4hpuQg4zHwJ4XBd5bZiXcd9urvrJNoNVKnxGHPiDVX/GiLFvr5DMYsDBQbZCypRQ==", + "license": "Apache-2.0", "dependencies": { - "@babel/types": "^7.0.0" + "@opentelemetry/api-logs": "0.202.0", + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "license": "MIT", + "node_modules/@opentelemetry/sdk-metrics": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.0.1.tgz", + "integrity": "sha512-wf8OaJoSnujMAHWR3g+/hGvNcsC16rf9s1So4JlMiFaFHiE4HpIA3oUh+uWZQ7CNuK8gVW/pQSkgoa5HkkOl0g==", + "license": "Apache-2.0", "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.9.0 <1.10.0" } }, - "node_modules/@types/babel__traverse": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", - "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", - "dev": true, - "license": "MIT", + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.0.1.tgz", + "integrity": "sha512-xYLlvk/xdScGx1aEqvxLwf6sXQLXCjk3/1SQT9X9AoN5rXRhkdvIFShuNNmtTEPRBqcsMbS4p/gJLNI2wXaDuQ==", + "license": "Apache-2.0", "dependencies": { - "@babel/types": "^7.20.7" + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-2.0.1.tgz", + "integrity": "sha512-UhdbPF19pMpBtCWYP5lHbTogLWx9N0EBxtdagvkn5YtsAnCBZzL7SjktG+ZmupRgifsHMjwUaCCaVmqGfSADmA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/context-async-hooks": "2.0.1", + "@opentelemetry/core": "2.0.1", + "@opentelemetry/sdk-trace-base": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.34.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.34.0.tgz", + "integrity": "sha512-aKcOkyrorBGlajjRdVoJWHTxfxO1vCNHLJVlSDaRHDIdjU+pX8IYQPvPDkYiujKLbRnWU+1TBwEt0QRgSm4SGA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "license": "BSD-3-Clause" + }, + "node_modules/@redocly/ajv": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.11.3.tgz", + "integrity": "sha512-4P3iZse91TkBiY+Dx5DUgxQ9GXkVJf++cmI0MOyLDxV9b5MUBI4II6ES8zA5JCbO72nKAJxWrw4PUPW+YP3ZDQ==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js-replace": "^1.0.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@redocly/cli": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@redocly/cli/-/cli-2.2.2.tgz", + "integrity": "sha512-LqC7VGoMxWZZC6P96vZO5JOXGSo5Aj6A2KiLcPYPLrZT4Rr/aMBjMauJ4lacX/Z2SuaWZ5CrcnprY1QlL/ZFkQ==", + "license": "MIT", + "dependencies": { + "@opentelemetry/exporter-trace-otlp-http": "0.202.0", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-trace-node": "2.0.1", + "@opentelemetry/semantic-conventions": "1.34.0", + "@redocly/openapi-core": "2.2.2", + "@redocly/respect-core": "2.2.2", + "abort-controller": "^3.0.0", + "chokidar": "^3.5.1", + "colorette": "^1.2.0", + "cookie": "^0.7.2", + "dotenv": "16.4.7", + "form-data": "^4.0.4", + "glob": "^11.0.1", + "handlebars": "^4.7.6", + "https-proxy-agent": "^7.0.5", + "mobx": "^6.0.4", + "pluralize": "^8.0.0", + "react": "^17.0.0 || ^18.2.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.2.0 || ^19.0.0", + "redoc": "2.5.1", + "semver": "^7.5.2", + "set-cookie-parser": "^2.3.5", + "simple-websocket": "^9.0.0", + "styled-components": "^6.0.7", + "undici": "^6.21.3", + "yargs": "17.0.1" + }, + "bin": { + "openapi": "bin/cli.js", + "redocly": "bin/cli.js" + }, + "engines": { + "node": ">=22.12.0 || >=20.19.0 <21.0.0", + "npm": ">=10" + } + }, + "node_modules/@redocly/cli/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/@redocly/cli/node_modules/colorette": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", + "license": "MIT" + }, + "node_modules/@redocly/cli/node_modules/form-data": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@redocly/cli/node_modules/glob": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", + "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.0.3", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@redocly/cli/node_modules/jackspeak": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@redocly/cli/node_modules/lru-cache": { + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", + "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@redocly/cli/node_modules/minimatch": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", + "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", + "license": "ISC", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@redocly/cli/node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@redocly/cli/node_modules/yargs": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.0.1.tgz", + "integrity": "sha512-xBBulfCc8Y6gLFcrPvtqKz9hz8SO0l1Ni8GgDekvBX2ro0HRQImDGnikfc33cgzcYUSncapnNcZDjVFIH3f6KQ==", + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@redocly/config": { + "version": "0.31.0", + "resolved": "https://registry.npmjs.org/@redocly/config/-/config-0.31.0.tgz", + "integrity": "sha512-KPm2v//zj7qdGvClX0YqRNLQ9K7loVJWFEIceNxJIYPXP4hrhNvOLwjmxIkdkai0SdqYqogR2yjM/MjF9/AGdQ==", + "license": "MIT", + "dependencies": { + "json-schema-to-ts": "2.7.2" + } + }, + "node_modules/@redocly/openapi-core": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-2.2.2.tgz", + "integrity": "sha512-7Db3yYOAH0k0dq+EkWEh0Cff+KzlzR82YG+R0UphG3sxgtVT7EE4OoDKGcVYey0Dh+Qn728WCKNXdBBKb3Svhw==", + "license": "MIT", + "dependencies": { + "@redocly/ajv": "^8.11.2", + "@redocly/config": "^0.31.0", + "ajv-formats": "^2.1.1", + "colorette": "^1.2.0", + "js-levenshtein": "^1.1.6", + "js-yaml": "^4.1.0", + "minimatch": "^10.0.1", + "pluralize": "^8.0.0", + "yaml-ast-parser": "0.0.43" + }, + "engines": { + "node": ">=22.12.0 || >=20.19.0 <21.0.0", + "npm": ">=10" + } + }, + "node_modules/@redocly/openapi-core/node_modules/colorette": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", + "license": "MIT" + }, + "node_modules/@redocly/openapi-core/node_modules/minimatch": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", + "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", + "license": "ISC", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@redocly/respect-core": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@redocly/respect-core/-/respect-core-2.2.2.tgz", + "integrity": "sha512-OpiS9LFlBd8xfHAzH5WJ7zTDsjL7FO9jH5QNLCy6/macnL0b6tg/uDUmMjxyUU7/eL2eQUEr8wlIa5oZaN5ZNg==", + "license": "MIT", + "dependencies": { + "@faker-js/faker": "^7.6.0", + "@noble/hashes": "^1.8.0", + "@redocly/ajv": "8.11.2", + "@redocly/openapi-core": "2.2.2", + "better-ajv-errors": "^1.2.0", + "colorette": "^2.0.20", + "jest-matcher-utils": "^29.3.1", + "json-pointer": "^0.6.2", + "jsonpath-plus": "^10.0.6", + "openapi-sampler": "^1.6.1", + "outdent": "^0.8.0" + }, + "engines": { + "node": ">=22.12.0 || >=20.19.0 <21.0.0", + "npm": ">=10" + } + }, + "node_modules/@redocly/respect-core/node_modules/@redocly/ajv": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-io1JpnwtIcvojV7QKDUSIuMN/ikdOUd1ReEnUnMKGfDVridQZ31J0MmIuqwuRjWDZfmvr+Q0MqCcfHM2gTivOg==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js-replace": "^1.0.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "15.2.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz", + "integrity": "sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-builtin-module": "^3.2.1", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.78.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", + "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@types/accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", + "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7" } }, "node_modules/@types/body-parser": { @@ -2206,6 +2769,12 @@ "@types/istanbul-lib-report": "*" } }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "license": "MIT" + }, "node_modules/@types/keygrip": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.6.tgz", @@ -2313,6 +2882,19 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/stylis": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.5.tgz", + "integrity": "sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==", + "license": "MIT" + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT", + "optional": true + }, "node_modules/@types/ws": { "version": "7.4.7", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz", @@ -2479,7 +3061,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "dev": true, "license": "MIT", "dependencies": { "event-target-shim": "^5.0.0" @@ -2501,6 +3082,48 @@ "node": ">= 0.6" } }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, "node_modules/algoliasearch": { "version": "4.24.0", "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.24.0.tgz", @@ -2565,12 +3188,12 @@ } }, "node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/ansi-styles": { @@ -2601,7 +3224,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", @@ -3118,11 +3740,29 @@ "integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==", "license": "Apache-2.0" }, + "node_modules/better-ajv-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/better-ajv-errors/-/better-ajv-errors-1.2.0.tgz", + "integrity": "sha512-UW+IsFycygIo7bclP9h5ugkNH8EjCSgqyFB/yQ4Hqqa1OEYDtb0uFIkYE0b6+CjkgJYVM5UKI/pJPxjYe9EZlA==", + "license": "Apache-2.0", + "dependencies": { + "@babel/code-frame": "^7.16.0", + "@humanwhocodes/momoa": "^2.0.2", + "chalk": "^4.1.2", + "jsonpointer": "^5.0.0", + "leven": "^3.1.0 < 4" + }, + "engines": { + "node": ">= 12.13.0" + }, + "peerDependencies": { + "ajv": "4.11.8 - 8" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -3379,6 +4019,12 @@ "node": ">= 0.4" } }, + "node_modules/call-me-maybe": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", + "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==", + "license": "MIT" + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -3402,6 +4048,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001720", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001720.tgz", @@ -3475,7 +4130,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, "license": "MIT", "dependencies": { "anymatch": "~3.1.2", @@ -3568,6 +4222,12 @@ "node": ">= 0.4" } }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "license": "MIT" + }, "node_modules/clean-git-ref": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/clean-git-ref/-/clean-git-ref-2.0.1.tgz", @@ -3576,14 +4236,17 @@ "license": "Apache-2.0" }, "node_modules/cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha512-0yayqDxWQbqk3ojkYqUKqaAQ6AfNKeKWRNA8kR0WXzAsdHpP4BIaOmMAG87JGuO6qcobyW4GjxHd9PmhEd+T9w==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "license": "ISC", "dependencies": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wrap-ansi": "^2.0.0" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" } }, "node_modules/clone": { @@ -3651,6 +4314,15 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -3736,7 +4408,6 @@ "version": "2.0.20", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true, "license": "MIT" }, "node_modules/combined-stream": { @@ -3944,6 +4615,15 @@ "node": ">=6" } }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/cookies": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.9.1.tgz", @@ -3977,6 +4657,18 @@ "is-plain-object": "^5.0.0" } }, + "node_modules/core-js": { + "version": "3.45.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.45.1.tgz", + "integrity": "sha512-L4NPsJlCfZsPeXukyzHFlg/i7IIVwHSItR0wg0FLNqYClJ4MQYTYLbC7EkjKYRLZF2iof2MUgN0EGy7MdQFChg==", + "hasInstallScript": true, + "license": "MIT", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -4047,6 +4739,15 @@ "node": ">= 8" } }, + "node_modules/css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", + "license": "ISC", + "engines": { + "node": ">=4" + } + }, "node_modules/css-select": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", @@ -4063,6 +4764,17 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "license": "MIT", + "dependencies": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, "node_modules/css-what": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", @@ -4075,6 +4787,12 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, "node_modules/d": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", @@ -4132,6 +4850,11 @@ "node": ">=0.10.0" } }, + "node_modules/decko": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decko/-/decko-1.2.0.tgz", + "integrity": "sha512-m8FnyHXV1QX+S1cl+KPFDIl6NMkxtKsy6+U/aYyjrOqWMuwAwYWu7ePqrsUHtDR5Y8Yk2pi/KIDSgF+vT4cPOQ==" + }, "node_modules/decode-uri-component": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", @@ -4325,7 +5048,6 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, "license": "MIT", "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -4379,6 +5101,15 @@ "url": "https://github.com/fb55/domhandler?sponsor=1" } }, + "node_modules/dompurify": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.7.tgz", + "integrity": "sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, "node_modules/domutils": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", @@ -4393,6 +5124,18 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, + "node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -4603,6 +5346,12 @@ "es6-symbol": "^3.1.1" } }, + "node_modules/es6-promise": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", + "integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==", + "license": "MIT" + }, "node_modules/es6-symbol": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", @@ -4632,7 +5381,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -4669,6 +5417,20 @@ "node": ">=0.10" } }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/estree-walker": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", @@ -4699,12 +5461,17 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" } }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -4950,6 +5717,12 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -4977,9 +5750,42 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", - "dev": true, "license": "MIT" }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fast-xml-parser": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz", + "integrity": "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^1.1.1" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/faye-websocket": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", @@ -5326,13 +6132,19 @@ "node": ">=0.10.0" } }, + "node_modules/foreach": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.6.tgz", + "integrity": "sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg==", + "license": "MIT" + }, "node_modules/foreground-child": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", - "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "license": "ISC", "dependencies": { - "cross-spawn": "^7.0.0", + "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" }, "engines": { @@ -5414,7 +6226,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -5445,10 +6256,13 @@ } }, "node_modules/get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", - "license": "ISC" + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } }, "node_modules/get-intrinsic": { "version": "1.3.0", @@ -5550,7 +6364,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.1" @@ -6038,11 +6851,134 @@ "v8flags": "^3.2.0", "yargs": "^7.1.0" }, - "bin": { - "gulp": "bin/gulp.js" + "bin": { + "gulp": "bin/gulp.js" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/gulp-cli/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-cli/node_modules/camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha512-4nhGqUkc4BqbBBB4Q6zLuD7lzzrHYrjKGeYaEji/3tFR5VdJu9v+LilhGIVe8wxEJPPOeWo7eg8dwY13TZ1BNg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-cli/node_modules/cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha512-0yayqDxWQbqk3ojkYqUKqaAQ6AfNKeKWRNA8kR0WXzAsdHpP4BIaOmMAG87JGuO6qcobyW4GjxHd9PmhEd+T9w==", + "license": "ISC", + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "node_modules/gulp-cli/node_modules/get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "license": "ISC" + }, + "node_modules/gulp-cli/node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", + "license": "MIT", + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-cli/node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", + "license": "MIT", + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-cli/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-cli/node_modules/wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw==", + "license": "MIT", + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" }, "engines": { - "node": ">= 0.10" + "node": ">=0.10.0" + } + }, + "node_modules/gulp-cli/node_modules/y18n": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", + "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", + "license": "ISC" + }, + "node_modules/gulp-cli/node_modules/yargs": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.2.tgz", + "integrity": "sha512-ZEjj/dQYQy0Zx0lgLMLR8QuaqTihnxirir7EwUHp1Axq4e3+k8jXU5K0VLbNvedv1f4EWtBonDIZm0NUr+jCcA==", + "license": "MIT", + "dependencies": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^5.0.1" + } + }, + "node_modules/gulp-cli/node_modules/yargs-parser": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.1.tgz", + "integrity": "sha512-wpav5XYiddjXxirPoCTUPbqM0PXvJ9hiBMvuJgInvo4/lAOTZzUprArw17q2O1P2+GHhbBr18/iQwjL5Z9BqfA==", + "license": "ISC", + "dependencies": { + "camelcase": "^3.0.0", + "object.assign": "^4.1.0" } }, "node_modules/gulp-connect": { @@ -6418,6 +7354,48 @@ "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==", "license": "MIT" }, + "node_modules/http2-client": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/http2-client/-/http2-client-1.3.5.tgz", + "integrity": "sha512-EC2utToWl4RKfs5zd36Mxq7nzHHBuomZboI0yYL6Y0RmBgT7Sgkq4rQ0ezFTYoIsSs7Tm9SJe+o2FcAg6GBhGA==", + "license": "MIT" + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -6572,7 +7550,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, "license": "MIT", "dependencies": { "binary-extensions": "^2.0.0" @@ -6693,15 +7670,12 @@ } }, "node_modules/is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "license": "MIT", - "dependencies": { - "number-is-nan": "^1.0.0" - }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/is-generator-fn": { @@ -7063,215 +8037,85 @@ } } }, - "node_modules/jest-changed-files": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", - "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-cli": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-cli/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-cli/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/jest-cli/node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/jest-cli/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-cli/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-cli/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-cli/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/jest-cli/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", "dev": true, - "license": "ISC", + "license": "MIT", + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, "engines": { - "node": ">=10" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-cli/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", "dev": true, "license": "MIT", "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" }, "engines": { - "node": ">=12" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-cli/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", "dev": true, - "license": "ISC", + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, "engines": { - "node": ">=12" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, "node_modules/jest-config": { @@ -7343,7 +8187,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, "license": "MIT", "dependencies": { "chalk": "^4.0.0", @@ -7407,7 +8250,6 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true, "license": "MIT", "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -7457,7 +8299,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, "license": "MIT", "dependencies": { "chalk": "^4.0.0", @@ -7775,11 +8616,19 @@ "node": ">=10" } }, + "node_modules/js-levenshtein": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", + "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, "license": "MIT" }, "node_modules/js-yaml": { @@ -7823,6 +8672,35 @@ "dev": true, "license": "MIT" }, + "node_modules/json-pointer": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/json-pointer/-/json-pointer-0.6.2.tgz", + "integrity": "sha512-vLWcKbOaXlO+jvRy4qNd+TI1QUPZzfJj1tpJ3vAXDych5XJf93ftpUKe5pKCrzyIIwgBJcOcCVRUfqQP25afBw==", + "license": "MIT", + "dependencies": { + "foreach": "^2.0.4" + } + }, + "node_modules/json-schema-to-ts": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-2.7.2.tgz", + "integrity": "sha512-R1JfqKqbBR4qE8UyBR56Ms30LL62/nlhoz+1UkfI/VE7p54Awu919FZ6ZUPG8zIa3XB65usPJgr1ONVncUGSaQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@types/json-schema": "^7.0.9", + "ts-algebra": "^1.2.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", @@ -7860,6 +8738,15 @@ "node": ">=18.0.0" } }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/just-debounce": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.1.0.tgz", @@ -8143,7 +9030,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -8251,6 +9137,24 @@ "dev": true, "license": "MIT" }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, "node_modules/lru-cache": { "version": "8.0.5", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-8.0.5.tgz", @@ -8261,6 +9165,12 @@ "node": ">=16.14" } }, + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "license": "MIT" + }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -8335,6 +9245,24 @@ "node": ">=0.10.0" } }, + "node_modules/mark.js": { + "version": "8.11.1", + "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", + "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==", + "license": "MIT" + }, + "node_modules/marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } + }, "node_modules/matchdep": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz", @@ -8663,6 +9591,66 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/mobx": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.15.0.tgz", + "integrity": "sha512-UczzB+0nnwGotYSgllfARAqWCJ5e/skuV2K/l+Zyck/H6pJIhLXuBnz+6vn2i211o7DtbE78HQtsYEKICHGI+g==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mobx" + } + }, + "node_modules/mobx-react": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/mobx-react/-/mobx-react-9.2.0.tgz", + "integrity": "sha512-dkGWCx+S0/1mfiuFfHRH8D9cplmwhxOV5CkXMp38u6rQGG2Pv3FWYztS0M7ncR6TyPRQKaTG/pnitInoYE9Vrw==", + "license": "MIT", + "dependencies": { + "mobx-react-lite": "^4.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mobx" + }, + "peerDependencies": { + "mobx": "^6.9.0", + "react": "^16.8.0 || ^17 || ^18 || ^19" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/mobx-react-lite": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/mobx-react-lite/-/mobx-react-lite-4.1.1.tgz", + "integrity": "sha512-iUxiMpsvNraCKXU+yPotsOncNNmyeS2B5DKL+TL6Tar/xm+wwNJAubJmtRSeAoYawdZqwv8Z/+5nPRHeQxTiXg==", + "license": "MIT", + "dependencies": { + "use-sync-external-store": "^1.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mobx" + }, + "peerDependencies": { + "mobx": "^6.9.0", + "react": "^16.8.0 || ^17 || ^18 || ^19" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -8702,6 +9690,24 @@ "dev": true, "license": "MIT" }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", @@ -8807,6 +9813,18 @@ "url": "https://opencollective.com/node-fetch" } }, + "node_modules/node-fetch-h2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/node-fetch-h2/-/node-fetch-h2-2.3.0.tgz", + "integrity": "sha512-ofRW94Ab0T4AOh5Fk8t0h8OBWrmjb0SSB20xh1H8YnPV9EJ+f5AMoYSUQ2zgJ4Iq2HAK0I2l5/Nequ8YzFS3Hg==", + "license": "MIT", + "dependencies": { + "http2-client": "^1.2.5" + }, + "engines": { + "node": "4.x || >=6.0.0" + } + }, "node_modules/node-gyp-build": { "version": "4.8.4", "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", @@ -8835,6 +9853,15 @@ "dev": true, "license": "MIT" }, + "node_modules/node-readfiles": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/node-readfiles/-/node-readfiles-0.2.0.tgz", + "integrity": "sha512-SU00ZarexNlE4Rjdm83vglt5Y9yiQ+XI1XpflWlb7q7UTN1JUItm69xMeiQCTxtTfnzt+83T8Cx+vI2ED++VDA==", + "license": "MIT", + "dependencies": { + "es6-promise": "^3.2.1" + } + }, "node_modules/node-releases": { "version": "2.0.19", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", @@ -8918,6 +9945,103 @@ "node": ">=0.10.0" } }, + "node_modules/oas-kit-common": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/oas-kit-common/-/oas-kit-common-1.0.8.tgz", + "integrity": "sha512-pJTS2+T0oGIwgjGpw7sIRU8RQMcUoKCDWFLdBqKB2BNmGpbBMH2sdqAaOXUg8OzonZHU0L7vfJu1mJFEiYDWOQ==", + "license": "BSD-3-Clause", + "dependencies": { + "fast-safe-stringify": "^2.0.7" + } + }, + "node_modules/oas-linter": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/oas-linter/-/oas-linter-3.2.2.tgz", + "integrity": "sha512-KEGjPDVoU5K6swgo9hJVA/qYGlwfbFx+Kg2QB/kd7rzV5N8N5Mg6PlsoCMohVnQmo+pzJap/F610qTodKzecGQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@exodus/schemasafe": "^1.0.0-rc.2", + "should": "^13.2.1", + "yaml": "^1.10.0" + }, + "funding": { + "url": "https://github.com/Mermade/oas-kit?sponsor=1" + } + }, + "node_modules/oas-linter/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/oas-resolver": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/oas-resolver/-/oas-resolver-2.5.6.tgz", + "integrity": "sha512-Yx5PWQNZomfEhPPOphFbZKi9W93CocQj18NlD2Pa4GWZzdZpSJvYwoiuurRI7m3SpcChrnO08hkuQDL3FGsVFQ==", + "license": "BSD-3-Clause", + "dependencies": { + "node-fetch-h2": "^2.3.0", + "oas-kit-common": "^1.0.8", + "reftools": "^1.1.9", + "yaml": "^1.10.0", + "yargs": "^17.0.1" + }, + "bin": { + "resolve": "resolve.js" + }, + "funding": { + "url": "https://github.com/Mermade/oas-kit?sponsor=1" + } + }, + "node_modules/oas-resolver/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/oas-schema-walker": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/oas-schema-walker/-/oas-schema-walker-1.1.5.tgz", + "integrity": "sha512-2yucenq1a9YPmeNExoUa9Qwrt9RFkjqaMAA1X+U7sbb0AqBeTIdMHky9SQQ6iN94bO5NW0W4TRYXerG+BdAvAQ==", + "license": "BSD-3-Clause", + "funding": { + "url": "https://github.com/Mermade/oas-kit?sponsor=1" + } + }, + "node_modules/oas-validator": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/oas-validator/-/oas-validator-5.0.8.tgz", + "integrity": "sha512-cu20/HE5N5HKqVygs3dt94eYJfBi0TsZvPVXDhbXQHiEityDN+RROTleefoKRKKJ9dFAF2JBkDHgvWj0sjKGmw==", + "license": "BSD-3-Clause", + "dependencies": { + "call-me-maybe": "^1.0.1", + "oas-kit-common": "^1.0.8", + "oas-linter": "^3.2.2", + "oas-resolver": "^2.5.6", + "oas-schema-walker": "^1.1.5", + "reftools": "^1.1.9", + "should": "^13.2.1", + "yaml": "^1.10.0" + }, + "funding": { + "url": "https://github.com/Mermade/oas-kit?sponsor=1" + } + }, + "node_modules/oas-validator/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -9153,6 +10277,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/openapi-sampler": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/openapi-sampler/-/openapi-sampler-1.6.1.tgz", + "integrity": "sha512-s1cIatOqrrhSj2tmJ4abFYZQK6l5v+V4toO5q1Pa0DyN8mtyqy2I+Qrj5W9vOELEtybIMQs/TBZGVO/DtTFK8w==", + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.7", + "fast-xml-parser": "^4.5.0", + "json-pointer": "0.6.2" + } + }, "node_modules/ordered-read-streams": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz", @@ -9204,6 +10339,12 @@ "node": ">=0.10.0" } }, + "node_modules/outdent": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/outdent/-/outdent-0.8.0.tgz", + "integrity": "sha512-KiOAIsdpUTcAXuykya5fnVVT+/5uS0Q1mrkRHcF89tpieSmY33O/tmc54CqwA+bfhbtEfZUNLHaPUiB9X3jt1A==", + "license": "MIT" + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -9352,6 +10493,12 @@ "node": ">=0.10.0" } }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "license": "MIT" + }, "node_modules/path-dirname": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", @@ -9467,11 +10614,16 @@ "dev": true, "license": "MIT" }, + "node_modules/perfect-scrollbar": { + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/perfect-scrollbar/-/perfect-scrollbar-1.5.6.tgz", + "integrity": "sha512-rixgxw3SxyJbCaSpo1n35A/fwI1r2rdwMKOTCg/AcG+xOEyZcE8UHVjpZMFCVImzsFoCZeJTT+M/rdEIQYO2nw==", + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, "license": "ISC" }, "node_modules/picomatch": { @@ -9665,6 +10817,27 @@ "node": ">=8" } }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/polished": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/polished/-/polished-4.3.1.tgz", + "integrity": "sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.17.8" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/portfinder": { "version": "1.0.32", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz", @@ -9706,11 +10879,44 @@ "node": ">=0.10.0" } }, + "node_modules/postcss": { + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "license": "MIT" + }, "node_modules/pretty-format": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", @@ -9725,7 +10931,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -9743,6 +10948,15 @@ "node": ">= 0.8" } }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -9799,6 +11013,47 @@ "node": ">= 6" } }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/protobufjs": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", + "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -9874,6 +11129,26 @@ "inherits": "~2.0.0" } }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/quick-format-unescaped": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", @@ -9881,6 +11156,15 @@ "dev": true, "license": "MIT" }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -9909,13 +11193,46 @@ "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", "license": "MIT" }, + "node_modules/react": { + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz", + "integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz", + "integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.26.0" + }, + "peerDependencies": { + "react": "^19.1.1" + } + }, "node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, "license": "MIT" }, + "node_modules/react-tabs": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/react-tabs/-/react-tabs-6.1.0.tgz", + "integrity": "sha512-6QtbTRDKM+jA/MZTTefvigNxo0zz+gnBTVFw2CFVvq+f2BuH0nF0vDLNClL045nuTAdOoK/IL1vTP0ZLX0DAyQ==", + "license": "MIT", + "dependencies": { + "clsx": "^2.0.0", + "prop-types": "^15.5.0" + }, + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0" + } + }, "node_modules/read-pkg": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", @@ -9947,7 +11264,6 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, "license": "MIT", "dependencies": { "inherits": "^2.0.3", @@ -9962,7 +11278,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, "license": "MIT", "dependencies": { "picomatch": "^2.2.1" @@ -9992,6 +11307,109 @@ "node": ">= 0.10" } }, + "node_modules/redoc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/redoc/-/redoc-2.5.1.tgz", + "integrity": "sha512-LmqA+4A3CmhTllGG197F0arUpmChukAj9klfSdxNRemT9Hr07xXr7OGKu4PHzBs359sgrJ+4JwmOlM7nxLPGMg==", + "license": "MIT", + "dependencies": { + "@redocly/openapi-core": "^1.4.0", + "classnames": "^2.3.2", + "decko": "^1.2.0", + "dompurify": "^3.2.4", + "eventemitter3": "^5.0.1", + "json-pointer": "^0.6.2", + "lunr": "^2.3.9", + "mark.js": "^8.11.1", + "marked": "^4.3.0", + "mobx-react": "9.2.0", + "openapi-sampler": "^1.5.0", + "path-browserify": "^1.0.1", + "perfect-scrollbar": "^1.5.5", + "polished": "^4.2.2", + "prismjs": "^1.29.0", + "prop-types": "^15.8.1", + "react-tabs": "^6.0.2", + "slugify": "~1.4.7", + "stickyfill": "^1.1.1", + "swagger2openapi": "^7.0.8", + "url-template": "^2.0.8" + }, + "engines": { + "node": ">=6.9", + "npm": ">=3.0.0" + }, + "peerDependencies": { + "core-js": "^3.1.4", + "mobx": "^6.0.4", + "react": "^16.8.4 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.4 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "styled-components": "^4.1.1 || ^5.1.1 || ^6.0.5" + } + }, + "node_modules/redoc/node_modules/@redocly/config": { + "version": "0.22.2", + "resolved": "https://registry.npmjs.org/@redocly/config/-/config-0.22.2.tgz", + "integrity": "sha512-roRDai8/zr2S9YfmzUfNhKjOF0NdcOIqF7bhf4MVC5UxpjIysDjyudvlAiVbpPHp3eDRWbdzUgtkK1a7YiDNyQ==", + "license": "MIT" + }, + "node_modules/redoc/node_modules/@redocly/openapi-core": { + "version": "1.34.5", + "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.34.5.tgz", + "integrity": "sha512-0EbE8LRbkogtcCXU7liAyC00n9uNG9hJ+eMyHFdUsy9lB/WGqnEBgwjA9q2cyzAVcdTkQqTBBU1XePNnN3OijA==", + "license": "MIT", + "dependencies": { + "@redocly/ajv": "^8.11.2", + "@redocly/config": "^0.22.0", + "colorette": "^1.2.0", + "https-proxy-agent": "^7.0.5", + "js-levenshtein": "^1.1.6", + "js-yaml": "^4.1.0", + "minimatch": "^5.0.1", + "pluralize": "^8.0.0", + "yaml-ast-parser": "0.0.43" + }, + "engines": { + "node": ">=18.17.0", + "npm": ">=9.5.0" + } + }, + "node_modules/redoc/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/redoc/node_modules/colorette": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", + "license": "MIT" + }, + "node_modules/redoc/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/reftools": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/reftools/-/reftools-1.1.9.tgz", + "integrity": "sha512-OVede/NQE13xBQ+ob5CKd5KyeJYU2YInb1bmV4nRoOfquZPkAkxuOXicSe1PvqIuZZ4kD13sPKBbR7UFDmli6w==", + "license": "BSD-3-Clause", + "funding": { + "url": "https://github.com/Mermade/oas-kit?sponsor=1" + } + }, "node_modules/regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", @@ -10092,7 +11510,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -10369,6 +11786,12 @@ "node": ">=10" } }, + "node_modules/scheduler": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "license": "MIT" + }, "node_modules/secure-json-parse": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", @@ -10629,6 +12052,12 @@ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "license": "ISC" }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -10714,6 +12143,12 @@ "sha.js": "bin.js" } }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", + "license": "MIT" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -10735,6 +12170,38 @@ "node": ">=8" } }, + "node_modules/should": { + "version": "13.2.3", + "resolved": "https://registry.npmjs.org/should/-/should-13.2.3.tgz", + "integrity": "sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==", + "license": "MIT", + "dependencies": { + "should-equal": "^2.0.0", + "should-format": "^3.0.3", + "should-type": "^1.4.0", + "should-type-adaptors": "^1.0.1", + "should-util": "^1.0.0" + } + }, + "node_modules/should-equal": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz", + "integrity": "sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==", + "license": "MIT", + "dependencies": { + "should-type": "^1.4.0" + } + }, + "node_modules/should-format": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz", + "integrity": "sha512-hZ58adtulAk0gKtua7QxevgUaXTTXxIi8t41L3zo9AHvjXO1/7sdLECuHeIN2SRtYXpNkmhoUP2pdeWgricQ+Q==", + "license": "MIT", + "dependencies": { + "should-type": "^1.3.0", + "should-type-adaptors": "^1.0.1" + } + }, "node_modules/should-proxy": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/should-proxy/-/should-proxy-1.0.4.tgz", @@ -10742,6 +12209,28 @@ "dev": true, "license": "MIT" }, + "node_modules/should-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz", + "integrity": "sha512-MdAsTu3n25yDbIe1NeN69G4n6mUnJGtSJHygX3+oN0ZbO3DTiATnf7XnYJdGT42JCXurTb1JI0qOBR65shvhPQ==", + "license": "MIT" + }, + "node_modules/should-type-adaptors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz", + "integrity": "sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==", + "license": "MIT", + "dependencies": { + "should-type": "^1.3.0", + "should-util": "^1.0.0" + } + }, + "node_modules/should-util": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.1.tgz", + "integrity": "sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==", + "license": "MIT" + }, "node_modules/side-channel": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", @@ -10819,6 +12308,56 @@ "simple-concat": "^1.0.0" } }, + "node_modules/simple-websocket": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/simple-websocket/-/simple-websocket-9.1.0.tgz", + "integrity": "sha512-8MJPnjRN6A8UCp1I+H/dSFyjwJhp6wta4hsVRhjf8w9qBHRzxYt14RaOcjvQnhD1N4yKOddEjflwMnQM4VtXjQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "debug": "^4.3.1", + "queue-microtask": "^1.2.2", + "randombytes": "^2.1.0", + "readable-stream": "^3.6.0", + "ws": "^7.4.2" + } + }, + "node_modules/simple-websocket/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/simple-websocket/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -10836,6 +12375,15 @@ "node": ">=8" } }, + "node_modules/slugify": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.4.7.tgz", + "integrity": "sha512-tf+h5W1IrjNm/9rKKj0JU2MDMruiopx0jjVA5zCdBtcGjfp0+c5rHw/zADLC3IeKlGHtVbHtpfzvYA0OYT+HKg==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -10979,6 +12527,15 @@ "node": ">=0.10.0" } }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-resolve": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", @@ -11047,9 +12604,9 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.20", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz", - "integrity": "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==", + "version": "3.0.22", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz", + "integrity": "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==", "license": "CC0-1.0" }, "node_modules/split-string": { @@ -11150,6 +12707,11 @@ "node": ">= 0.6" } }, + "node_modules/stickyfill": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stickyfill/-/stickyfill-1.1.1.tgz", + "integrity": "sha512-GCp7vHAfpao+Qh/3Flh9DXEJ/qSi0KJwJw6zYlZOtRYXWUIpMM6mC2rIep/dK8RQqwW0KxGJIllmjPIBOGN8AA==" + }, "node_modules/stream-exhaust": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", @@ -11166,47 +12728,23 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, "license": "MIT", "dependencies": { "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-length/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-length/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" } }, "node_modules/string-template": { @@ -11215,17 +12753,17 @@ "integrity": "sha512-Yptehjogou2xm4UJbxJ4CxgZx12HBfeystp0y3x7s4Dj32ltVVG1Gg8YhKjHZkHicuKpZX/ffilA8505VbUbpw==" }, "node_modules/string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/string-width-cjs": { @@ -11243,25 +12781,7 @@ "node": ">=8" } }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { + "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", @@ -11273,18 +12793,6 @@ "node": ">=8" } }, - "node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/strip-ansi-cjs": { "name": "strip-ansi", "version": "6.0.1", @@ -11298,15 +12806,6 @@ "node": ">=8" } }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/strip-bom": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", @@ -11342,6 +12841,52 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strnum": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", + "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/styled-components": { + "version": "6.1.19", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.19.tgz", + "integrity": "sha512-1v/e3Dl1BknC37cXMhwGomhO8AkYmN41CqyX9xhUDxry1ns3BFQy2lLDRQXJRdVVWB9OHemv/53xaStimvWyuA==", + "license": "MIT", + "dependencies": { + "@emotion/is-prop-valid": "1.2.2", + "@emotion/unitless": "0.8.1", + "@types/stylis": "4.2.5", + "css-to-react-native": "3.2.0", + "csstype": "3.1.3", + "postcss": "8.4.49", + "shallowequal": "1.1.0", + "stylis": "4.3.2", + "tslib": "2.6.2" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/styled-components" + }, + "peerDependencies": { + "react": ">= 16.8.0", + "react-dom": ">= 16.8.0" + } + }, + "node_modules/stylis": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz", + "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==", + "license": "MIT" + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -11376,6 +12921,84 @@ "es6-symbol": "^3.1.1" } }, + "node_modules/swagger2openapi": { + "version": "7.0.8", + "resolved": "https://registry.npmjs.org/swagger2openapi/-/swagger2openapi-7.0.8.tgz", + "integrity": "sha512-upi/0ZGkYgEcLeGieoz8gT74oWHA0E7JivX7aN9mAf+Tc7BQoRBvnIGHoPDw+f9TXTW4s6kGYCZJtauP6OYp7g==", + "license": "BSD-3-Clause", + "dependencies": { + "call-me-maybe": "^1.0.1", + "node-fetch": "^2.6.1", + "node-fetch-h2": "^2.3.0", + "node-readfiles": "^0.2.0", + "oas-kit-common": "^1.0.8", + "oas-resolver": "^2.5.6", + "oas-schema-walker": "^1.1.5", + "oas-validator": "^5.0.8", + "reftools": "^1.1.9", + "yaml": "^1.10.0", + "yargs": "^17.0.1" + }, + "bin": { + "boast": "boast.js", + "oas-validate": "oas-validate.js", + "swagger2openapi": "swagger2openapi.js" + }, + "funding": { + "url": "https://github.com/Mermade/oas-kit?sponsor=1" + } + }, + "node_modules/swagger2openapi/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/swagger2openapi/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/swagger2openapi/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/swagger2openapi/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/swagger2openapi/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, "node_modules/sync-request": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/sync-request/-/sync-request-6.1.0.tgz", @@ -11750,6 +13373,18 @@ "node-gyp-build": "^4.8.4" } }, + "node_modules/ts-algebra": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-1.2.2.tgz", + "integrity": "sha512-kloPhf1hq3JbCPOTYoOWDKxebWjNb2o/LKnNfkWhxVVisFFmMJPPdJeGoGmM+iRLyoXAR61e08Pb+vUXINg8aA==", + "license": "MIT" + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "license": "0BSD" + }, "node_modules/tsscmp": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", @@ -11871,6 +13506,15 @@ "node": ">= 0.10" } }, + "node_modules/undici": { + "version": "6.21.3", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.3.tgz", + "integrity": "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==", + "license": "MIT", + "engines": { + "node": ">=18.17" + } + }, "node_modules/undici-types": { "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", @@ -12015,6 +13659,12 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/uri-js-replace": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uri-js-replace/-/uri-js-replace-1.0.1.tgz", + "integrity": "sha512-W+C9NWNLFOoBI2QWDp4UT9pv65r2w5Cx+3sTYFvtMdDBxkKt1syCqsUdSFAChbEe1uK5TfS04wt/nGwmaeIQ0g==", + "license": "MIT" + }, "node_modules/urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", @@ -12022,6 +13672,12 @@ "deprecated": "Please see https://github.com/lydell/urix#deprecated", "license": "MIT" }, + "node_modules/url-template": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", + "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==", + "license": "BSD" + }, "node_modules/use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", @@ -12031,6 +13687,15 @@ "node": ">=0.10.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", + "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -12391,16 +14056,20 @@ } }, "node_modules/wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "license": "MIT", "dependencies": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/wrap-ansi-cjs": { @@ -12421,50 +14090,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -12496,7 +14121,6 @@ "version": "7.5.10", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8.3.0" @@ -12534,10 +14158,13 @@ } }, "node_modules/y18n": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", - "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", - "license": "ISC" + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } }, "node_modules/yallist": { "version": "5.0.0", @@ -12560,54 +14187,46 @@ "node": ">= 14" } }, + "node_modules/yaml-ast-parser": { + "version": "0.0.43", + "resolved": "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz", + "integrity": "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==", + "license": "Apache-2.0" + }, "node_modules/yargs": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.2.tgz", - "integrity": "sha512-ZEjj/dQYQy0Zx0lgLMLR8QuaqTihnxirir7EwUHp1Axq4e3+k8jXU5K0VLbNvedv1f4EWtBonDIZm0NUr+jCcA==", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "license": "MIT", "dependencies": { - "camelcase": "^3.0.0", - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "get-caller-file": "^1.0.1", - "os-locale": "^1.4.0", - "read-pkg-up": "^1.0.1", + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^1.0.2", - "which-module": "^1.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^5.0.1" + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" } }, "node_modules/yargs-parser": { "version": "20.2.9", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, "license": "ISC", "engines": { "node": ">=10" } }, - "node_modules/yargs/node_modules/camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha512-4nhGqUkc4BqbBBB4Q6zLuD7lzzrHYrjKGeYaEji/3tFR5VdJu9v+LilhGIVe8wxEJPPOeWo7eg8dwY13TZ1BNg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/yargs/node_modules/yargs-parser": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.1.tgz", - "integrity": "sha512-wpav5XYiddjXxirPoCTUPbqM0PXvJ9hiBMvuJgInvo4/lAOTZzUprArw17q2O1P2+GHhbBr18/iQwjL5Z9BqfA==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "license": "ISC", - "dependencies": { - "camelcase": "^3.0.0", - "object.assign": "^4.1.0" + "engines": { + "node": ">=12" } }, "node_modules/yauzl": { diff --git a/package.json b/package.json index d73b98c..b450f5f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@redpanda-data/docs-extensions-and-macros", - "version": "4.9.1", + "version": "4.10.0", "description": "Antora extensions and macros developed for Redpanda documentation.", "keywords": [ "antora", @@ -21,7 +21,10 @@ "get-console-version": "doc-tools get-console-version", "build": "antora --to-dir docs --fetch local-antora-playbook.yml", "serve": "wds --node-resolve --open preview/test/ --watch --root-dir docs", - "test": "jest" + "test": "jest", + "bundle:admin": "doc-tools generate bundle-admin-api --surface admin", + "bundle:connect": "doc-tools generate bundle-admin-api --surface connect", + "bundle:both": "doc-tools generate bundle-admin-api --surface both" }, "contributors": [ { @@ -99,7 +102,9 @@ "sync-request": "^6.1.0", "tar": "^7.4.3", "tree-sitter": "^0.22.4", - "yaml": "^2.7.1" + "yaml": "^2.7.1", + "yargs": "^17.7.2", + "@redocly/cli": "^2.2.0" }, "devDependencies": { "@antora/cli": "3.1.4", diff --git a/tools/bundle-openapi.js b/tools/bundle-openapi.js new file mode 100644 index 0000000..3a2b10c --- /dev/null +++ b/tools/bundle-openapi.js @@ -0,0 +1,757 @@ +#!/usr/bin/env node + +const fs = require('fs'); +const path = require('path'); +const { execSync, spawnSync } = require('child_process'); +const yaml = require('yaml'); + +/** + * Normalize git tag by removing 'v' prefix if present + * @param {string} tag - Git tag like 'v25.1.1' or '25.1.1' + * @returns {string} Normalized version like '25.1.1' + */ +function normalizeTag(tag) { + if (!tag || typeof tag !== 'string') { + throw new Error('Tag must be a non-empty string'); + } + + // Trim whitespace + tag = tag.trim(); + + if (!tag) { + throw new Error('Invalid version format: tag cannot be empty'); + } + + // Handle dev branch + if (tag === 'dev') { + return 'dev'; + } + + // Remove 'v' prefix if present + const normalized = tag.startsWith('v') ? tag.slice(1) : tag; + + // Validate semantic version format + const semverPattern = /^\d+\.\d+\.\d+(-[\w\.-]+)?(\+[\w\.-]+)?$/; + if (!semverPattern.test(normalized) && normalized !== 'dev') { + throw new Error(`Invalid version format: ${tag}. Expected format like v25.1.1 or 25.1.1`); + } + + return normalized; +} + +/** + * Extract major.minor version from semantic version + * @param {string} version - Version like '25.1.1' + * @returns {string} Major.minor version like '25.1' + */ +function getMajorMinor(version) { + if (!version || typeof version !== 'string') { + throw new Error('Version must be a non-empty string'); + } + + if (version === 'dev') { + return 'dev'; + } + + const parts = version.split('.'); + if (parts.length < 2) { + throw new Error(`Invalid version format: ${version}. Expected X.Y.Z format`); + } + + const major = parseInt(parts[0], 10); + const minor = parseInt(parts[1], 10); + + if (isNaN(major) || isNaN(minor)) { + throw new Error(`Major and minor versions must be numbers: ${version}`); + } + + return `${major}.${minor}`; +} + +/** + * Sort object keys recursively for deterministic output + * @param {any} obj - Object to sort + * @returns {any} Object with sorted keys + */ +function sortObjectKeys(obj) { + if (obj === null || typeof obj !== 'object') { + return obj; + } + + if (Array.isArray(obj)) { + return obj.map(sortObjectKeys); + } + + const sortedObj = {}; + Object.keys(obj) + .sort() + .forEach(key => { + sortedObj[key] = sortObjectKeys(obj[key]); + }); + + return sortedObj; +} + +/** + * Detect available OpenAPI bundler + * @param {boolean} quiet - Suppress output + * @returns {string} Available bundler command + */ +function detectBundler(quiet = false) { + const bundlers = ['swagger-cli', 'redocly']; + + for (const bundler of bundlers) { + try { + execSync(`${bundler} --version`, { + stdio: 'ignore', + timeout: 10000 + }); + + if (!quiet) { + console.log(`✅ Using ${bundler} for OpenAPI bundling`); + } + return bundler; + } catch (error) { + // Continue to next bundler + } + } + + // Try npx @redocly/cli as fallback + try { + execSync('npx @redocly/cli --version', { + stdio: 'ignore', + timeout: 10000 + }); + + if (!quiet) { + console.log('✅ Using npx @redocly/cli for OpenAPI bundling'); + } + return 'npx @redocly/cli'; + } catch (error) { + // Try legacy npx redocly + try { + execSync('npx redocly --version', { + stdio: 'ignore', + timeout: 10000 + }); + + if (!quiet) { + console.log('✅ Using npx redocly for OpenAPI bundling'); + } + return 'npx redocly'; + } catch (error) { + // Final fallback failed + } + } + + throw new Error( + 'No OpenAPI bundler found. Please install one of:\n' + + ' npm install -g swagger-cli\n' + + ' npm install -g @redocly/cli\n' + + 'For more information, see: https://github.com/APIDevTools/swagger-cli or https://redocly.com/docs/cli/' + ); +} + +/** + * Create an index.yaml file that references all Admin API fragments + * @param {string} tempDir - Temporary directory path + * @param {string} surface - API surface ('admin' or 'connect') + * @param {boolean} quiet - Suppress output + * @returns {string[]} Array of fragment file paths + */ +function createEntrypoint(tempDir, apiSurface) { + // Validate input parameters + if (!tempDir || typeof tempDir !== 'string' || tempDir.trim() === '') { + throw new Error('Invalid temporary directory'); + } + + // Check if directory exists + if (!fs.existsSync(tempDir)) { + throw new Error('Invalid temporary directory'); + } + + if (!apiSurface || typeof apiSurface !== 'string' || !['admin', 'connect'].includes(apiSurface)) { + throw new Error('Invalid API surface'); + } + + let quiet = false; // Default for logging + if (!quiet) { + console.log('🔍 Looking for fragments in:'); + console.log(` Admin v2: ${path.join(tempDir, 'vbuild/openapi/proto/redpanda/core/admin/v2')}`); + console.log(` Common: ${path.join(tempDir, 'vbuild/openapi/proto/redpanda/core/common')}`); + } + + const fragmentDirs = []; + let fragmentFiles = []; + + try { + if (apiSurface === 'admin') { + const adminDir = path.join(tempDir, 'vbuild/openapi/proto/redpanda/core/admin/v2'); + const commonDir = path.join(tempDir, 'vbuild/openapi/proto/redpanda/core/common'); + + fragmentDirs.push(adminDir, commonDir); + } else if (apiSurface === 'connect') { + const connectDir = path.join(tempDir, 'vbuild/openapi/proto/redpanda/connect'); + fragmentDirs.push(connectDir); + } + + // Log directory existence for debugging + if (!quiet && fs.existsSync(path.join(tempDir, 'vbuild'))) { + console.log('📂 vbuild directory contents:'); + try { + const contents = fs.readdirSync(path.join(tempDir, 'vbuild'), { recursive: true }); + contents.slice(0, 10).forEach(item => { + console.log(` ${item}`); + }); + if (contents.length > 10) { + console.log(` ... and ${contents.length - 10} more items`); + } + } catch (dirErr) { + console.log(` ❌ Error reading directory: ${dirErr.message}`); + } + } + + fragmentDirs.forEach(dir => { + if (fs.existsSync(dir)) { + try { + const files = fs.readdirSync(dir) + .filter(file => file.endsWith('.openapi.yaml') || file.endsWith('.openapi.yml')) + .map(file => path.join(dir, file)) + .filter(filePath => fs.statSync(filePath).isFile()); // Make sure it's actually a file + + fragmentFiles.push(...files); + } catch (readErr) { + throw new Error(`Failed to read fragment directories: ${readErr.message}`); + } + } else { + if (!quiet) { + console.log(`📁 ${path.basename(dir) === 'v2' ? 'Admin v2' : path.basename(dir)} directory not found: ${dir}`); + } + } + }); + + } catch (err) { + throw new Error(`Failed to scan for OpenAPI fragments: ${err.message}`); + } + + if (fragmentFiles.length === 0) { + throw new Error('No OpenAPI fragments found to bundle. Make sure \'buf generate\' has run successfully'); + } + + // Most bundlers can handle multiple input files or merge operations. + return fragmentFiles; +} + +/** + * Bundle OpenAPI fragments using external bundler + * @param {string} bundler - 'swagger-cli', 'redocly', 'npx redocly', or 'npx @redocly/cli' + * @param {string[]|string} fragmentFiles - Array of fragment file paths or single entrypoint + * @param {string} outputPath - Path for bundled output + * @param {string} tempDir - Temporary directory for creating merged file + * @param {boolean} quiet - Suppress output + * @throws {Error} If bundling fails + */ +function runBundler(bundler, fragmentFiles, outputPath, tempDir, quiet = false) { + if (!bundler || typeof bundler !== 'string') { + throw new Error('Invalid bundler specified'); + } + + if (!fragmentFiles || (Array.isArray(fragmentFiles) && fragmentFiles.length === 0)) { + throw new Error('No fragment files provided for bundling'); + } + + if (!outputPath || typeof outputPath !== 'string') { + throw new Error('Invalid output path specified'); + } + + if (!tempDir || !fs.existsSync(tempDir)) { + throw new Error('Invalid temporary directory'); + } + + const stdio = quiet ? 'ignore' : 'inherit'; + const timeout = 120000; // 2 minutes timeout + + // If we have multiple fragments, we need to merge them first since bundlers + // typically expect a single entrypoint file + let entrypoint; + + try { + if (Array.isArray(fragmentFiles) && fragmentFiles.length > 1) { + // Create a merged entrypoint file + entrypoint = path.join(tempDir, 'merged-entrypoint.yaml'); + + const mergedContent = { + openapi: '3.1.0', + info: { + title: 'Redpanda Admin API', + version: '2.0.0' + }, + paths: {}, + components: { + schemas: {} + } + }; + + // Manually merge all fragment files with better error handling + for (const filePath of fragmentFiles) { + try { + if (!fs.existsSync(filePath)) { + console.warn(`⚠️ Fragment file not found: ${filePath}`); + continue; + } + + const fragmentContent = fs.readFileSync(filePath, 'utf8'); + const fragmentData = yaml.parse(fragmentContent); + + if (!fragmentData || typeof fragmentData !== 'object') { + console.warn(`⚠️ Invalid fragment data in: ${filePath}`); + continue; + } + + // Merge paths + if (fragmentData.paths && typeof fragmentData.paths === 'object') { + Object.assign(mergedContent.paths, fragmentData.paths); + } + + // Merge components + if (fragmentData.components && typeof fragmentData.components === 'object') { + if (fragmentData.components.schemas) { + Object.assign(mergedContent.components.schemas, fragmentData.components.schemas); + } + // Merge other component types + const componentTypes = ['responses', 'parameters', 'examples', 'requestBodies', 'headers', 'securitySchemes', 'links', 'callbacks']; + for (const componentType of componentTypes) { + if (fragmentData.components[componentType]) { + if (!mergedContent.components[componentType]) { + mergedContent.components[componentType] = {}; + } + Object.assign(mergedContent.components[componentType], fragmentData.components[componentType]); + } + } + } + } catch (error) { + console.warn(`⚠️ Failed to parse fragment ${filePath}: ${error.message}`); + } + } + + // Validate merged content + if (Object.keys(mergedContent.paths).length === 0) { + throw new Error('No valid paths found in any fragments'); + } + + fs.writeFileSync(entrypoint, yaml.stringify(mergedContent), 'utf8'); + + if (!quiet) { + console.log(`📄 Created merged entrypoint with ${Object.keys(mergedContent.paths).length} paths`); + } + } else { + // Single file or string entrypoint + entrypoint = Array.isArray(fragmentFiles) ? fragmentFiles[0] : fragmentFiles; + + if (!fs.existsSync(entrypoint)) { + throw new Error(`Entrypoint file not found: ${entrypoint}`); + } + } + + // Ensure output directory exists + const outputDir = path.dirname(outputPath); + fs.mkdirSync(outputDir, { recursive: true }); + + let result; + if (bundler === 'swagger-cli') { + result = spawnSync('swagger-cli', ['bundle', entrypoint, '-o', outputPath, '-t', 'yaml'], { + stdio, + timeout + }); + } else if (bundler === 'redocly') { + result = spawnSync('redocly', ['bundle', entrypoint, '--output', outputPath], { + stdio, + timeout + }); + } else if (bundler === 'npx redocly') { + result = spawnSync('npx', ['redocly', 'bundle', entrypoint, '--output', outputPath], { + stdio, + timeout + }); + } else if (bundler === 'npx @redocly/cli') { + result = spawnSync('npx', ['@redocly/cli', 'bundle', entrypoint, '--output', outputPath], { + stdio, + timeout + }); + } else { + throw new Error(`Unknown bundler: ${bundler}`); + } + + if (result.error) { + if (result.error.code === 'ETIMEDOUT') { + throw new Error(`Bundler timed out after ${timeout / 1000} seconds`); + } + throw new Error(`Bundler execution failed: ${result.error.message}`); + } + + if (result.status !== 0) { + const errorMsg = result.stderr ? result.stderr.toString() : 'Unknown error'; + throw new Error(`${bundler} bundle failed with exit code ${result.status}: ${errorMsg}`); + } + + // Verify output file was created + if (!fs.existsSync(outputPath)) { + throw new Error(`Bundler completed but output file not found: ${outputPath}`); + } + + const stats = fs.statSync(outputPath); + if (stats.size === 0) { + throw new Error(`Bundler created empty output file: ${outputPath}`); + } + + if (!quiet) { + console.log(`✅ Bundle created: ${outputPath} (${Math.round(stats.size / 1024)}KB)`); + } + + } catch (error) { + // Clean up temporary entrypoint file on error + if (entrypoint && entrypoint !== fragmentFiles && fs.existsSync(entrypoint)) { + try { + fs.unlinkSync(entrypoint); + } catch { + // Ignore cleanup errors + } + } + throw error; + } +} + +/** + * Post-process the bundled OpenAPI document + * @param {string} filePath - Path to bundled YAML file + * @param {Object} options - Processing options + * @param {string} options.surface - API surface ('admin' or 'connect') + * @param {string} options.tag - Git tag for versioning + * @param {string} options.majorMinor - Major.minor version + * @param {string} [options.adminMajor] - Admin API major version + * @param {boolean} quiet - Suppress output + */ +function postProcessBundle(filePath, options, quiet = false) { + if (!filePath || typeof filePath !== 'string') { + throw new Error('Bundle file not found'); + } + + if (!fs.existsSync(filePath)) { + throw new Error('Bundle file not found'); + } + + if (!options || typeof options !== 'object') { + throw new Error('Missing required options'); + } + + const { surface, tag, majorMinor, adminMajor, normalizedTag, useAdminMajorVersion } = options; + + if (!surface || !['admin', 'connect'].includes(surface)) { + throw new Error('Invalid API surface'); + } + + // Require at least one version identifier + if (!tag && !normalizedTag && !majorMinor) { + throw new Error('Missing required options'); + } + + try { + const content = fs.readFileSync(filePath, 'utf8'); + if (!content.trim()) { + throw new Error('Bundle file is empty'); + } + + let bundle; + try { + bundle = yaml.parse(content); + } catch (parseError) { + throw new Error(`Invalid YAML in bundle file: ${parseError.message}`); + } + + if (!bundle || typeof bundle !== 'object') { + throw new Error('Bundle file does not contain valid OpenAPI structure'); + } + + // Normalize the tag and extract version info + const normalizedVersion = normalizedTag || (tag ? normalizeTag(tag) : '1.0.0'); + let versionMajorMinor; + + if (useAdminMajorVersion && surface === 'admin' && adminMajor) { + // Use admin major version for info.version when flag is set + versionMajorMinor = adminMajor; + } else { + // Use normalized tag version (default behavior) + versionMajorMinor = majorMinor || (normalizedVersion !== '1.0.0' ? getMajorMinor(normalizedVersion) : '1.0'); + } + + // Update info section with proper metadata + if (!bundle.info) { + bundle.info = {}; + } + + bundle.info.version = versionMajorMinor; + + if (surface === 'admin') { + bundle.info.title = 'Redpanda Admin API'; + bundle.info.description = 'Redpanda Admin API specification'; + if (adminMajor) { + bundle.info['x-admin-api-major'] = adminMajor; + } + } else if (surface === 'connect') { + bundle.info.title = 'Redpanda Connect RPCs'; + bundle.info.description = 'Redpanda Connect API specification'; + } + + // Additional metadata expected by tests + if (tag || normalizedTag) { + bundle.info['x-redpanda-core-version'] = tag || normalizedTag || normalizedVersion; + } + bundle.info['x-generated-at'] = new Date().toISOString(); + bundle.info['x-generator'] = 'redpanda-docs-openapi-bundler'; + + // Sort keys for deterministic output + const sortedBundle = sortObjectKeys(bundle); + + // Write back to file + fs.writeFileSync(filePath, yaml.stringify(sortedBundle, { + lineWidth: 0, + minContentWidth: 0, + indent: 2 + }), 'utf8'); + + if (!quiet) { + console.log(`📝 Updated bundle metadata: version=${normalizedVersion}`); + } + + return sortedBundle; + + } catch (error) { + throw new Error(`Post-processing failed: ${error.message}`); + } +} + +/** + * Main function to bundle OpenAPI documents + * @param {Object} options - Configuration options + * @param {string} options.tag - Git tag to checkout (e.g., 'v25.1.1') + * @param {string} options.surface - API surface ('admin', 'connect', or 'both') + * @param {string} [options.output] - Output file path (standalone mode) + * @param {string} [options.outAdmin] - Output path for admin API (doc-tools mode) + * @param {string} [options.outConnect] - Output path for connect API (doc-tools mode) + * @param {string} [options.repo] - Repository URL (defaults to redpanda-data/redpanda) + * @param {string} [options.adminMajor] - Admin API major version + * @param {boolean} [options.quiet] - Suppress output + */ +async function bundleOpenAPI(options) { + const { tag, surface, output, outAdmin, outConnect, repo, adminMajor, useAdminMajorVersion, quiet = false } = options; + + // Validate required parameters + if (!tag) { + throw new Error('Git tag is required'); + } + + if (!surface || !['admin', 'connect', 'both'].includes(surface)) { + throw new Error('API surface must be "admin", "connect", or "both"'); + } + + // Handle different surface options + const surfaces = surface === 'both' ? ['admin', 'connect'] : [surface]; + const results = []; + + const tempDir = fs.mkdtempSync(path.join(process.cwd(), 'openapi-bundle-')); + + // Set up cleanup handlers + const cleanup = () => { + try { + if (fs.existsSync(tempDir)) { + fs.rmSync(tempDir, { recursive: true, force: true }); + } + } catch (error) { + console.error(`Warning: Failed to cleanup temporary directory: ${error.message}`); + } + }; + + // Handle graceful shutdown + process.on('SIGINT', cleanup); + process.on('SIGTERM', cleanup); + process.on('uncaughtException', cleanup); + + try { + // Clone repository (only once for all surfaces) + if (!quiet) { + console.log('📥 Cloning redpanda repository...'); + } + + const repositoryUrl = repo || 'https://github.com/redpanda-data/redpanda.git'; + + try { + execSync(`git clone --depth 1 --branch ${tag} ${repositoryUrl} redpanda`, { + cwd: tempDir, + stdio: quiet ? 'ignore' : 'inherit', + timeout: 60000 // 1 minute timeout + }); + } catch (cloneError) { + throw new Error(`Failed to clone repository: ${cloneError.message}`); + } + + const repoDir = path.join(tempDir, 'redpanda'); + + // Verify repository was cloned + if (!fs.existsSync(repoDir)) { + throw new Error('Repository clone failed - directory not found'); + } + + // Run buf generate + if (!quiet) { + console.log('🔧 Running buf generate...'); + } + + try { + execSync('buf generate --template buf.gen.openapi.yaml', { + cwd: repoDir, + stdio: quiet ? 'ignore' : 'inherit', + timeout: 120000 // 2 minutes timeout + }); + } catch (bufError) { + throw new Error(`buf generate failed: ${bufError.message}`); + } + + // Process each surface + for (const currentSurface of surfaces) { + // Determine output path based on mode (standalone vs doc-tools integration) + let finalOutput; + if (output) { + // Standalone mode with explicit output + finalOutput = output; + } else if (currentSurface === 'admin' && outAdmin) { + // Doc-tools mode with admin output + finalOutput = outAdmin; + } else if (currentSurface === 'connect' && outConnect) { + // Doc-tools mode with connect output + finalOutput = outConnect; + } else { + // Default paths + finalOutput = currentSurface === 'admin' + ? 'admin/redpanda-admin-api.yaml' + : 'connect/redpanda-connect-api.yaml'; + } + + if (!quiet) { + console.log(`🚀 Bundling OpenAPI for ${currentSurface} API (tag: ${tag})`); + console.log(`📁 Output: ${finalOutput}`); + } + + // Find OpenAPI fragments + const fragmentFiles = createEntrypoint(repoDir, currentSurface); + + if (!quiet) { + console.log(`📋 Found ${fragmentFiles.length} OpenAPI fragments`); + fragmentFiles.forEach(file => { + const relativePath = path.relative(repoDir, file); + console.log(` ${relativePath}`); + }); + } + + // Detect and use bundler + const bundler = detectBundler(quiet); + + // Bundle the OpenAPI fragments + if (!quiet) { + console.log('🔄 Bundling OpenAPI fragments...'); + } + + const tempOutput = path.join(tempDir, `bundled-${currentSurface}.yaml`); + await runBundler(bundler, fragmentFiles, tempOutput, tempDir, quiet); + + // Post-process the bundle + if (!quiet) { + console.log('📝 Post-processing bundle...'); + } + + const postProcessOptions = { + surface: currentSurface, + tag: tag, + majorMinor: getMajorMinor(normalizeTag(tag)), + adminMajor: adminMajor, + useAdminMajorVersion: useAdminMajorVersion + }; + + postProcessBundle(tempOutput, postProcessOptions, quiet); + + // Move to final output location + const outputDir = path.dirname(finalOutput); + if (!fs.existsSync(outputDir)) { + fs.mkdirSync(outputDir, { recursive: true }); + } + + fs.copyFileSync(tempOutput, finalOutput); + + if (!quiet) { + const stats = fs.statSync(finalOutput); + console.log(`✅ Bundle complete: ${finalOutput} (${Math.round(stats.size / 1024)}KB)`); + } + + results.push({ + surface: currentSurface, + outputPath: finalOutput, + fragmentCount: fragmentFiles.length, + bundler: bundler + }); + } + + return results.length === 1 ? results[0] : results; + + } catch (error) { + if (!quiet) { + console.error(`❌ Bundling failed: ${error.message}`); + } + throw error; + } finally { + cleanup(); + } +} + +// Export functions for testing +module.exports = { + bundleOpenAPI, + normalizeTag, + getMajorMinor, + sortObjectKeys, + detectBundler, + createEntrypoint, + postProcessBundle +}; + +// CLI interface if run directly +if (require.main === module) { + const { program } = require('commander'); + + program + .name('bundle-openapi') + .description('Bundle OpenAPI fragments from Redpanda repository') + .requiredOption('-t, --tag ', 'Git tag to checkout (e.g., v25.1.1)') + .requiredOption('-s, --surface ', 'API surface', (value) => { + if (!['admin', 'connect', 'both'].includes(value)) { + throw new Error('Invalid API surface. Must be "admin", "connect", or "both"'); + } + return value; + }) + .option('-o, --output ', 'Output file path (defaults: admin/redpanda-admin-api.yaml or connect/redpanda-connect-api.yaml)') + .option('--out-admin ', 'Output path for admin API', 'admin/redpanda-admin-api.yaml') + .option('--out-connect ', 'Output path for connect API', 'connect/redpanda-connect-api.yaml') + .option('--repo ', 'Repository URL', 'https://github.com/redpanda-data/redpanda.git') + .option('--admin-major ', 'Admin API major version', 'v2.0.0') + .option('--use-admin-major-version', 'Use admin major version for info.version instead of git tag', false) + .option('-q, --quiet', 'Suppress output', false) + .action(async (options) => { + try { + await bundleOpenAPI(options); + process.exit(0); + } catch (error) { + console.error(`Error: ${error.message}`); + process.exit(1); + } + }); + + program.parse(); +} \ No newline at end of file From d7e4abd91f623eb8d97ad24a9064531533bac7cf Mon Sep 17 00:00:00 2001 From: JakeSCahill Date: Thu, 2 Oct 2025 10:32:57 +0100 Subject: [PATCH 2/9] Apply suggestions --- admin/redpanda-admin-api.yaml | 2 +- bin/doc-tools.js | 30 ++------ cli-utils/install-test-dependencies.sh | 99 ++++++++------------------ package-lock.json | 4 +- package.json | 6 +- 5 files changed, 44 insertions(+), 97 deletions(-) diff --git a/admin/redpanda-admin-api.yaml b/admin/redpanda-admin-api.yaml index cddd246..7fb16d0 100644 --- a/admin/redpanda-admin-api.yaml +++ b/admin/redpanda-admin-api.yaml @@ -1290,7 +1290,7 @@ info: title: Redpanda Admin API version: v2.0.0 x-admin-api-major: v2.0.0 - x-generated-at: 2025-10-02T09:04:11.998Z + x-generated-at: 2025-10-02T09:31:56.030Z x-generator: redpanda-docs-openapi-bundler x-redpanda-core-version: dev openapi: 3.1.0 diff --git a/bin/doc-tools.js b/bin/doc-tools.js index 8bfe9dc..8b975c3 100755 --- a/bin/doc-tools.js +++ b/bin/doc-tools.js @@ -1541,8 +1541,8 @@ automation }); automation - .command('bundle-admin-api') - .description('Bundle Redpanda OpenAPI fragments into complete OpenAPI 3.1 documents') + .command('bundle-openapi') + .description('Bundle Redpanda OpenAPI fragments for admin and connect APIs into complete OpenAPI 3.1 documents') .requiredOption('-t, --tag ', 'Git tag to check out (e.g., v24.3.2 or 24.3.2 or dev)') .option('--repo ', 'Repository URL', 'https://github.com/redpanda-data/redpanda.git') .addOption(new Option('-s, --surface ', 'Which API surface(s) to bundle').choices(['admin', 'connect', 'both']).makeOptionMandatory()) @@ -1556,28 +1556,12 @@ automation requireCmd('git', 'Install Git: https://git-scm.com/downloads'); requireCmd('buf', 'Install buf: https://buf.build/docs/installation'); - // Check for OpenAPI bundler (swagger-cli or redocly) - let bundlerAvailable = false; + // Check for OpenAPI bundler using the existing detectBundler function try { - execSync('swagger-cli --version', { stdio: 'ignore' }); - bundlerAvailable = true; - } catch { - try { - execSync('redocly --version', { stdio: 'ignore' }); - bundlerAvailable = true; - } catch { - try { - execSync('npx redocly --version', { stdio: 'ignore' }); - bundlerAvailable = true; - } catch { - fail('OpenAPI bundler not found. Install one of:\n' + - ' npm install -g swagger-cli\n' + - ' or\n' + - ' npm install -g @redocly/cli\n' + - ' or\n' + - ' npm install @redocly/cli (local)'); - } - } + const { detectBundler } = require('../tools/bundle-openapi.js'); + detectBundler(true); // quiet mode to avoid duplicate output + } catch (err) { + fail(err.message); } try { diff --git a/cli-utils/install-test-dependencies.sh b/cli-utils/install-test-dependencies.sh index 042d6db..3992dfd 100755 --- a/cli-utils/install-test-dependencies.sh +++ b/cli-utils/install-test-dependencies.sh @@ -146,7 +146,7 @@ install_make() { sudo apt-get update && sudo apt-get install build-essential -y || sudo yum groupinstall "Development Tools" -y || { log_error "Failed to install make"; exit 1; } ;; Darwin) - if ! command_exists xcode-select; then + if ! xcode-select -p &>/dev/null; then xcode-select --install || { log_error "Failed to install Xcode Command Line Tools"; exit 1; } fi ;; @@ -405,84 +405,47 @@ ensure_dependencies_installed() { # Ensure all dependencies are installed log_info "Installing/checking dependencies for doc-tools CLI commands..." ensure_dependencies_installed -# Function to check rpk installation and display its version -check_rpk_installed() { + +# Function to install rpk for rpcn-connector-docs command +install_rpk() { if command_exists rpk; then log_info "rpk is already installed. Version information:" rpk --version return 0 - else - return 1 - fi -} - -# Function to install rpk -install_rpk() { - # Check if rpk is already installed - if check_rpk_installed; then - return 0 fi - + log_info "Installing rpk..." - # Determine OS and architecture - OS="$(uname -s)" - ARCH="$(uname -m)" - - # Check if running on macOS and use Homebrew to install rpk - if [ "${OS}" == "Darwin" ]; then - log_info "Detected macOS. Attempting to install rpk using Homebrew..." - - # Check if Homebrew is installed - if ! command_exists brew; then - log_error "Homebrew not found. Please install Homebrew first: https://brew.sh" - exit 1 - fi - - # Install rpk - brew install redpanda-data/tap/redpanda || { log_error "Failed to install rpk via Homebrew"; exit 1; } - - # Verify installation - log_info "rpk has been installed. Version information:" - rpk --version - return 0 - fi - - # For Linux systems - if [ "${OS}" == "Linux" ]; then - FILENAME="rpk-linux-amd64.zip" - URL_BASE="https://github.com/redpanda-data/redpanda/releases" - - # Download latest version of rpk - log_info "Downloading ${FILENAME}..." - curl -Lf --retry 3 -O "${URL_BASE}/latest/download/${FILENAME}" \ - || { log_error "Failed to download rpk"; exit 1; } - - # Ensure the target directory exists - mkdir -p $HOME/.local/bin || { log_error "Failed to create directory"; exit 1; } - - # Unzip the rpk binary to the target directory - unzip -o "${FILENAME}" -d $HOME/.local/bin || { log_error "Failed to unzip rpk"; exit 1; } - - # Remove the downloaded archive - rm "${FILENAME}" || { log_error "Failed to remove downloaded archive"; exit 1; } - - # Add the target directory to PATH for the current session + # Try to install rpk using the installation script + if curl -LO https://github.com/redpanda-data/redpanda/releases/latest/download/rpk-linux-amd64.zip; then + unzip rpk-linux-amd64.zip + mkdir -p ~/.local/bin + mv rpk ~/.local/bin/ + rm rpk-linux-amd64.zip + + # Add to PATH for current session export PATH=$HOME/.local/bin:$PATH - + # Add the target directory to PATH for future sessions - echo 'export PATH=$HOME/.local/bin:$PATH' >> ~/.bashrc - source ~/.bashrc - + if ! grep -q 'export PATH=$HOME/.local/bin:$PATH' ~/.bashrc 2>/dev/null; then + echo 'export PATH=$HOME/.local/bin:$PATH' >> ~/.bashrc + fi + # Verify installation - log_info "rpk has been installed. Version information:" - rpk --version - return 0 + if command_exists rpk; then + log_info "rpk has been installed successfully. Version information:" + rpk --version + return 0 + else + log_warn "rpk installation may have failed. Please install manually:" + log_warn "https://docs.redpanda.com/current/get-started/rpk-install/" + return 1 + fi + else + log_warn "Failed to download rpk. Please install manually:" + log_warn "https://docs.redpanda.com/current/get-started/rpk-install/" + return 1 fi - - log_error "Unsupported operating system: ${OS}" - log_error "Please install rpk manually: https://docs.redpanda.com/current/get-started/rpk-install/" - exit 1 } # Install rpk for rpcn-connector-docs command diff --git a/package-lock.json b/package-lock.json index 20d1fa5..b91d1be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@redpanda-data/docs-extensions-and-macros", - "version": "4.9.1", + "version": "4.10.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@redpanda-data/docs-extensions-and-macros", - "version": "4.9.1", + "version": "4.10.0", "license": "ISC", "dependencies": { "@asciidoctor/tabs": "^1.0.0-beta.6", diff --git a/package.json b/package.json index b450f5f..8f91203 100644 --- a/package.json +++ b/package.json @@ -22,9 +22,9 @@ "build": "antora --to-dir docs --fetch local-antora-playbook.yml", "serve": "wds --node-resolve --open preview/test/ --watch --root-dir docs", "test": "jest", - "bundle:admin": "doc-tools generate bundle-admin-api --surface admin", - "bundle:connect": "doc-tools generate bundle-admin-api --surface connect", - "bundle:both": "doc-tools generate bundle-admin-api --surface both" + "bundle:admin": "doc-tools generate bundle-openapi --surface admin", + "bundle:connect": "doc-tools generate bundle-openapi --surface connect", + "bundle:both": "doc-tools generate bundle-openapi --surface both" }, "contributors": [ { From 38b6a24d493557bee2d7af0ecca4200922645704 Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Thu, 2 Oct 2025 10:36:37 +0100 Subject: [PATCH 3/9] =?UTF-8?q?=F0=9F=93=9D=20Add=20docstrings=20to=20`DOC?= =?UTF-8?q?-1705`=20(#135)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Docstrings generation was requested by @JakeSCahill. * https://github.com/redpanda-data/docs-extensions-and-macros/pull/134#issuecomment-3360140978 The following files were modified: * `cli-utils/install-test-dependencies.sh` * `tools/bundle-openapi.js` Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- cli-utils/install-test-dependencies.sh | 38 +++++---- tools/bundle-openapi.js | 113 ++++++++++++++++--------- 2 files changed, 95 insertions(+), 56 deletions(-) mode change 100755 => 100644 cli-utils/install-test-dependencies.sh diff --git a/cli-utils/install-test-dependencies.sh b/cli-utils/install-test-dependencies.sh old mode 100755 new mode 100644 index 3992dfd..a50e3f4 --- a/cli-utils/install-test-dependencies.sh +++ b/cli-utils/install-test-dependencies.sh @@ -7,17 +7,19 @@ GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color -# Function to log colored output +# log_info prints a green "[INFO]"-prefixed message to stdout. log_info() { echo -e "${GREEN}[INFO]${NC} $1"; } +# log_warn prints a warning message prefixed with "[WARN]" in yellow to stdout. log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } +# log_error echoes the provided message prefixed with a red `[ERROR]` tag. log_error() { echo -e "${RED}[ERROR]${NC} $1"; } -# Function to check if a command exists +# command_exists checks whether the specified command is available on PATH. command_exists() { command -v "$1" &>/dev/null } -# Function to install Node.js +# install_node checks for Node.js and enforces a minimum version of 22.20.0; if Node.js is missing or below the required version it logs upgrade/install instructions and returns non-zero. install_node() { local required_version="22.20.0" @@ -51,7 +53,7 @@ install_node() { fi } -# Function to install Rust +# install_rust ensures the Rust toolchain is installed and configures the current shell environment. install_rust() { if command_exists rustc; then log_info "Rust is already installed. Version: $(rustc --version)" @@ -63,7 +65,7 @@ install_rust() { fi } -# Function to install Git +# install_git installs Git if it is not present, using apt/yum on Linux or Homebrew on macOS. It logs the installed version on success and logs an error and exits with a non-zero status if the OS is unsupported or installation fails. install_git() { if command_exists git; then log_info "Git is already installed. Version: $(git --version)" @@ -90,7 +92,8 @@ install_git() { fi } -# Function to install buf (Protocol Buffer tool) +# install_buf installs the buf Protocol Buffer CLI tool when it is not already available. +# On Linux it places a released buf binary into /usr/local/bin; on macOS it uses Homebrew if present. install_buf() { if command_exists buf; then log_info "buf is already installed. Version: $(buf --version)" @@ -121,7 +124,7 @@ install_buf() { fi } -# Function to install Docker +# install_docker checks whether Docker is installed; logs the installed version and warns if the Docker daemon is not running. If Docker is not found, logs the manual installation URL and notes that Docker is required for the metrics-docs and rpk-docs commands. install_docker() { if command_exists docker; then log_info "Docker is already installed. Version: $(docker --version)" @@ -135,7 +138,7 @@ install_docker() { fi } -# Function to install make +# install_make ensures `make` is available, installing build-essential (or Development Tools) on Linux or the Xcode Command Line Tools on macOS, and exits with an error for unsupported OS or failed installation. install_make() { if command_exists make; then log_info "make is already installed. Version: $(make --version | head -1)" @@ -159,7 +162,7 @@ install_make() { fi } -# Function to install Python3 +# install_python ensures Python3 and pip are available. It logs an existing python/python3 version if present; otherwise installs `python3` and `python3-pip` on Linux (apt-get or yum) or via Homebrew on macOS, and exits with an error for unsupported systems or failed installations. install_python() { if command_exists python3; then log_info "Python3 is already installed. Version: $(python3 --version)" @@ -188,7 +191,7 @@ install_python() { fi } -# Function to install OpenAPI bundlers +# install_openapi_bundlers ensures an OpenAPI bundler (Redocly CLI or swagger-cli) is available by installing `@redocly/cli` globally and falling back to `swagger-cli` if needed. It exits with a non-zero status if neither bundler can be installed. install_openapi_bundlers() { local bundler_found=false @@ -225,7 +228,7 @@ install_openapi_bundlers() { fi } -# Function to install pandoc (for helm-spec command) +# install_pandoc ensures pandoc is available on the system, installing it via apt/yum on Linux or Homebrew on macOS, and exits with an error message on failure. install_pandoc() { if command_exists pandoc; then log_info "pandoc is already installed. Version: $(pandoc --version | head -1)" @@ -252,7 +255,7 @@ install_pandoc() { fi } -# Function to install helm-docs (for helm-spec command) +# install_helm_docs checks for helm-docs and, if missing, logs installation instructions and macOS/Homebrew guidance for the helm-spec command. install_helm_docs() { if command_exists helm-docs; then log_info "helm-docs is already installed. Version: $(helm-docs --version)" @@ -269,7 +272,7 @@ install_helm_docs() { fi } -# Function to install crd-ref-docs (for crd-spec command) +# install_crd_ref_docs checks for the `crd-ref-docs` tool and logs whether it is installed or provides a manual install URL required by the `crd-spec` command. install_crd_ref_docs() { if command_exists crd-ref-docs; then log_info "crd-ref-docs is already installed" @@ -279,7 +282,7 @@ install_crd_ref_docs() { fi } -# Function to install Go (for crd-spec command) +# install_go checks whether Go is installed and logs its version; if missing, it logs OS-specific installation guidance and a download URL for use by the crd-spec command. install_go() { if command_exists go; then log_info "Go is already installed. Version: $(go version)" @@ -299,7 +302,7 @@ install_go() { fi } -# Function to install curl and tar (for metrics-docs, rpk-docs) +# install_basic_tools ensures `curl` and `tar` are present; installs them on Linux using `apt-get` or `yum`, and on macOS uses Homebrew for `curl` (noting that `tar` is typically preinstalled). Exits with a non-zero status if an attempted automatic installation fails. install_basic_tools() { if ! command_exists curl; then log_info "Installing curl..." @@ -332,7 +335,8 @@ install_basic_tools() { fi } -# Function to check if expect and jq are installed and install them if they're not +# ensure_dependencies_installed checks for `expect` and `jq`, installs them if missing, and orchestrates verification and installation of core and optional tooling required by the doc-tools CLI. +# It runs installers for Node.js, Rust, Git, buf, Docker, make, Python, basic tools, OpenAPI bundlers, pandoc, and optional helpers (helm-docs, crd-ref-docs, Go), and will emit a warning if the Node.js version requirement is not met. ensure_dependencies_installed() { if ! command_exists expect; then log_info "Installing expect..." @@ -406,7 +410,7 @@ ensure_dependencies_installed() { log_info "Installing/checking dependencies for doc-tools CLI commands..." ensure_dependencies_installed -# Function to install rpk for rpcn-connector-docs command +# install_rpk installs Redpanda's rpk CLI into ~/.local/bin by downloading the latest Linux amd64 release, adding it to PATH for the current and future sessions, and verifying the installation; returns 0 on success and non-zero on failure. install_rpk() { if command_exists rpk; then log_info "rpk is already installed. Version information:" diff --git a/tools/bundle-openapi.js b/tools/bundle-openapi.js index 3a2b10c..2e0764c 100644 --- a/tools/bundle-openapi.js +++ b/tools/bundle-openapi.js @@ -6,9 +6,15 @@ const { execSync, spawnSync } = require('child_process'); const yaml = require('yaml'); /** - * Normalize git tag by removing 'v' prefix if present - * @param {string} tag - Git tag like 'v25.1.1' or '25.1.1' - * @returns {string} Normalized version like '25.1.1' + * Normalize a Git tag into a semantic version string. + * + * Trims surrounding whitespace, returns 'dev' unchanged, removes a leading 'v' if present, + * and validates that the result matches MAJOR.MINOR.PATCH with optional pre-release/build metadata. + * Throws if the input is not a non-empty string or does not conform to the expected version format. + * + * @param {string} tag - Git tag (e.g., 'v25.1.1', '25.1.1', or 'dev'). + * @returns {string} Normalized version (e.g., '25.1.1' or 'dev'). + * @throws {Error} If `tag` is not a non-empty string or does not match the semantic version pattern. */ function normalizeTag(tag) { if (!tag || typeof tag !== 'string') { @@ -40,9 +46,13 @@ function normalizeTag(tag) { } /** - * Extract major.minor version from semantic version - * @param {string} version - Version like '25.1.1' - * @returns {string} Major.minor version like '25.1' + * Return the major.minor portion of a semantic version string. + * + * Accepts a semantic version like `25.1.1` and yields `25.1`. The special value + * `'dev'` is returned unchanged. + * @param {string} version - Semantic version (e.g., `'25.1.1'`) or `'dev'`. + * @returns {string} The `major.minor` string (e.g., `'25.1'`) or `'dev'`. + * @throws {Error} If `version` is not a non-empty string, lacks major/minor parts, or if major/minor are not numeric. */ function getMajorMinor(version) { if (!version || typeof version !== 'string') { @@ -69,9 +79,10 @@ function getMajorMinor(version) { } /** - * Sort object keys recursively for deterministic output - * @param {any} obj - Object to sort - * @returns {any} Object with sorted keys + * Produce a new value with object keys sorted recursively for deterministic output. + * Non-objects are returned unchanged; arrays are processed element-wise. + * @param {*} obj - Value to normalize; may be an object, array, or any primitive. + * @returns {*} A new value where any objects have their keys sorted lexicographically. */ function sortObjectKeys(obj) { if (obj === null || typeof obj !== 'object') { @@ -153,11 +164,14 @@ function detectBundler(quiet = false) { } /** - * Create an index.yaml file that references all Admin API fragments - * @param {string} tempDir - Temporary directory path - * @param {string} surface - API surface ('admin' or 'connect') - * @param {boolean} quiet - Suppress output - * @returns {string[]} Array of fragment file paths + * Collects file paths of OpenAPI fragment files for the specified API surface. + * + * @param {string} tempDir - Path to a temporary repository workspace that contains generated OpenAPI fragments (must exist). + * @param {'admin'|'connect'} apiSurface - API surface to scan; either `'admin'` or `'connect'`. + * @returns {string[]} Array of full paths to discovered fragment files (*.openapi.yaml / *.openapi.yml). + * @throws {Error} If tempDir is missing or does not exist. + * @throws {Error} If apiSurface is not 'admin' or 'connect'. + * @throws {Error} If no OpenAPI fragment files are found. */ function createEntrypoint(tempDir, apiSurface) { // Validate input parameters @@ -243,13 +257,18 @@ function createEntrypoint(tempDir, apiSurface) { } /** - * Bundle OpenAPI fragments using external bundler - * @param {string} bundler - 'swagger-cli', 'redocly', 'npx redocly', or 'npx @redocly/cli' - * @param {string[]|string} fragmentFiles - Array of fragment file paths or single entrypoint - * @param {string} outputPath - Path for bundled output - * @param {string} tempDir - Temporary directory for creating merged file - * @param {boolean} quiet - Suppress output - * @throws {Error} If bundling fails + * Bundle one or more OpenAPI fragment files into a single bundled YAML using a selected external bundler. + * + * Merges multiple fragment files into a temporary single entrypoint when required, invokes the specified bundler + * executable (supported values: 'swagger-cli', 'redocly', 'npx redocly', 'npx @redocly/cli'), and writes the bundled + * output to the given outputPath. Ensures the output directory exists and verifies the produced file is non-empty. + * + * @param {string} bundler - The bundler to invoke: 'swagger-cli', 'redocly', 'npx redocly', or 'npx @redocly/cli'. + * @param {string[]|string} fragmentFiles - Array of fragment file paths to merge or a single entrypoint file path. + * @param {string} outputPath - Filesystem path where the bundled OpenAPI YAML will be written. + * @param {string} tempDir - Existing temporary directory used to create a merged entrypoint when multiple fragments are provided. + * @param {boolean} [quiet=false] - If true, suppresses console output from this function and child process stdio. + * @throws {Error} If input validation fails, the bundler process times out or exits with an error, or the output file is missing or empty. */ function runBundler(bundler, fragmentFiles, outputPath, tempDir, quiet = false) { if (!bundler || typeof bundler !== 'string') { @@ -422,14 +441,23 @@ function runBundler(bundler, fragmentFiles, outputPath, tempDir, quiet = false) } /** - * Post-process the bundled OpenAPI document - * @param {string} filePath - Path to bundled YAML file - * @param {Object} options - Processing options - * @param {string} options.surface - API surface ('admin' or 'connect') - * @param {string} options.tag - Git tag for versioning - * @param {string} options.majorMinor - Major.minor version - * @param {string} [options.adminMajor] - Admin API major version - * @param {boolean} quiet - Suppress output + * Update bundle metadata, enforce a deterministic key order, and rewrite the bundled OpenAPI YAML. + * + * Reads the bundled YAML at `filePath`, validates and augments its `info` object (titles, descriptions, + * version fields and x- metadata) based on `options.surface` and provided version information, sorts + * object keys deterministically, and writes the updated YAML back to `filePath`. + * + * @param {string} filePath - Path to the bundled OpenAPI YAML file to process. + * @param {Object} options - Processing options. + * @param {'admin'|'connect'} options.surface - API surface to target; affects title and description. + * @param {string} [options.tag] - Git tag used for versioning (may be normalized internally). + * @param {string} [options.normalizedTag] - Pre-normalized version string to use instead of `tag`. + * @param {string} [options.majorMinor] - Major.minor version to set in `info.version`. + * @param {string} [options.adminMajor] - Admin API major version to set as `x-admin-api-major`. + * @param {boolean} [options.useAdminMajorVersion] - When true and surface is 'admin', prefer `adminMajor` for `info.version`. + * @param {boolean} [quiet=false] - Suppress console output when true. + * @returns {Object} The processed OpenAPI bundle object with keys sorted deterministically. + * @throws {Error} If inputs are missing/invalid, the file is absent or empty, YAML parsing fails, or processing cannot complete. */ function postProcessBundle(filePath, options, quiet = false) { if (!filePath || typeof filePath !== 'string') { @@ -531,16 +559,23 @@ function postProcessBundle(filePath, options, quiet = false) { } /** - * Main function to bundle OpenAPI documents - * @param {Object} options - Configuration options - * @param {string} options.tag - Git tag to checkout (e.g., 'v25.1.1') - * @param {string} options.surface - API surface ('admin', 'connect', or 'both') - * @param {string} [options.output] - Output file path (standalone mode) - * @param {string} [options.outAdmin] - Output path for admin API (doc-tools mode) - * @param {string} [options.outConnect] - Output path for connect API (doc-tools mode) - * @param {string} [options.repo] - Repository URL (defaults to redpanda-data/redpanda) - * @param {string} [options.adminMajor] - Admin API major version - * @param {boolean} [options.quiet] - Suppress output + * Bundle OpenAPI fragments for the specified API surface(s) from a repository tag and write the resulting bundled YAML files to disk. + * + * @param {Object} options - Configuration options. + * @param {string} options.tag - Git tag to checkout (e.g., 'v25.1.1'). + * @param {'admin'|'connect'|'both'} options.surface - API surface to process. + * @param {string} [options.output] - Standalone output file path; when provided, used for the single output file. + * @param {string} [options.outAdmin] - Output path for the admin API when integrating with doc-tools mode. + * @param {string} [options.outConnect] - Output path for the connect API when integrating with doc-tools mode. + * @param {string} [options.repo] - Repository URL to clone (defaults to https://github.com/redpanda-data/redpanda.git). + * @param {string} [options.adminMajor] - Admin API major version string used for metadata (e.g., 'v2.0.0'). + * @param {boolean} [options.useAdminMajorVersion] - When true and processing the admin surface, use `adminMajor` for the bundle info.version. + * @param {boolean} [options.quiet=false] - Suppress logging to stdout/stderr when true. + * @returns {Object|Object[]} An object (for a single surface) or an array of objects (for both surfaces) with fields: + * - surface: processed surface name ('admin' or 'connect'), + * - outputPath: final written file path, + * - fragmentCount: number of OpenAPI fragment files processed, + * - bundler: name or command of the bundler used. */ async function bundleOpenAPI(options) { const { tag, surface, output, outAdmin, outConnect, repo, adminMajor, useAdminMajorVersion, quiet = false } = options; From fa450f9cd6278ddc95c7ece2be9fe09690149301 Mon Sep 17 00:00:00 2001 From: JakeSCahill Date: Thu, 2 Oct 2025 10:45:19 +0100 Subject: [PATCH 4/9] Remove openapi spec --- TESTING.md | 93 ++ admin/redpanda-admin-api.yaml | 1580 --------------------------------- 2 files changed, 93 insertions(+), 1580 deletions(-) create mode 100644 TESTING.md delete mode 100644 admin/redpanda-admin-api.yaml diff --git a/TESTING.md b/TESTING.md new file mode 100644 index 0000000..b4be073 --- /dev/null +++ b/TESTING.md @@ -0,0 +1,93 @@ +# Testing Instructions + +## 1. Install dependencies: +```bash +npm install +``` + +## 2. Link the package for local testing: +```bash +npm link +``` + +## 3. Install CLI dependencies: +```bash +# Install all required dependencies for CLI commands +./cli-utils/install-test-dependencies.sh + +# Or manually install core dependencies: +npm install -g @redocly/cli # or swagger-cli +brew install buf git # or appropriate package manager +``` + +## 4. Verify CLI command renamed correctly: +```bash +# Check that the old command is gone and new one exists +doc-tools generate --help + +# Should show 'bundle-openapi' command instead of 'bundle-admin-api' +``` + +## 5. Test OpenAPI bundling functionality: +```bash +# Test admin API bundling +doc-tools generate bundle-openapi --surface admin --tag v25.1.1 + +# Test connect API bundling +doc-tools generate bundle-openapi --surface connect --tag v25.1.1 + +# Test both APIs +doc-tools generate bundle-openapi --surface both --tag v25.1.1 + +# Test with version override +doc-tools generate bundle-openapi --surface admin --tag v25.1.1 --use-admin-major-version +``` + +## 6. Test npm script shortcuts: +```bash +# These should use the new command name +npm run bundle:admin +npm run bundle:connect +npm run bundle:both +``` + +## 7. Test bundler detection: +```bash +# Should automatically detect and use available bundlers +# (swagger-cli, @redocly/cli, or fallback to npx variants) +doc-tools generate bundle-openapi --surface admin --tag v25.1.1 --quiet +``` + +## 8. Run test suite: +```bash +npm test +``` + +## 9. Verify error handling: +```bash +# Test with invalid tag +doc-tools generate bundle-openapi --surface admin --tag invalid-tag + +# Test with missing dependencies (if you want to test error paths) +# Temporarily rename bundler and test error message +``` + +## 10. Test standalone tool: +```bash +# Test the standalone bundle-openapi.js directly +node tools/bundle-openapi.js --tag v25.1.1 --surface admin +``` + +## Expected Results: +- ✅ All tests pass (86/86) +- ✅ CLI command renamed from `bundle-admin-api` to `bundle-openapi` +- ✅ Command description updated to mention both admin and connect APIs +- ✅ Bundler detection uses shared `detectBundler()` function (no duplication) +- ✅ Generated OpenAPI files contain proper metadata and structure +- ✅ npm scripts work with new command name +- ✅ No breaking changes to existing functionality + +## Files to Check: +- `admin/redpanda-admin-api.yaml` - Generated admin API spec +- `connect/redpanda-connect-api.yaml` - Generated connect API spec (if tested) +- Verify proper version metadata in generated files \ No newline at end of file diff --git a/admin/redpanda-admin-api.yaml b/admin/redpanda-admin-api.yaml deleted file mode 100644 index 7fb16d0..0000000 --- a/admin/redpanda-admin-api.yaml +++ /dev/null @@ -1,1580 +0,0 @@ -components: - schemas: - connect-protocol-version: - const: 1 - description: Define the version of the Connect protocol - enum: - - 1 - title: Connect-Protocol-Version - type: number - connect-timeout-header: - description: Define the timeout, in ms - title: Connect-Timeout-Ms - type: number - connect.error: - additionalProperties: true - description: "Error type returned by Connect: https://connectrpc.com/docs/go/errors/#http-representation" - properties: - code: - description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. - enum: - - canceled - - unknown - - invalid_argument - - deadline_exceeded - - not_found - - already_exists - - permission_denied - - resource_exhausted - - failed_precondition - - aborted - - out_of_range - - unimplemented - - internal - - unavailable - - data_loss - - unauthenticated - examples: - - not_found - type: string - details: - description: A list of messages that carry the error details. There is no limit on the number of messages. - items: - $ref: "#/components/schemas/connect.error_details.Any" - type: array - message: - description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. - type: string - title: Connect Error - type: object - connect.error_details.Any: - additionalProperties: true - description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message, with an additional debug field for ConnectRPC error details. - properties: - debug: - description: Deserialized error detail payload. The 'type' field indicates the schema. This field is for easier debugging and should not be relied upon for application logic. - discriminator: - propertyName: type - oneOf: - - additionalProperties: true - description: Detailed error information. - title: Any - type: object - title: Debug - type: - description: "A URL that acts as a globally unique identifier for the type of the serialized message. For example: `type.googleapis.com/google.rpc.ErrorInfo`. This is used to determine the schema of the data in the `value` field and is the discriminator for the `debug` field." - type: string - value: - description: The Protobuf message, serialized as bytes and base64-encoded. The specific message type is identified by the `type` field. - format: binary - type: string - type: object - google.protobuf.Duration: - description: |- - A Duration represents a signed, fixed-length span of time represented - as a count of seconds and fractions of seconds at nanosecond - resolution. It is independent of any calendar and concepts like "day" - or "month". It is related to Timestamp in that the difference between - two Timestamp values is a Duration and it can be added or subtracted - from a Timestamp. Range is approximately +-10,000 years. - - # Examples - - Example 1: Compute Duration from two Timestamps in pseudo code. - - Timestamp start = ...; - Timestamp end = ...; - Duration duration = ...; - - duration.seconds = end.seconds - start.seconds; - duration.nanos = end.nanos - start.nanos; - - if (duration.seconds < 0 && duration.nanos > 0) { - duration.seconds += 1; - duration.nanos -= 1000000000; - } else if (duration.seconds > 0 && duration.nanos < 0) { - duration.seconds -= 1; - duration.nanos += 1000000000; - } - - Example 2: Compute Timestamp from Timestamp + Duration in pseudo code. - - Timestamp start = ...; - Duration duration = ...; - Timestamp end = ...; - - end.seconds = start.seconds + duration.seconds; - end.nanos = start.nanos + duration.nanos; - - if (end.nanos < 0) { - end.seconds -= 1; - end.nanos += 1000000000; - } else if (end.nanos >= 1000000000) { - end.seconds += 1; - end.nanos -= 1000000000; - } - - Example 3: Compute Duration from datetime.timedelta in Python. - - td = datetime.timedelta(days=3, minutes=10) - duration = Duration() - duration.FromTimedelta(td) - - # JSON Mapping - - In JSON format, the Duration type is encoded as a string rather than an - object, where the string ends in the suffix "s" (indicating seconds) and - is preceded by the number of seconds, with nanoseconds expressed as - fractional seconds. For example, 3 seconds with 0 nanoseconds should be - encoded in JSON format as "3s", while 3 seconds and 1 nanosecond should - be expressed in JSON format as "3.000000001s", and 3 seconds and 1 - microsecond should be expressed in JSON format as "3.000001s". - format: duration - type: string - google.protobuf.FieldMask: - description: |- - `FieldMask` represents a set of symbolic field paths, for example: - - paths: "f.a" - paths: "f.b.d" - - Here `f` represents a field in some root message, `a` and `b` - fields in the message found in `f`, and `d` a field found in the - message in `f.b`. - - Field masks are used to specify a subset of fields that should be - returned by a get operation or modified by an update operation. - Field masks also have a custom JSON encoding (see below). - - # Field Masks in Projections - - When used in the context of a projection, a response message or - sub-message is filtered by the API to only contain those fields as - specified in the mask. For example, if the mask in the previous - example is applied to a response message as follows: - - f { - a : 22 - b { - d : 1 - x : 2 - } - y : 13 - } - z: 8 - - The result will not contain specific values for fields x,y and z - (their value will be set to the default, and omitted in proto text - output): - - - f { - a : 22 - b { - d : 1 - } - } - - A repeated field is not allowed except at the last position of a - paths string. - - If a FieldMask object is not present in a get operation, the - operation applies to all fields (as if a FieldMask of all fields - had been specified). - - Note that a field mask does not necessarily apply to the - top-level response message. In case of a REST get operation, the - field mask applies directly to the response, but in case of a REST - list operation, the mask instead applies to each individual message - in the returned resource list. In case of a REST custom method, - other definitions may be used. Where the mask applies will be - clearly documented together with its declaration in the API. In - any case, the effect on the returned resource/resources is required - behavior for APIs. - - # Field Masks in Update Operations - - A field mask in update operations specifies which fields of the - targeted resource are going to be updated. The API is required - to only change the values of the fields as specified in the mask - and leave the others untouched. If a resource is passed in to - describe the updated values, the API ignores the values of all - fields not covered by the mask. - - If a repeated field is specified for an update operation, new values will - be appended to the existing repeated field in the target resource. Note that - a repeated field is only allowed in the last position of a `paths` string. - - If a sub-message is specified in the last position of the field mask for an - update operation, then new value will be merged into the existing sub-message - in the target resource. - - For example, given the target message: - - f { - b { - d: 1 - x: 2 - } - c: [1] - } - - And an update message: - - f { - b { - d: 10 - } - c: [2] - } - - then if the field mask is: - - paths: ["f.b", "f.c"] - - then the result will be: - - f { - b { - d: 10 - x: 2 - } - c: [1, 2] - } - - An implementation may provide options to override this default behavior for - repeated and message fields. - - In order to reset a field's value to the default, the field must - be in the mask and set to the default value in the provided resource. - Hence, in order to reset all fields of a resource, provide a default - instance of the resource and set all fields in the mask, or do - not provide a mask as described below. - - If a field mask is not present on update, the operation applies to - all fields (as if a field mask of all fields has been specified). - Note that in the presence of schema evolution, this may mean that - fields the client does not know and has therefore not filled into - the request will be reset to their default. If this is unwanted - behavior, a specific service may require a client to always specify - a field mask, producing an error if not. - - As with get operations, the location of the resource which - describes the updated values in the request message depends on the - operation kind. In any case, the effect of the field mask is - required to be honored by the API. - - ## Considerations for HTTP REST - - The HTTP kind of an update operation which uses a field mask must - be set to PATCH instead of PUT in order to satisfy HTTP semantics - (PUT must only be used for full updates). - - # JSON Encoding of Field Masks - - In JSON, a field mask is encoded as a single string where paths are - separated by a comma. Fields name in each path are converted - to/from lower-camel naming conventions. - - As an example, consider the following message declarations: - - message Profile { - User user = 1; - Photo photo = 2; - } - message User { - string display_name = 1; - string address = 2; - } - - In proto a field mask for `Profile` may look as such: - - mask { - paths: "user.display_name" - paths: "photo" - } - - In JSON, the same mask is represented as below: - - { - mask: "user.displayName,photo" - } - - # Field Masks and Oneof Fields - - Field masks treat fields in oneofs just as regular fields. Consider the - following message: - - message SampleMessage { - oneof test_oneof { - string name = 4; - SubMessage sub_message = 9; - } - } - - The field mask can be: - - mask { - paths: "name" - } - - Or: - - mask { - paths: "sub_message" - } - - Note that oneof type names ("test_oneof" in this case) cannot be used in - paths. - - ## Field Mask Verification - - The implementation of any API method which has a FieldMask type field in the - request should verify the included field paths, and return an - `INVALID_ARGUMENT` error if any path is unmappable. - type: string - google.protobuf.Timestamp: - description: |- - A Timestamp represents a point in time independent of any time zone or local - calendar, encoded as a count of seconds and fractions of seconds at - nanosecond resolution. The count is relative to an epoch at UTC midnight on - January 1, 1970, in the proleptic Gregorian calendar which extends the - Gregorian calendar backwards to year one. - - All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap - second table is needed for interpretation, using a [24-hour linear - smear](https://developers.google.com/time/smear). - - The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By - restricting to that range, we ensure that we can convert to and from [RFC - 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings. - - # Examples - - Example 1: Compute Timestamp from POSIX `time()`. - - Timestamp timestamp; - timestamp.set_seconds(time(NULL)); - timestamp.set_nanos(0); - - Example 2: Compute Timestamp from POSIX `gettimeofday()`. - - struct timeval tv; - gettimeofday(&tv, NULL); - - Timestamp timestamp; - timestamp.set_seconds(tv.tv_sec); - timestamp.set_nanos(tv.tv_usec * 1000); - - Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`. - - FILETIME ft; - GetSystemTimeAsFileTime(&ft); - UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime; - - // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z - // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z. - Timestamp timestamp; - timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL)); - timestamp.set_nanos((INT32) ((ticks % 10000000) * 100)); - - Example 4: Compute Timestamp from Java `System.currentTimeMillis()`. - - long millis = System.currentTimeMillis(); - - Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000) - .setNanos((int) ((millis % 1000) * 1000000)).build(); - - Example 5: Compute Timestamp from Java `Instant.now()`. - - Instant now = Instant.now(); - - Timestamp timestamp = - Timestamp.newBuilder().setSeconds(now.getEpochSecond()) - .setNanos(now.getNano()).build(); - - Example 6: Compute Timestamp from current time in Python. - - timestamp = Timestamp() - timestamp.GetCurrentTime() - - # JSON Mapping - - In JSON format, the Timestamp type is encoded as a string in the - [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the - format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" - where {year} is always expressed using four digits while {month}, {day}, - {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional - seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution), - are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone - is required. A proto3 JSON serializer should always use UTC (as indicated by - "Z") when printing the Timestamp type and a proto3 JSON parser should be - able to accept both UTC and other timezones (as indicated by an offset). - - For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past - 01:30 UTC on January 15, 2017. - - In JavaScript, one can convert a Date object to this format using the - standard - [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) - method. In Python, a standard `datetime.datetime` object can be converted - to this format using - [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with - the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use - the Joda Time's [`ISODateTimeFormat.dateTime()`]( - http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime() - ) to obtain a formatter capable of generating timestamps in this format. - examples: - - 2023-01-15T01:30:15.01Z - - 2024-12-25T12:00:00Z - format: date-time - type: string - redpanda.core.admin.v2.ACLAccessFilter: - additionalProperties: false - description: Filter an ACL based on its access - properties: - host: - description: |- - The host to match. If not set, will default to match all hosts - with the specified `operation` and `permission_type`. Note that - the asterisk `*` is literal and matches hosts that are set to `*` - title: host - type: string - operation: - $ref: "#/components/schemas/redpanda.core.common.ACLOperation" - description: The ACL operation to match - title: operation - permissionType: - $ref: "#/components/schemas/redpanda.core.common.ACLPermissionType" - description: The permission type - title: permission_type - principal: - description: |- - The name of the principal, if not set will default to match - all principals with the specified `operation` and `permission_type` - title: principal - type: string - title: ACLAccessFilter - type: object - redpanda.core.admin.v2.ACLFilter: - additionalProperties: false - description: A filter for ACLs - properties: - accessFilter: - $ref: "#/components/schemas/redpanda.core.admin.v2.ACLAccessFilter" - description: The access filter - title: access_filter - resourceFilter: - $ref: "#/components/schemas/redpanda.core.admin.v2.ACLResourceFilter" - description: The resource filter - title: resource_filter - title: ACLFilter - type: object - redpanda.core.admin.v2.ACLResourceFilter: - additionalProperties: false - description: A filter to match ACLs for resources - properties: - name: - description: |- - Name, if not given will default to match all items in `resource_type`. - Note that asterisk `*` is literal and matches resource ACLs - that are named `*` - title: name - type: string - patternType: - $ref: "#/components/schemas/redpanda.core.common.ACLPattern" - description: The pattern to apply to name - title: pattern_type - resourceType: - $ref: "#/components/schemas/redpanda.core.common.ACLResource" - description: The ACL resource type to match - title: resource_type - title: ACLResourceFilter - type: object - redpanda.core.admin.v2.AdminServer: - additionalProperties: false - description: AdminServer has information about the admin server within the broker. - properties: - routes: - description: All of the ConnectRPC routes available on this admin server. - items: - $ref: "#/components/schemas/redpanda.core.admin.v2.RPCRoute" - title: routes - type: array - title: AdminServer - type: object - redpanda.core.admin.v2.AuthenticationConfiguration: - additionalProperties: false - description: |- - Authentication config. Currently only supporting SASL/SCRAM, - however made as a oneof for expansion - oneOf: - - properties: - scramConfiguration: - $ref: "#/components/schemas/redpanda.core.admin.v2.ScramConfig" - description: SASL/SCRAM configuration - title: scram_configuration - required: - - scramConfiguration - title: scram_configuration - title: AuthenticationConfiguration - type: object - redpanda.core.admin.v2.Broker: - additionalProperties: false - description: The resource for an individual broker within the Kafka Cluster. - properties: - adminServer: - $ref: "#/components/schemas/redpanda.core.admin.v2.AdminServer" - description: The admin server information. - title: admin_server - buildInfo: - $ref: "#/components/schemas/redpanda.core.admin.v2.BuildInfo" - description: The build this broker is running. - title: build_info - nodeId: - description: This broker's node ID. - format: int32 - title: node_id - type: integer - title: Broker - type: object - redpanda.core.admin.v2.BuildInfo: - additionalProperties: false - description: BuildInfo contains information about the Redpanda build. - properties: - buildSha: - description: The git commit SHA of the build. - title: build_sha - type: string - version: - description: A version string of Redpanda like "v25.2.1" - title: version - type: string - title: BuildInfo - type: object - redpanda.core.admin.v2.ConsumerOffsetSyncOptions: - additionalProperties: false - description: Options for syncing consumer offsets - properties: - enabled: - description: Whether it's enabled - title: enabled - type: boolean - groupFilters: - description: The filters - items: - $ref: "#/components/schemas/redpanda.core.admin.v2.NameFilter" - title: group_filters - type: array - interval: - $ref: "#/components/schemas/google.protobuf.Duration" - description: |- - Sync interval - If 0 provided, defaults to 30 seconds - title: interval - title: ConsumerOffsetSyncOptions - type: object - redpanda.core.admin.v2.CreateShadowLinkRequest: - additionalProperties: false - description: Create a new shadow link - properties: - shadowLink: - $ref: "#/components/schemas/redpanda.core.admin.v2.ShadowLink" - description: The shadow link to create - title: shadow_link - title: CreateShadowLinkRequest - type: object - redpanda.core.admin.v2.CreateShadowLinkResponse: - additionalProperties: false - description: Response to creating a shadow link - properties: - shadowLink: - $ref: "#/components/schemas/redpanda.core.admin.v2.ShadowLink" - description: The shadow link that was created - title: shadow_link - title: CreateShadowLinkResponse - type: object - redpanda.core.admin.v2.DeleteShadowLinkRequest: - additionalProperties: false - description: Request to delete a shadow link - properties: - name: - description: The name of the link to delete - title: name - type: string - required: - - name - title: DeleteShadowLinkRequest - type: object - redpanda.core.admin.v2.DeleteShadowLinkResponse: - additionalProperties: false - description: Response to deleting a shadow link - title: DeleteShadowLinkResponse - type: object - redpanda.core.admin.v2.FailOverRequest: - additionalProperties: false - description: Request to fail over a shadow link or a single shadow topic - properties: - name: - description: The name of the shadow link to fail over - title: name - type: string - shadowTopicName: - description: |- - (OPTIONAL) The name of the shadow topic to fail over, if not set will fail over - the entire shadow link - title: shadow_topic_name - type: string - required: - - name - title: FailOverRequest - type: object - redpanda.core.admin.v2.FailOverResponse: - additionalProperties: false - description: The response to the FailOverRequest - properties: - shadowLink: - $ref: "#/components/schemas/redpanda.core.admin.v2.ShadowLink" - description: The shadow link that was failed over - title: shadow_link - title: FailOverResponse - type: object - redpanda.core.admin.v2.FilterType: - description: What type of filter this is, include or exclude - enum: - - FILTER_TYPE_UNSPECIFIED - - FILTER_TYPE_INCLUDE - - FILTER_TYPE_EXCLUDE - title: FilterType - type: string - redpanda.core.admin.v2.GetBrokerRequest: - additionalProperties: false - description: GetBrokerRequest returns information about a single broker in the cluster - properties: - nodeId: - description: |- - The node ID for the broker. If set to -1 then the broker handling the RPC - request will response with it's own information. - format: int32 - title: node_id - type: integer - title: GetBrokerRequest - type: object - redpanda.core.admin.v2.GetBrokerResponse: - additionalProperties: false - description: GetBrokerResponse is the response from the GetBroker RPC. - properties: - broker: - $ref: "#/components/schemas/redpanda.core.admin.v2.Broker" - description: The specified broker and it's information. - title: broker - title: GetBrokerResponse - type: object - redpanda.core.admin.v2.GetShadowLinkRequest: - additionalProperties: false - description: Request to get the information about a shadow link - properties: - name: - description: The name of the shadow link to get - title: name - type: string - required: - - name - title: GetShadowLinkRequest - type: object - redpanda.core.admin.v2.GetShadowLinkResponse: - additionalProperties: false - description: Response to getting a shadow link - properties: - shadowLink: - $ref: "#/components/schemas/redpanda.core.admin.v2.ShadowLink" - description: The shadow link that was retrieved - title: shadow_link - title: GetShadowLinkResponse - type: object - redpanda.core.admin.v2.ListBrokersRequest: - additionalProperties: false - description: ListBrokersRequest returns information about all the brokers in the cluster - title: ListBrokersRequest - type: object - redpanda.core.admin.v2.ListBrokersResponse: - additionalProperties: false - description: ListBrokersResponse is the response from the ListBrokers RPC. - properties: - brokers: - description: The brokers in the cluster and their information. - items: - $ref: "#/components/schemas/redpanda.core.admin.v2.Broker" - title: brokers - type: array - title: ListBrokersResponse - type: object - redpanda.core.admin.v2.ListShadowLinksRequest: - additionalProperties: false - description: Request to list all shadow links - title: ListShadowLinksRequest - type: object - redpanda.core.admin.v2.ListShadowLinksResponse: - additionalProperties: false - description: All shadow links on the cluster - properties: - shadowLinks: - description: The shadow links - items: - $ref: "#/components/schemas/redpanda.core.admin.v2.ShadowLink" - title: shadow_links - type: array - title: ListShadowLinksResponse - type: object - redpanda.core.admin.v2.NameFilter: - additionalProperties: false - description: A filter based on the name of a resource - properties: - filterType: - $ref: "#/components/schemas/redpanda.core.admin.v2.FilterType" - description: Include or exclude - title: filter_type - name: - description: |- - The resource name, or "*" - Note if "*", must be the _only_ character - and `pattern_type` must be `PATTERN_TYPE_LITERAL` - title: name - type: string - patternType: - $ref: "#/components/schemas/redpanda.core.admin.v2.PatternType" - description: Literal or prefix - title: pattern_type - title: NameFilter - type: object - redpanda.core.admin.v2.PatternType: - description: The matching pattern type - enum: - - PATTERN_TYPE_UNSPECIFIED - - PATTERN_TYPE_LITERAL - - PATTERN_TYPE_PREFIX - title: PatternType - type: string - redpanda.core.admin.v2.RPCRoute: - additionalProperties: false - description: A route in the Admin API RPC server. - properties: - httpRoute: - description: |- - The HTTP route for this RPC method. - - For example `/redpanda.core.admin.v2.AdminService/GetVersion` - title: http_route - type: string - name: - description: |- - The name of the RPC method. - - For example `redpanda.core.admin.v2.AdminService.GetVersion` - title: name - type: string - title: RPCRoute - type: object - redpanda.core.admin.v2.ScramConfig: - additionalProperties: false - description: SCRAM settings - properties: - password: - description: Password - title: password - type: string - writeOnly: true - passwordSet: - description: Indicates that the password has been set - readOnly: true - title: password_set - type: boolean - passwordSetAt: - $ref: "#/components/schemas/google.protobuf.Timestamp" - description: |- - Timestamp of when the password was last set - only valid if password_set - is true - readOnly: true - title: password_set_at - scramMechanism: - $ref: "#/components/schemas/redpanda.core.admin.v2.ScramMechanism" - description: The SCRAM mechanism to use - title: scram_mechanism - username: - description: SCRAM username - title: username - type: string - title: ScramConfig - type: object - redpanda.core.admin.v2.ScramMechanism: - description: Valid SCRAM mechanisms - enum: - - SCRAM_MECHANISM_UNSPECIFIED - - SCRAM_MECHANISM_SCRAM_SHA_256 - - SCRAM_MECHANISM_SCRAM_SHA_512 - title: ScramMechanism - type: string - redpanda.core.admin.v2.SecuritySettingsSyncOptions: - additionalProperties: false - description: Options for syncing security settings - properties: - aclFilters: - description: ACL filters - items: - $ref: "#/components/schemas/redpanda.core.admin.v2.ACLFilter" - title: acl_filters - type: array - enabled: - description: Whether or not it's enabled - title: enabled - type: boolean - interval: - $ref: "#/components/schemas/google.protobuf.Duration" - description: |- - Sync interval - If 0 provided, defaults to 30 seconds - title: interval - roleFilters: - description: Role filters - items: - $ref: "#/components/schemas/redpanda.core.admin.v2.NameFilter" - title: role_filters - type: array - scramCredFilters: - description: SCRAM credential filters - items: - $ref: "#/components/schemas/redpanda.core.admin.v2.NameFilter" - title: scram_cred_filters - type: array - title: SecuritySettingsSyncOptions - type: object - redpanda.core.admin.v2.ShadowLink: - additionalProperties: false - description: A ShadowLink resource - properties: - configurations: - $ref: "#/components/schemas/redpanda.core.admin.v2.ShadowLinkConfigurations" - description: Shadow link configuration - title: configurations - name: - description: The name of the shadow link - title: name - type: string - status: - $ref: "#/components/schemas/redpanda.core.admin.v2.ShadowLinkStatus" - description: Status of the shadow link - readOnly: true - title: status - uid: - description: The UUID of the shadow link - readOnly: true - title: uid - type: string - required: - - name - title: ShadowLink - type: object - redpanda.core.admin.v2.ShadowLinkClientOptions: - additionalProperties: false - description: Options for the client link - properties: - authenticationConfiguration: - $ref: "#/components/schemas/redpanda.core.admin.v2.AuthenticationConfiguration" - description: Authentication settings - nullable: true - title: authentication_configuration - bootstrapServers: - description: The bootstrap servers to use - items: - type: string - title: bootstrap_servers - type: array - clientId: - description: |- - The Client ID for the Kafka RPC requests setn by this cluster to the - source cluster - readOnly: true - title: client_id - type: string - connectionTimeoutMs: - description: |- - Connection timeout - If 0 is provided, defaults to 1 second - format: int32 - title: connection_timeout_ms - type: integer - fetchMaxBytes: - description: |- - Fetch max bytes - If 0 is provided, defaults to 1MiB - format: int32 - title: fetch_max_bytes - type: integer - fetchMinBytes: - description: |- - Fetch min bytes - If 0 is provided, defaults to 1 byte - format: int32 - title: fetch_min_bytes - type: integer - fetchWaitMaxMs: - description: |- - Fetch request timeout - If 0 is provided, defaults to 100ms - format: int32 - title: fetch_wait_max_ms - type: integer - metadataMaxAgeMs: - description: |- - Max metadata age - If 0 is provided, defaults to 10 seconds - format: int32 - title: metadata_max_age_ms - type: integer - retryBackoffMs: - description: |- - Retry base backoff - If 0 is provided, defaults to 100ms - format: int32 - title: retry_backoff_ms - type: integer - sourceClusterId: - description: |- - If provided, this is the expected ID of the source cluster. If it does - not match then the connection will be rejected. If provided, this value - must match the `ClusterId` field returned in the Kafka Metadata response - message - title: source_cluster_id - type: string - tlsSettings: - $ref: "#/components/schemas/redpanda.core.admin.v2.TLSSettings" - description: TLS settings - nullable: true - title: tls_settings - required: - - bootstrapServers - title: ShadowLinkClientOptions - type: object - redpanda.core.admin.v2.ShadowLinkConfigurations: - additionalProperties: false - description: ShadowLink options - properties: - clientOptions: - $ref: "#/components/schemas/redpanda.core.admin.v2.ShadowLinkClientOptions" - description: Configuration for the internal kafka client - title: client_options - consumerOffsetSyncOptions: - $ref: "#/components/schemas/redpanda.core.admin.v2.ConsumerOffsetSyncOptions" - description: Consumer offset sync options - title: consumer_offset_sync_options - securitySyncOptions: - $ref: "#/components/schemas/redpanda.core.admin.v2.SecuritySettingsSyncOptions" - description: Security settings sync options - title: security_sync_options - topicMetadataSyncOptions: - $ref: "#/components/schemas/redpanda.core.admin.v2.TopicMetadataSyncOptions" - description: Topic metadata sync options - title: topic_metadata_sync_options - title: ShadowLinkConfigurations - type: object - redpanda.core.admin.v2.ShadowLinkState: - description: State of the shadow link - enum: - - SHADOW_LINK_STATE_UNSPECIFIED - - SHADOW_LINK_STATE_ACTIVE - - SHADOW_LINK_STATE_PAUSED - - SHADOW_LINK_STATE_FAILING_OVER - - SHADOW_LINK_STATE_FAILED_OVER - title: ShadowLinkState - type: string - redpanda.core.admin.v2.ShadowLinkStatus: - additionalProperties: false - description: Status of the shadow link - properties: - shadowTopicStatuses: - description: Status of shadow topics - items: - $ref: "#/components/schemas/redpanda.core.admin.v2.ShadowTopicStatus" - title: shadow_topic_statuses - type: array - state: - $ref: "#/components/schemas/redpanda.core.admin.v2.ShadowLinkState" - title: state - taskStatuses: - description: Statuses of the running tasks - items: - $ref: "#/components/schemas/redpanda.core.admin.v2.ShadowLinkTaskStatus" - title: task_statuses - type: array - title: ShadowLinkStatus - type: object - redpanda.core.admin.v2.ShadowLinkTaskStatus: - additionalProperties: false - description: Status of a task - properties: - brokerId: - description: The broker the task is running on - format: int32 - title: broker_id - type: integer - name: - description: Name of the task - title: name - type: string - reason: - description: Reason for task being in state - title: reason - type: string - state: - $ref: "#/components/schemas/redpanda.core.admin.v2.TaskState" - description: State of the task - title: state - title: ShadowLinkTaskStatus - type: object - redpanda.core.admin.v2.ShadowTopicState: - description: State of a shadow topic - enum: - - SHADOW_TOPIC_STATE_UNSPECIFIED - - SHADOW_TOPIC_STATE_ACTIVE - - SHADOW_TOPIC_STATE_PROMOTED - - SHADOW_TOPIC_STATE_FAULTED - - SHADOW_TOPIC_STATE_PAUSED - title: ShadowTopicState - type: string - redpanda.core.admin.v2.ShadowTopicStatus: - additionalProperties: false - description: Status of a ShadowTopic - properties: - name: - description: Name of the shadow topic - title: name - type: string - partitionInformation: - description: List of partition information for the shadow topic - items: - $ref: "#/components/schemas/redpanda.core.admin.v2.TopicPartitionInformation" - title: partition_information - type: array - state: - $ref: "#/components/schemas/redpanda.core.admin.v2.ShadowTopicState" - description: State of the shadow topic - title: state - topicId: - description: Topic ID of the shadow topic - title: topic_id - type: string - title: ShadowTopicStatus - type: object - redpanda.core.admin.v2.TLSFileSettings: - additionalProperties: false - description: TLS file settings - properties: - caPath: - description: Path to the CA - title: ca_path - type: string - certPath: - description: Path to the cert - title: cert_path - type: string - keyPath: - description: |- - Key and Cert are optional but if one is provided, then both must be - Path to the key - title: key_path - type: string - title: TLSFileSettings - type: object - redpanda.core.admin.v2.TLSPEMSettings: - additionalProperties: false - description: Used when providing the TLS information in PEM format - properties: - ca: - description: The CA - title: ca - type: string - cert: - description: The cert - title: cert - type: string - key: - description: |- - Key and Cert are optional but if one is provided, then both must be - The key - title: key - type: string - writeOnly: true - keyFingerprint: - description: The SHA-256 of the key, in base64 format - readOnly: true - title: key_fingerprint - type: string - title: TLSPEMSettings - type: object - redpanda.core.admin.v2.TLSSettings: - additionalProperties: false - description: TLS settings - oneOf: - - properties: - tlsFileSettings: - $ref: "#/components/schemas/redpanda.core.admin.v2.TLSFileSettings" - description: Certificates and keys are provided as files - title: tls_file_settings - required: - - tlsFileSettings - title: tls_file_settings - - properties: - tlsPemSettings: - $ref: "#/components/schemas/redpanda.core.admin.v2.TLSPEMSettings" - description: Certificates and keys are provided in PEM format - title: tls_pem_settings - required: - - tlsPemSettings - title: tls_pem_settings - title: TLSSettings - type: object - redpanda.core.admin.v2.TaskState: - description: Task states - enum: - - TASK_STATE_UNSPECIFIED - - TASK_STATE_ACTIVE - - TASK_STATE_PAUSED - - TASK_STATE_LINK_UNAVAILABLE - - TASK_STATE_NOT_RUNNING - - TASK_STATE_FAULTED - title: TaskState - type: string - redpanda.core.admin.v2.TopicMetadataSyncOptions: - additionalProperties: false - description: Options for syncing topic metadata - properties: - autoCreateShadowTopicFilters: - description: |- - List of filters that indicate which topics should be automatically - created as shadow topics on the shadow cluster. This only controls - automatic creation of shadow topics and does not effect the state of the - mirror topic once it is created. - items: - $ref: "#/components/schemas/redpanda.core.admin.v2.NameFilter" - title: auto_create_shadow_topic_filters - type: array - interval: - $ref: "#/components/schemas/google.protobuf.Duration" - description: |- - How often to sync metadata - If 0 provided, defaults to 30 seconds - title: interval - shadowedTopicProperties: - description: |- - Additional topic properties to shadow - Partition count, `max.message.bytes`, `cleanup.policy` and - `timestamp.type` will always be replicated - items: - type: string - title: shadowed_topic_properties - type: array - title: TopicMetadataSyncOptions - type: object - redpanda.core.admin.v2.TopicPartitionInformation: - additionalProperties: false - description: Topic partition information - properties: - highWatermark: - description: Shadowed partition's HWM - format: int64 - title: high_watermark - type: - - integer - - string - partitionId: - description: Partition ID - format: int64 - title: partition_id - type: - - integer - - string - sourceHighWatermark: - description: Source partition's HWM - format: int64 - title: source_high_watermark - type: - - integer - - string - sourceLastStableOffset: - description: Source partition's LSO - format: int64 - title: source_last_stable_offset - type: - - integer - - string - title: TopicPartitionInformation - type: object - redpanda.core.admin.v2.UpdateShadowLinkRequest: - additionalProperties: false - description: Updates a shadow link - properties: - shadowLink: - $ref: "#/components/schemas/redpanda.core.admin.v2.ShadowLink" - description: The shadow link to update - title: shadow_link - updateMask: - $ref: "#/components/schemas/google.protobuf.FieldMask" - description: |- - The list of fields to update - See [AIP-134](https://google.aip.dev/134) for how to use `field_mask` - title: update_mask - title: UpdateShadowLinkRequest - type: object - redpanda.core.admin.v2.UpdateShadowLinkResponse: - additionalProperties: false - description: Response to the update shadow link request - properties: - shadowLink: - $ref: "#/components/schemas/redpanda.core.admin.v2.ShadowLink" - description: The shadow link that was updated - title: shadow_link - title: UpdateShadowLinkResponse - type: object - redpanda.core.common.ACLOperation: - description: / The ACL operation to match - enum: - - ACL_OPERATION_UNSPECIFIED - - ACL_OPERATION_ANY - - ACL_OPERATION_READ - - ACL_OPERATION_WRITE - - ACL_OPERATION_CREATE - - ACL_OPERATION_REMOVE - - ACL_OPERATION_ALTER - - ACL_OPERATION_DESCRIBE - - ACL_OPERATION_CLUSTER_ACTION - - ACL_OPERATION_DESCRIBE_CONFIGS - - ACL_OPERATION_ALTER_CONFIGS - - ACL_OPERATION_IDEMPOTENT_WRITE - title: ACLOperation - type: string - redpanda.core.common.ACLPattern: - description: / The ACL pattern type - enum: - - ACL_PATTERN_UNSPECIFIED - - ACL_PATTERN_ANY - - ACL_PATTERN_LITERAL - - ACL_PATTERN_PREFIXED - - ACL_PATTERN_MATCH - title: ACLPattern - type: string - redpanda.core.common.ACLPermissionType: - description: / ACL permission types - enum: - - ACL_PERMISSION_TYPE_UNSPECIFIED - - ACL_PERMISSION_TYPE_ANY - - ACL_PERMISSION_TYPE_ALLOW - - ACL_PERMISSION_TYPE_DENY - title: ACLPermissionType - type: string - redpanda.core.common.ACLResource: - description: / The ACL resource types - enum: - - ACL_RESOURCE_UNSPECIFIED - - ACL_RESOURCE_ANY - - ACL_RESOURCE_CLUSTER - - ACL_RESOURCE_GROUP - - ACL_RESOURCE_TOPIC - - ACL_RESOURCE_TXN_ID - - ACL_RESOURCE_SR_SUBJECT - - ACL_RESOURCE_SR_REGISTRY - - ACL_RESOURCE_SR_ANY - title: ACLResource - type: string -info: - description: Redpanda Admin API specification - title: Redpanda Admin API - version: v2.0.0 - x-admin-api-major: v2.0.0 - x-generated-at: 2025-10-02T09:31:56.030Z - x-generator: redpanda-docs-openapi-bundler - x-redpanda-core-version: dev -openapi: 3.1.0 -paths: - /redpanda.core.admin.v2.BrokerService/GetBroker: - post: - description: GetBroker returns information about a single broker in the cluster. - operationId: redpanda.core.admin.v2.BrokerService.GetBroker - parameters: - - in: header - name: Connect-Protocol-Version - required: true - schema: - $ref: "#/components/schemas/connect-protocol-version" - - in: header - name: Connect-Timeout-Ms - schema: - $ref: "#/components/schemas/connect-timeout-header" - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/redpanda.core.admin.v2.GetBrokerRequest" - required: true - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/redpanda.core.admin.v2.GetBrokerResponse" - description: Success - default: - content: - application/json: - schema: - $ref: "#/components/schemas/connect.error" - description: Error - summary: GetBroker returns information about a single broker in the cluster. - tags: - - redpanda.core.admin.v2.BrokerService - /redpanda.core.admin.v2.BrokerService/ListBrokers: - post: - description: ListBrokers returns information about all brokers in the cluster. - operationId: redpanda.core.admin.v2.BrokerService.ListBrokers - parameters: - - in: header - name: Connect-Protocol-Version - required: true - schema: - $ref: "#/components/schemas/connect-protocol-version" - - in: header - name: Connect-Timeout-Ms - schema: - $ref: "#/components/schemas/connect-timeout-header" - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/redpanda.core.admin.v2.ListBrokersRequest" - required: true - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/redpanda.core.admin.v2.ListBrokersResponse" - description: Success - default: - content: - application/json: - schema: - $ref: "#/components/schemas/connect.error" - description: Error - summary: ListBrokers returns information about all brokers in the cluster. - tags: - - redpanda.core.admin.v2.BrokerService - /redpanda.core.admin.v2.ShadowLinkService/CreateShadowLink: - post: - operationId: redpanda.core.admin.v2.ShadowLinkService.CreateShadowLink - parameters: - - in: header - name: Connect-Protocol-Version - required: true - schema: - $ref: "#/components/schemas/connect-protocol-version" - - in: header - name: Connect-Timeout-Ms - schema: - $ref: "#/components/schemas/connect-timeout-header" - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/redpanda.core.admin.v2.CreateShadowLinkRequest" - required: true - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/redpanda.core.admin.v2.CreateShadowLinkResponse" - description: Success - default: - content: - application/json: - schema: - $ref: "#/components/schemas/connect.error" - description: Error - summary: CreateShadowLink - tags: - - redpanda.core.admin.v2.ShadowLinkService - /redpanda.core.admin.v2.ShadowLinkService/DeleteShadowLink: - post: - operationId: redpanda.core.admin.v2.ShadowLinkService.DeleteShadowLink - parameters: - - in: header - name: Connect-Protocol-Version - required: true - schema: - $ref: "#/components/schemas/connect-protocol-version" - - in: header - name: Connect-Timeout-Ms - schema: - $ref: "#/components/schemas/connect-timeout-header" - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/redpanda.core.admin.v2.DeleteShadowLinkRequest" - required: true - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/redpanda.core.admin.v2.DeleteShadowLinkResponse" - description: Success - default: - content: - application/json: - schema: - $ref: "#/components/schemas/connect.error" - description: Error - summary: DeleteShadowLink - tags: - - redpanda.core.admin.v2.ShadowLinkService - /redpanda.core.admin.v2.ShadowLinkService/FailOver: - post: - description: Fails over a shadow link or a single shadow topic - operationId: redpanda.core.admin.v2.ShadowLinkService.FailOver - parameters: - - in: header - name: Connect-Protocol-Version - required: true - schema: - $ref: "#/components/schemas/connect-protocol-version" - - in: header - name: Connect-Timeout-Ms - schema: - $ref: "#/components/schemas/connect-timeout-header" - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/redpanda.core.admin.v2.FailOverRequest" - required: true - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/redpanda.core.admin.v2.FailOverResponse" - description: Success - default: - content: - application/json: - schema: - $ref: "#/components/schemas/connect.error" - description: Error - summary: Fails over a shadow link or a single shadow topic - tags: - - redpanda.core.admin.v2.ShadowLinkService - /redpanda.core.admin.v2.ShadowLinkService/GetShadowLink: - post: - operationId: redpanda.core.admin.v2.ShadowLinkService.GetShadowLink - parameters: - - in: header - name: Connect-Protocol-Version - required: true - schema: - $ref: "#/components/schemas/connect-protocol-version" - - in: header - name: Connect-Timeout-Ms - schema: - $ref: "#/components/schemas/connect-timeout-header" - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/redpanda.core.admin.v2.GetShadowLinkRequest" - required: true - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/redpanda.core.admin.v2.GetShadowLinkResponse" - description: Success - default: - content: - application/json: - schema: - $ref: "#/components/schemas/connect.error" - description: Error - summary: GetShadowLink - tags: - - redpanda.core.admin.v2.ShadowLinkService - /redpanda.core.admin.v2.ShadowLinkService/ListShadowLinks: - post: - operationId: redpanda.core.admin.v2.ShadowLinkService.ListShadowLinks - parameters: - - in: header - name: Connect-Protocol-Version - required: true - schema: - $ref: "#/components/schemas/connect-protocol-version" - - in: header - name: Connect-Timeout-Ms - schema: - $ref: "#/components/schemas/connect-timeout-header" - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/redpanda.core.admin.v2.ListShadowLinksRequest" - required: true - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/redpanda.core.admin.v2.ListShadowLinksResponse" - description: Success - default: - content: - application/json: - schema: - $ref: "#/components/schemas/connect.error" - description: Error - summary: ListShadowLinks - tags: - - redpanda.core.admin.v2.ShadowLinkService - /redpanda.core.admin.v2.ShadowLinkService/UpdateShadowLink: - post: - operationId: redpanda.core.admin.v2.ShadowLinkService.UpdateShadowLink - parameters: - - in: header - name: Connect-Protocol-Version - required: true - schema: - $ref: "#/components/schemas/connect-protocol-version" - - in: header - name: Connect-Timeout-Ms - schema: - $ref: "#/components/schemas/connect-timeout-header" - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/redpanda.core.admin.v2.UpdateShadowLinkRequest" - required: true - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/redpanda.core.admin.v2.UpdateShadowLinkResponse" - description: Success - default: - content: - application/json: - schema: - $ref: "#/components/schemas/connect.error" - description: Error - summary: UpdateShadowLink - tags: - - redpanda.core.admin.v2.ShadowLinkService From 03b73364bde374cf3da10fea99d7a3e7e7c3ac6c Mon Sep 17 00:00:00 2001 From: JakeSCahill Date: Thu, 2 Oct 2025 10:48:27 +0100 Subject: [PATCH 5/9] Remove --- TESTING.md | 93 ------------------------------------------------------ 1 file changed, 93 deletions(-) delete mode 100644 TESTING.md diff --git a/TESTING.md b/TESTING.md deleted file mode 100644 index b4be073..0000000 --- a/TESTING.md +++ /dev/null @@ -1,93 +0,0 @@ -# Testing Instructions - -## 1. Install dependencies: -```bash -npm install -``` - -## 2. Link the package for local testing: -```bash -npm link -``` - -## 3. Install CLI dependencies: -```bash -# Install all required dependencies for CLI commands -./cli-utils/install-test-dependencies.sh - -# Or manually install core dependencies: -npm install -g @redocly/cli # or swagger-cli -brew install buf git # or appropriate package manager -``` - -## 4. Verify CLI command renamed correctly: -```bash -# Check that the old command is gone and new one exists -doc-tools generate --help - -# Should show 'bundle-openapi' command instead of 'bundle-admin-api' -``` - -## 5. Test OpenAPI bundling functionality: -```bash -# Test admin API bundling -doc-tools generate bundle-openapi --surface admin --tag v25.1.1 - -# Test connect API bundling -doc-tools generate bundle-openapi --surface connect --tag v25.1.1 - -# Test both APIs -doc-tools generate bundle-openapi --surface both --tag v25.1.1 - -# Test with version override -doc-tools generate bundle-openapi --surface admin --tag v25.1.1 --use-admin-major-version -``` - -## 6. Test npm script shortcuts: -```bash -# These should use the new command name -npm run bundle:admin -npm run bundle:connect -npm run bundle:both -``` - -## 7. Test bundler detection: -```bash -# Should automatically detect and use available bundlers -# (swagger-cli, @redocly/cli, or fallback to npx variants) -doc-tools generate bundle-openapi --surface admin --tag v25.1.1 --quiet -``` - -## 8. Run test suite: -```bash -npm test -``` - -## 9. Verify error handling: -```bash -# Test with invalid tag -doc-tools generate bundle-openapi --surface admin --tag invalid-tag - -# Test with missing dependencies (if you want to test error paths) -# Temporarily rename bundler and test error message -``` - -## 10. Test standalone tool: -```bash -# Test the standalone bundle-openapi.js directly -node tools/bundle-openapi.js --tag v25.1.1 --surface admin -``` - -## Expected Results: -- ✅ All tests pass (86/86) -- ✅ CLI command renamed from `bundle-admin-api` to `bundle-openapi` -- ✅ Command description updated to mention both admin and connect APIs -- ✅ Bundler detection uses shared `detectBundler()` function (no duplication) -- ✅ Generated OpenAPI files contain proper metadata and structure -- ✅ npm scripts work with new command name -- ✅ No breaking changes to existing functionality - -## Files to Check: -- `admin/redpanda-admin-api.yaml` - Generated admin API spec -- `connect/redpanda-connect-api.yaml` - Generated connect API spec (if tested) -- Verify proper version metadata in generated files \ No newline at end of file From 84bf7fec91923fb949f3acee98e66cb858881c27 Mon Sep 17 00:00:00 2001 From: JakeSCahill Date: Thu, 2 Oct 2025 10:53:07 +0100 Subject: [PATCH 6/9] Apply suggestions --- cli-utils/install-test-dependencies.sh | 105 +++++++++++++++++++------ tools/bundle-openapi.js | 30 ++++++- 2 files changed, 108 insertions(+), 27 deletions(-) diff --git a/cli-utils/install-test-dependencies.sh b/cli-utils/install-test-dependencies.sh index a50e3f4..f0d5c25 100644 --- a/cli-utils/install-test-dependencies.sh +++ b/cli-utils/install-test-dependencies.sh @@ -410,7 +410,7 @@ ensure_dependencies_installed() { log_info "Installing/checking dependencies for doc-tools CLI commands..." ensure_dependencies_installed -# install_rpk installs Redpanda's rpk CLI into ~/.local/bin by downloading the latest Linux amd64 release, adding it to PATH for the current and future sessions, and verifying the installation; returns 0 on success and non-zero on failure. +# install_rpk installs Redpanda's rpk CLI into ~/.local/bin by downloading the appropriate release for the current OS and architecture, adding it to PATH for the current and future sessions, and verifying the installation; returns 0 on success and non-zero on failure. install_rpk() { if command_exists rpk; then log_info "rpk is already installed. Version information:" @@ -420,33 +420,92 @@ install_rpk() { log_info "Installing rpk..." - # Try to install rpk using the installation script - if curl -LO https://github.com/redpanda-data/redpanda/releases/latest/download/rpk-linux-amd64.zip; then - unzip rpk-linux-amd64.zip - mkdir -p ~/.local/bin - mv rpk ~/.local/bin/ - rm rpk-linux-amd64.zip - - # Add to PATH for current session - export PATH=$HOME/.local/bin:$PATH - - # Add the target directory to PATH for future sessions - if ! grep -q 'export PATH=$HOME/.local/bin:$PATH' ~/.bashrc 2>/dev/null; then - echo 'export PATH=$HOME/.local/bin:$PATH' >> ~/.bashrc - fi - - # Verify installation - if command_exists rpk; then - log_info "rpk has been installed successfully. Version information:" - rpk --version - return 0 + # Detect OS and architecture + local os_name=$(uname -s) + local arch_name=$(uname -m) + + # Map OS name to rpk release format + local rpk_os="" + case "$os_name" in + "Darwin") + rpk_os="darwin" + ;; + "Linux") + rpk_os="linux" + ;; + *) + log_warn "Unsupported operating system: $os_name" + log_warn "Please install rpk manually:" + log_warn "https://docs.redpanda.com/current/get-started/rpk-install/" + return 1 + ;; + esac + + # Map architecture to rpk release format + local rpk_arch="" + case "$arch_name" in + "x86_64" | "amd64") + rpk_arch="amd64" + ;; + "arm64" | "aarch64") + rpk_arch="arm64" + ;; + *) + log_warn "Unsupported architecture: $arch_name" + log_warn "Please install rpk manually:" + log_warn "https://docs.redpanda.com/current/get-started/rpk-install/" + return 1 + ;; + esac + + # Construct download URL and filename + local rpk_filename="rpk-${rpk_os}-${rpk_arch}.zip" + local rpk_url="https://github.com/redpanda-data/redpanda/releases/latest/download/${rpk_filename}" + + log_info "Detected ${os_name} ${arch_name}, downloading ${rpk_filename}..." + + # Try to download and install rpk + if curl -LO "$rpk_url"; then + if unzip "$rpk_filename" 2>/dev/null; then + mkdir -p ~/.local/bin + if mv rpk ~/.local/bin/ 2>/dev/null; then + rm "$rpk_filename" + + # Add to PATH for current session + export PATH=$HOME/.local/bin:$PATH + + # Add the target directory to PATH for future sessions + if ! grep -q 'export PATH=$HOME/.local/bin:$PATH' ~/.bashrc 2>/dev/null; then + echo 'export PATH=$HOME/.local/bin:$PATH' >> ~/.bashrc + fi + + # Verify installation + if command_exists rpk; then + log_info "rpk has been installed successfully. Version information:" + rpk --version + return 0 + else + log_warn "rpk installation may have failed. Please install manually:" + log_warn "https://docs.redpanda.com/current/get-started/rpk-install/" + return 1 + fi + else + log_warn "Failed to move rpk binary to ~/.local/bin/" + rm -f "$rpk_filename" rpk 2>/dev/null + log_warn "Please install rpk manually:" + log_warn "https://docs.redpanda.com/current/get-started/rpk-install/" + return 1 + fi else - log_warn "rpk installation may have failed. Please install manually:" + log_warn "Failed to unzip $rpk_filename (may not exist for ${rpk_os}-${rpk_arch})" + rm -f "$rpk_filename" 2>/dev/null + log_warn "Please install rpk manually:" log_warn "https://docs.redpanda.com/current/get-started/rpk-install/" return 1 fi else - log_warn "Failed to download rpk. Please install manually:" + log_warn "Failed to download $rpk_url" + log_warn "Please install rpk manually:" log_warn "https://docs.redpanda.com/current/get-started/rpk-install/" return 1 fi diff --git a/tools/bundle-openapi.js b/tools/bundle-openapi.js index 2e0764c..4cb313f 100644 --- a/tools/bundle-openapi.js +++ b/tools/bundle-openapi.js @@ -606,10 +606,27 @@ async function bundleOpenAPI(options) { } }; - // Handle graceful shutdown - process.on('SIGINT', cleanup); - process.on('SIGTERM', cleanup); - process.on('uncaughtException', cleanup); + // Create dedicated handlers that clean up and then terminate + const cleanupAndExit = (signal) => { + return () => { + cleanup(); + process.exit(signal === 'SIGTERM' ? 0 : 1); + }; + }; + + const cleanupAndCrash = (error) => { + cleanup(); + console.error('Fatal error:', error); + process.exit(1); + }; + + // Handle graceful shutdown and crashes + const sigintHandler = cleanupAndExit('SIGINT'); + const sigtermHandler = cleanupAndExit('SIGTERM'); + + process.on('SIGINT', sigintHandler); + process.on('SIGTERM', sigtermHandler); + process.on('uncaughtException', cleanupAndCrash); try { // Clone repository (only once for all surfaces) @@ -742,6 +759,11 @@ async function bundleOpenAPI(options) { } throw error; } finally { + // Remove event handlers to restore default behavior + process.removeListener('SIGINT', sigintHandler); + process.removeListener('SIGTERM', sigtermHandler); + process.removeListener('uncaughtException', cleanupAndCrash); + cleanup(); } } From d19ca78e86dfa99febab70fb43f4b2c75d0692bf Mon Sep 17 00:00:00 2001 From: Jake Cahill <45230295+JakeSCahill@users.noreply.github.com> Date: Thu, 2 Oct 2025 10:55:13 +0100 Subject: [PATCH 7/9] Update tools/bundle-openapi.js --- tools/bundle-openapi.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/bundle-openapi.js b/tools/bundle-openapi.js index 4cb313f..734053b 100644 --- a/tools/bundle-openapi.js +++ b/tools/bundle-openapi.js @@ -311,7 +311,7 @@ function runBundler(bundler, fragmentFiles, outputPath, tempDir, quiet = false) } }; - // Manually merge all fragment files with better error handling + // Manually merge all fragment files for (const filePath of fragmentFiles) { try { if (!fs.existsSync(filePath)) { From 40d90c4364a6717a99bd58f71cea1f12ab012df2 Mon Sep 17 00:00:00 2001 From: JakeSCahill Date: Fri, 3 Oct 2025 09:18:16 +0100 Subject: [PATCH 8/9] Add buf as a node module --- bin/doc-tools.js | 2 +- cli-utils/install-test-dependencies.sh | 0 package-lock.json | 137 +++++++++++++++++++++++++ package.json | 1 + 4 files changed, 139 insertions(+), 1 deletion(-) mode change 100644 => 100755 cli-utils/install-test-dependencies.sh diff --git a/bin/doc-tools.js b/bin/doc-tools.js index 8b975c3..69a2781 100755 --- a/bin/doc-tools.js +++ b/bin/doc-tools.js @@ -1554,7 +1554,7 @@ automation .action(async (options) => { // Verify dependencies requireCmd('git', 'Install Git: https://git-scm.com/downloads'); - requireCmd('buf', 'Install buf: https://buf.build/docs/installation'); + requireCmd('buf', 'buf should be automatically available after npm install'); // Check for OpenAPI bundler using the existing detectBundler function try { diff --git a/cli-utils/install-test-dependencies.sh b/cli-utils/install-test-dependencies.sh old mode 100644 new mode 100755 diff --git a/package-lock.json b/package-lock.json index b91d1be..ff3b438 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "ISC", "dependencies": { "@asciidoctor/tabs": "^1.0.0-beta.6", + "@bufbuild/buf": "^1.28.1", "@octokit/core": "^6.1.2", "@octokit/plugin-retry": "^7.1.1", "@octokit/rest": "^21.0.1", @@ -1118,6 +1119,142 @@ "dev": true, "license": "MIT" }, + "node_modules/@bufbuild/buf": { + "version": "1.57.2", + "resolved": "https://registry.npmjs.org/@bufbuild/buf/-/buf-1.57.2.tgz", + "integrity": "sha512-N51MOnjROdtBX3fPU2KGdmwfiT1WWXzs8Jw1tMMkyVAmCSahWbObGIPq7e1fw7lR5i0s3bTxQyk3KRmb2/uwNg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "bin": { + "buf": "bin/buf", + "protoc-gen-buf-breaking": "bin/protoc-gen-buf-breaking", + "protoc-gen-buf-lint": "bin/protoc-gen-buf-lint" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@bufbuild/buf-darwin-arm64": "1.57.2", + "@bufbuild/buf-darwin-x64": "1.57.2", + "@bufbuild/buf-linux-aarch64": "1.57.2", + "@bufbuild/buf-linux-armv7": "1.57.2", + "@bufbuild/buf-linux-x64": "1.57.2", + "@bufbuild/buf-win32-arm64": "1.57.2", + "@bufbuild/buf-win32-x64": "1.57.2" + } + }, + "node_modules/@bufbuild/buf-darwin-arm64": { + "version": "1.57.2", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-darwin-arm64/-/buf-darwin-arm64-1.57.2.tgz", + "integrity": "sha512-ybwjZ8hFpDiSfTY8ltuNulh6e4CYcojZX+joo/VocFb4InRFyF08S+JSO+1ld+nTmJ24ziGwXZTg8qXiiN6qog==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@bufbuild/buf-darwin-x64": { + "version": "1.57.2", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-darwin-x64/-/buf-darwin-x64-1.57.2.tgz", + "integrity": "sha512-uoNjkkyjmJlzQyfPI/mwlutDvu48gFg8pYzlK0RhNshvTWCDo2vXbw1gZSaQnv1xK585N9Axmyjp0K2sYJkaDQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@bufbuild/buf-linux-aarch64": { + "version": "1.57.2", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-linux-aarch64/-/buf-linux-aarch64-1.57.2.tgz", + "integrity": "sha512-JvB9M+GraP7tgYlyKUhF46+pw/hctFyAjcLvPCPxr8Lr9uo3I6uqU8KywE4AuzdFReaZ4wtdF6sUlsfDOo8Geg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@bufbuild/buf-linux-armv7": { + "version": "1.57.2", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-linux-armv7/-/buf-linux-armv7-1.57.2.tgz", + "integrity": "sha512-13ZBU/LTboW7B/qjMV6fY6z8q0uyfSpp+y2J+/PAzCrC64ewSTE30pMKFQujMQ05Po0kY5e9yWvTElJcLbzt5w==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@bufbuild/buf-linux-x64": { + "version": "1.57.2", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-linux-x64/-/buf-linux-x64-1.57.2.tgz", + "integrity": "sha512-R28Y6g0kGlM/agtVc8sAjzGhgtbA4JyJYg4w7ZPm/FaVEw2sAYZBsa5uxmMNNOmWopKfd0yr2Pgq8ndfG0QSug==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@bufbuild/buf-win32-arm64": { + "version": "1.57.2", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-win32-arm64/-/buf-win32-arm64-1.57.2.tgz", + "integrity": "sha512-6YlPr2Z0VLae/76raYDEcotN7YGNgn3qaNy7x5idAoNeAr0EK2WsKtlBypK0/fQO3GOrYo0HGM/0SFENXoQKbw==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@bufbuild/buf-win32-x64": { + "version": "1.57.2", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-win32-x64/-/buf-win32-x64-1.57.2.tgz", + "integrity": "sha512-7YKz2RVPQlL+rJYzNL7JK3LpvZUz7wY6hof3QOTNX3sHY6pRD+mtWt/PITTlC3PtyjSKy17wAhbkokC0JErsHw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@emotion/is-prop-valid": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz", diff --git a/package.json b/package.json index 8f91203..d996772 100644 --- a/package.json +++ b/package.json @@ -81,6 +81,7 @@ }, "dependencies": { "@asciidoctor/tabs": "^1.0.0-beta.6", + "@bufbuild/buf": "^1.28.1", "@octokit/core": "^6.1.2", "@octokit/plugin-retry": "^7.1.1", "@octokit/rest": "^21.0.1", From 375a2f26c443b37f0e39a1ab9c1a94666e1512a1 Mon Sep 17 00:00:00 2001 From: JakeSCahill Date: Fri, 3 Oct 2025 11:41:40 +0100 Subject: [PATCH 9/9] Fix field name --- tools/redpanda-connect/helpers/renderConnectFields.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/redpanda-connect/helpers/renderConnectFields.js b/tools/redpanda-connect/helpers/renderConnectFields.js index 433b5cb..3b137d3 100644 --- a/tools/redpanda-connect/helpers/renderConnectFields.js +++ b/tools/redpanda-connect/helpers/renderConnectFields.js @@ -64,7 +64,7 @@ module.exports = function renderConnectFields(children, prefix = '') { // --- Interpolation support notice --- const interpolationNotice = 'This field supports xref:configuration:interpolation.adoc#bloblang-queries[interpolation functions].'; - if (child.interpolation === true) { + if (child.interpolated === true) { // Only add if not already present (case-insensitive) const descLower = desc.toLowerCase(); if (!descLower.includes('interpolation functions')) {