@eventcatalog/core 4.10.13 → 4.10.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analytics/analytics.cjs +1 -1
- package/dist/analytics/analytics.js +2 -2
- package/dist/analytics/log-build.cjs +1 -1
- package/dist/analytics/log-build.js +3 -3
- package/dist/{chunk-SSSN5FXC.js → chunk-7LWOCX4R.js} +1 -1
- package/dist/{chunk-IIECZFXN.js → chunk-A4JRWRCV.js} +1 -1
- package/dist/{chunk-JHUICVBT.js → chunk-HBVTU6ZP.js} +1 -1
- package/dist/{chunk-PADMH2RJ.js → chunk-I6HUOX4R.js} +1 -1
- package/dist/{chunk-34RMFKFB.js → chunk-Q2ATWE7N.js} +1 -1
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/docs/api/02-config.md +60 -6
- package/dist/docs/development/ask-your-architecture/02-eventcatalog-assistant/02-configuration.md +18 -11
- package/dist/docs/development/authentication/providers/03-setting-up-github.md +29 -0
- package/dist/docs/development/bring-your-own-documentation/custom-pages/02-adding-custom-docs.md +2 -2
- package/dist/docs/development/bring-your-own-documentation/custom-pages/03-components.md +17 -0
- package/dist/docs/development/components/07-resource-references.md +7 -1
- package/dist/docs/development/components/components/05-architecture-graph.md +65 -0
- package/dist/docs/development/components/components/{05-attachments.md → 06-attachments.md} +1 -1
- package/dist/docs/development/components/components/{06-channel-information.md → 07-channel-information.md} +1 -1
- package/dist/docs/development/components/components/{07-custom-properties.md → 08-custom-properties.md} +1 -1
- package/dist/docs/development/components/components/{08-design.md → 09-design.md} +1 -1
- package/dist/docs/development/components/components/{09-entitymap.md → 10-entitymap.md} +1 -1
- package/dist/docs/development/components/components/{10-flow.md → 11-flow.md} +1 -1
- package/dist/docs/development/components/components/{11-link.md → 12-link.md} +1 -1
- package/dist/docs/development/components/components/{12-mermaid-file-loader.md → 13-mermaid-file-loader.md} +13 -1
- package/dist/docs/development/components/components/{13-message-table.md → 14-message-table.md} +1 -1
- package/dist/docs/development/components/components/{14-nodegraph.md → 15-nodegraph.md} +1 -1
- package/dist/docs/development/components/components/{15-openapi.md → 16-openapi.md} +1 -1
- package/dist/docs/development/components/components/{16-prompt.md → 17-prompt.md} +1 -1
- package/dist/docs/development/components/components/{17-remote-schema.md → 18-remote-schema.md} +1 -1
- package/dist/docs/development/components/components/{18-resource-group-table.md → 19-resource-group-table.md} +1 -1
- package/dist/docs/development/components/components/{19-resource-link.md → 20-resource-link.md} +1 -1
- package/dist/docs/development/components/components/{20-schema.md → 21-schema.md} +1 -1
- package/dist/docs/development/components/components/{21-schema-viewer.md → 22-schema-viewer.md} +1 -1
- package/dist/docs/development/components/components/{22-steps.md → 23-steps.md} +1 -1
- package/dist/docs/development/components/components/{23-tabs.md → 24-tabs.md} +1 -1
- package/dist/docs/development/components/components/{24-tiles.md → 25-tiles.md} +1 -1
- package/dist/docs/development/components/components/{25-visibility.md → 26-visibility.md} +1 -1
- package/dist/docs/development/components/diagram-syntax/06-mermaid.md +45 -6
- package/dist/docs/development/customization/04-documentation-sidebar.md +3 -3
- package/dist/docs/development/customization/05-resource-sidebar.md +235 -0
- package/dist/docs/development/customization/09-embed-eventcatalog.md +179 -0
- package/dist/docs/development/customization/_category_.json +1 -1
- package/dist/docs/development/deployment/build-and-deploy.md +4 -0
- package/dist/docs/development/deployment/hosting-options.md +1 -1
- package/dist/docs/development/deployment/link-validation.md +105 -0
- package/dist/docs/development/developer-tools/eventcatalog-linter/00-overview.md +76 -0
- package/dist/docs/development/developer-tools/eventcatalog-linter/01-setup.md +144 -0
- package/dist/docs/development/developer-tools/eventcatalog-linter/_category_.json +10 -0
- package/dist/docs/development/developer-tools/eventcatalog-linter/how-to/01-configure-rules.md +130 -0
- package/dist/docs/development/developer-tools/eventcatalog-linter/how-to/02-run-in-ci.md +93 -0
- package/dist/docs/development/developer-tools/eventcatalog-linter/how-to/03-use-custom-frontmatter.md +81 -0
- package/dist/docs/development/developer-tools/eventcatalog-linter/how-to/04-reference-external-catalogs.md +51 -0
- package/dist/docs/development/developer-tools/eventcatalog-linter/how-to/05-fix-common-problems.md +120 -0
- package/dist/docs/development/developer-tools/eventcatalog-linter/how-to/_category_.json +12 -0
- package/dist/docs/development/developer-tools/eventcatalog-linter/reference/01-cli.md +109 -0
- package/dist/docs/development/developer-tools/eventcatalog-linter/reference/02-configuration.md +120 -0
- package/dist/docs/development/developer-tools/eventcatalog-linter/reference/03-rules.md +287 -0
- package/dist/docs/development/developer-tools/eventcatalog-linter/reference/04-versions.md +77 -0
- package/dist/docs/development/developer-tools/eventcatalog-linter/reference/05-supported-resources.md +75 -0
- package/dist/docs/development/developer-tools/eventcatalog-linter/reference/_category_.json +12 -0
- package/dist/docs/development/guides/97-architecture-graph.md +78 -0
- package/dist/docs/development/guides/domains/02-create-domain.md +1 -1
- package/dist/docs/development/guides/domains/03-ownership-and-language/02-adding-ubiquitous-language.md +29 -0
- package/dist/docs/development/guides/domains/09-reference.md +10 -0
- package/dist/docs/development/guides/resources/messages/04-message-channels/02-adding-channels.md +2 -2
- package/dist/docs/development/guides/resources/messages/04-message-channels/04-adding-messages-to-services.md +8 -0
- package/dist/docs/development/guides/resources/schemas/03-add-specifications-to-services/01-add-openapi-specifications.md +2 -2
- package/dist/docs/development/guides/resources/schemas/03-add-specifications-to-services/02-add-asyncapi-specifications.md +3 -3
- package/dist/docs/development/guides/systems/05-system-context-maps.md +2 -0
- package/dist/docs/development/license-keys/license-validation.md +7 -3
- package/dist/docs/development/license-keys/overview.md +7 -3
- package/dist/docs/development/upgrading/upgrading.md +3 -3
- package/dist/docs/development/upgrading/v2.md +3 -3
- package/dist/docs/federation/00-overview.md +158 -0
- package/dist/docs/federation/01-first-federation.md +199 -0
- package/dist/docs/federation/_category_.json +12 -0
- package/dist/docs/federation/explanation/_category_.json +12 -0
- package/dist/docs/federation/explanation/how-it-works.md +129 -0
- package/dist/docs/federation/explanation/lockfile-and-cache.md +134 -0
- package/dist/docs/federation/explanation/mvp-status-and-feedback.md +86 -0
- package/dist/docs/federation/explanation/ownership-and-references.md +134 -0
- package/dist/docs/federation/how-to/_category_.json +12 -0
- package/dist/docs/federation/how-to/configure-github-sources.md +156 -0
- package/dist/docs/federation/how-to/configure-validation-rules.md +127 -0
- package/dist/docs/federation/how-to/run-in-ci.md +138 -0
- package/dist/docs/federation/how-to/share-assets-and-components.md +125 -0
- package/dist/docs/federation/how-to/use-local-sources.md +138 -0
- package/dist/docs/{plugins/eventcatalog-federation → federation/legacy-federation}/00-introduction.md +11 -2
- package/dist/docs/{plugins/eventcatalog-federation → federation/legacy-federation}/01-installation.md +11 -3
- package/dist/docs/{plugins/eventcatalog-federation → federation/legacy-federation}/02-plugin-configuration.md +11 -2
- package/dist/docs/{plugins/eventcatalog-federation → federation/legacy-federation}/03-examples.md +11 -1
- package/dist/docs/{plugins/eventcatalog-federation → federation/legacy-federation}/04-configuration.md +11 -1
- package/dist/docs/{plugins/eventcatalog-federation → federation/legacy-federation}/05-setup-team-catalog.md +11 -2
- package/dist/docs/federation/legacy-federation/_category_.json +12 -0
- package/dist/docs/federation/reference/_category_.json +12 -0
- package/dist/docs/federation/reference/cli.md +122 -0
- package/dist/docs/federation/reference/configuration.md +192 -0
- package/dist/docs/federation/reference/diagnostic-rules.md +173 -0
- package/dist/docs/federation/reference/generated-output.md +118 -0
- package/dist/docs/federation/reference/supported-content.md +86 -0
- package/dist/docs/federation/reference/troubleshooting.md +208 -0
- package/dist/docs/plugins/01-intro.md +3 -3
- package/dist/docs/plugins/03-all-plugins.md +2 -2
- package/dist/docs/plugins/asyncapi/00-intro.md +4 -4
- package/dist/docs/plugins/asyncapi/02-plugin-configuration.md +4 -4
- package/dist/docs/plugins/backstage/00-overview.md +53 -0
- package/dist/docs/plugins/backstage/01-get-started.md +95 -0
- package/dist/docs/plugins/backstage/02-embed-entity-tabs.md +109 -0
- package/dist/docs/plugins/backstage/03-add-overview-cards.md +79 -0
- package/dist/docs/plugins/backstage/04-control-theme-and-size.md +54 -0
- package/dist/docs/plugins/backstage/05-migrate-to-annotations.md +49 -0
- package/dist/docs/plugins/backstage/06-configuration.md +85 -0
- package/dist/docs/plugins/backstage/07-components.md +129 -0
- package/dist/docs/plugins/backstage/08-how-embeds-work.md +52 -0
- package/dist/docs/plugins/backstage/_category_.json +4 -3
- package/dist/docs/plugins/eventbridge/00-intro.md +5 -5
- package/dist/docs/plugins/eventbridge/03-api.md +3 -3
- package/dist/docs/plugins/graphql/00-intro.md +2 -2
- package/dist/docs/plugins/graphql/02-plugin-configuration.md +2 -2
- package/dist/docs/plugins/hookdeck/01-intro.md +1 -1
- package/dist/docs/plugins/openapi/00-intro.md +1 -1
- package/dist/eventcatalog.cjs +1 -1
- package/dist/eventcatalog.js +5 -5
- package/dist/generate.cjs +1 -1
- package/dist/generate.js +3 -3
- package/dist/utils/cli-logger.cjs +1 -1
- package/dist/utils/cli-logger.js +2 -2
- package/eventcatalog/src/components/SchemaExplorer/AvroSchemaViewer.tsx +21 -19
- package/eventcatalog/src/components/SchemaExplorer/JSONSchemaViewer.test.tsx +114 -0
- package/eventcatalog/src/components/SchemaExplorer/JSONSchemaViewer.tsx +107 -86
- package/eventcatalog/src/components/SchemaExplorer/ProtobufSchemaViewer.test.tsx +91 -0
- package/eventcatalog/src/components/SchemaExplorer/ProtobufSchemaViewer.tsx +115 -33
- package/eventcatalog/src/components/SchemaExplorer/SchemaViewerHover.test.tsx +33 -0
- package/eventcatalog/src/components/SchemaExplorer/protobuf-validation.test.ts +84 -0
- package/eventcatalog/src/components/SchemaExplorer/protobuf-validation.ts +149 -0
- package/eventcatalog/src/utils/collections/glob-loader.ts +2 -2
- package/eventcatalog/src/utils/protobuf-schema.ts +211 -16
- package/package.json +8 -8
- package/dist/docs/cli/channels.md +0 -180
- package/dist/docs/cli/commands.md +0 -183
- package/dist/docs/cli/custom-docs.md +0 -78
- package/dist/docs/cli/data-products.md +0 -177
- package/dist/docs/cli/data-stores.md +0 -166
- package/dist/docs/cli/diagrams.md +0 -147
- package/dist/docs/cli/domains.md +0 -280
- package/dist/docs/cli/entities.md +0 -138
- package/dist/docs/cli/events.md +0 -186
- package/dist/docs/cli/export.md +0 -27
- package/dist/docs/cli/governance.md +0 -24
- package/dist/docs/cli/messages.md +0 -69
- package/dist/docs/cli/queries.md +0 -183
- package/dist/docs/cli/services.md +0 -266
- package/dist/docs/cli/snapshots.md +0 -44
- package/dist/docs/cli/teams.md +0 -75
- package/dist/docs/cli/users.md +0 -75
- package/dist/docs/cli/utilities.md +0 -43
- package/dist/docs/development/developer-tools/eventcatalog-linter.md +0 -732
- package/dist/docs/plugins/backstage/00-intro.md +0 -67
- package/dist/docs/plugins/backstage/01-installation.md +0 -242
- package/dist/docs/plugins/backstage/02-api.md +0 -51
- package/dist/docs/plugins/backstage/03-examples.md +0 -12
- package/dist/docs/plugins/eventcatalog-federation/_category_.json +0 -11
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import type { ProtobufField } from '@utils/protobuf-schema';
|
|
3
|
+
import {
|
|
4
|
+
formatProtobufOptionValue,
|
|
5
|
+
formatProtovalidateRule,
|
|
6
|
+
getCustomFieldAnnotations,
|
|
7
|
+
getProtovalidateRules,
|
|
8
|
+
isProtovalidateRequired,
|
|
9
|
+
} from './protobuf-validation';
|
|
10
|
+
|
|
11
|
+
describe('protobuf validation helpers', () => {
|
|
12
|
+
it('flattens direct and aggregate Protovalidate options', () => {
|
|
13
|
+
const field: ProtobufField = {
|
|
14
|
+
name: 'status',
|
|
15
|
+
type: 'Status',
|
|
16
|
+
options: [
|
|
17
|
+
{ name: '(buf.validate.field).required', value: true },
|
|
18
|
+
{
|
|
19
|
+
name: '(buf.validate.field).enum',
|
|
20
|
+
value: { defined_only: true, not_in: [0] },
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
expect(getProtovalidateRules(field)).toEqual([
|
|
26
|
+
{ path: 'required', value: true },
|
|
27
|
+
{ path: 'enum.defined_only', value: true },
|
|
28
|
+
{ path: 'enum.not_in', value: [0] },
|
|
29
|
+
]);
|
|
30
|
+
expect(isProtovalidateRequired(field)).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('ignores non-Protovalidate custom options', () => {
|
|
34
|
+
const field: ProtobufField = {
|
|
35
|
+
name: 'description',
|
|
36
|
+
type: 'string',
|
|
37
|
+
options: [{ name: '(datahub.v1.gdpr_rule).string', value: true }],
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
expect(getProtovalidateRules(field)).toEqual([]);
|
|
41
|
+
expect(isProtovalidateRequired(field)).toBe(false);
|
|
42
|
+
expect(getCustomFieldAnnotations(field)).toEqual([{ name: '(datahub.v1.gdpr_rule).string', value: true }]);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('only treats extension options as custom annotations and formats aggregate values', () => {
|
|
46
|
+
const field: ProtobufField = {
|
|
47
|
+
name: 'description',
|
|
48
|
+
type: 'string',
|
|
49
|
+
options: [
|
|
50
|
+
{ name: 'deprecated', value: true },
|
|
51
|
+
{ name: '(buf.validate.field).string.min_len', value: 1 },
|
|
52
|
+
{ name: '(datahub.v1.classification)', value: { level: 'personal', encrypted: true } },
|
|
53
|
+
],
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
expect(getCustomFieldAnnotations(field)).toEqual([
|
|
57
|
+
{
|
|
58
|
+
name: '(datahub.v1.classification)',
|
|
59
|
+
value: { level: 'personal', encrypted: true },
|
|
60
|
+
},
|
|
61
|
+
]);
|
|
62
|
+
expect(formatProtobufOptionValue(getCustomFieldAnnotations(field)[0].value)).toBe('{"level":"personal","encrypted":true}');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('formats common rules for display', () => {
|
|
66
|
+
expect(formatProtovalidateRule({ path: 'string.email', value: true })).toEqual({ label: 'Format', value: 'Email' });
|
|
67
|
+
expect(formatProtovalidateRule({ path: 'uint32.gte', value: 1 })).toEqual({ label: 'Minimum', value: '1' });
|
|
68
|
+
expect(formatProtovalidateRule({ path: 'enum.defined_only', value: true })).toEqual({
|
|
69
|
+
label: 'Defined enum values only',
|
|
70
|
+
});
|
|
71
|
+
expect(formatProtovalidateRule({ path: 'enum.not_in', value: [0] })).toEqual({
|
|
72
|
+
label: 'Excluded values',
|
|
73
|
+
value: '0',
|
|
74
|
+
});
|
|
75
|
+
expect(formatProtovalidateRule({ path: 'repeated.items.string.min_len', value: 3 })).toEqual({
|
|
76
|
+
label: 'Item minimum length',
|
|
77
|
+
value: '3',
|
|
78
|
+
});
|
|
79
|
+
expect(formatProtovalidateRule({ path: 'map.values.string.max_len', value: 100 })).toEqual({
|
|
80
|
+
label: 'Value maximum length',
|
|
81
|
+
value: '100',
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
});
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import type { ProtobufField, ProtobufOptionValue } from '@utils/protobuf-schema';
|
|
2
|
+
|
|
3
|
+
const PROTOVALIDATE_FIELD_OPTION = '(buf.validate.field)';
|
|
4
|
+
|
|
5
|
+
export interface ProtovalidateRule {
|
|
6
|
+
path: string;
|
|
7
|
+
value: ProtobufOptionValue;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface ProtovalidateRuleDisplay {
|
|
11
|
+
label: string;
|
|
12
|
+
value?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ProtobufCustomAnnotation {
|
|
16
|
+
name: string;
|
|
17
|
+
value: ProtobufOptionValue;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const isOptionObject = (value: ProtobufOptionValue): value is { [key: string]: ProtobufOptionValue } =>
|
|
21
|
+
typeof value === 'object' && !Array.isArray(value);
|
|
22
|
+
|
|
23
|
+
const flattenOptionValue = (path: string, value: ProtobufOptionValue): ProtovalidateRule[] => {
|
|
24
|
+
if (!isOptionObject(value)) return [{ path, value }];
|
|
25
|
+
|
|
26
|
+
return Object.entries(value).flatMap(([name, nestedValue]) => flattenOptionValue(path ? `${path}.${name}` : name, nestedValue));
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const getProtovalidateRules = (field: ProtobufField): ProtovalidateRule[] =>
|
|
30
|
+
(field.options ?? []).flatMap((option) => {
|
|
31
|
+
if (option.name !== PROTOVALIDATE_FIELD_OPTION && !option.name.startsWith(`${PROTOVALIDATE_FIELD_OPTION}.`)) return [];
|
|
32
|
+
|
|
33
|
+
const path = option.name.slice(PROTOVALIDATE_FIELD_OPTION.length).replace(/^\./, '');
|
|
34
|
+
return flattenOptionValue(path, option.value);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
export const isProtovalidateRequired = (field: ProtobufField) =>
|
|
38
|
+
getProtovalidateRules(field).some((rule) => rule.path === 'required' && rule.value === true);
|
|
39
|
+
|
|
40
|
+
export const getCustomFieldAnnotations = (field: ProtobufField): ProtobufCustomAnnotation[] =>
|
|
41
|
+
(field.options ?? [])
|
|
42
|
+
.filter(
|
|
43
|
+
(option) =>
|
|
44
|
+
option.name.startsWith('(') &&
|
|
45
|
+
option.name !== PROTOVALIDATE_FIELD_OPTION &&
|
|
46
|
+
!option.name.startsWith(`${PROTOVALIDATE_FIELD_OPTION}.`)
|
|
47
|
+
)
|
|
48
|
+
.map(({ name, value }) => ({ name, value }));
|
|
49
|
+
|
|
50
|
+
export const formatProtobufOptionValue = (value: ProtobufOptionValue): string => {
|
|
51
|
+
if (Array.isArray(value)) return value.map(formatProtobufOptionValue).join(', ');
|
|
52
|
+
if (isOptionObject(value)) return JSON.stringify(value);
|
|
53
|
+
return String(value);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const humanize = (value: string) => value.replace(/_/g, ' ').replace(/\b\w/g, (character) => character.toUpperCase());
|
|
57
|
+
|
|
58
|
+
const FORMAT_NAMES: Record<string, string> = {
|
|
59
|
+
address: 'Address',
|
|
60
|
+
email: 'Email',
|
|
61
|
+
hostname: 'Hostname',
|
|
62
|
+
ip: 'IP',
|
|
63
|
+
ipv4: 'IPv4',
|
|
64
|
+
ipv6: 'IPv6',
|
|
65
|
+
protobuf_fqn: 'Protobuf FQN',
|
|
66
|
+
tuuid: 'TUUID',
|
|
67
|
+
uri: 'URI',
|
|
68
|
+
uri_ref: 'URI reference',
|
|
69
|
+
uuid: 'UUID',
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const getRuleScope = (path: string) => {
|
|
73
|
+
if (path.includes('repeated.items')) return 'Item';
|
|
74
|
+
if (path.includes('map.keys')) return 'Key';
|
|
75
|
+
if (path.includes('map.values')) return 'Value';
|
|
76
|
+
return '';
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const withScope = (scope: string, label: string) =>
|
|
80
|
+
scope ? `${scope} ${label.charAt(0).toLowerCase()}${label.slice(1)}` : label;
|
|
81
|
+
|
|
82
|
+
export const formatProtovalidateRule = ({ path, value }: ProtovalidateRule): ProtovalidateRuleDisplay => {
|
|
83
|
+
const name = path.split('.').pop() ?? path;
|
|
84
|
+
const scope = getRuleScope(path);
|
|
85
|
+
const formattedValue = formatProtobufOptionValue(value);
|
|
86
|
+
|
|
87
|
+
switch (name) {
|
|
88
|
+
case 'required':
|
|
89
|
+
return value === true ? { label: 'Required' } : { label: 'Required', value: formattedValue };
|
|
90
|
+
case 'const':
|
|
91
|
+
return { label: withScope(scope, 'Constant'), value: formattedValue };
|
|
92
|
+
case 'len':
|
|
93
|
+
return { label: withScope(scope, 'Length'), value: formattedValue };
|
|
94
|
+
case 'min_len':
|
|
95
|
+
return { label: withScope(scope, 'Minimum length'), value: formattedValue };
|
|
96
|
+
case 'max_len':
|
|
97
|
+
return { label: withScope(scope, 'Maximum length'), value: formattedValue };
|
|
98
|
+
case 'len_bytes':
|
|
99
|
+
return { label: withScope(scope, 'Byte length'), value: formattedValue };
|
|
100
|
+
case 'min_bytes':
|
|
101
|
+
return { label: withScope(scope, 'Minimum byte length'), value: formattedValue };
|
|
102
|
+
case 'max_bytes':
|
|
103
|
+
return { label: withScope(scope, 'Maximum byte length'), value: formattedValue };
|
|
104
|
+
case 'pattern':
|
|
105
|
+
return { label: withScope(scope, 'Match pattern'), value: formattedValue };
|
|
106
|
+
case 'gt':
|
|
107
|
+
return { label: withScope(scope, 'Greater than'), value: formattedValue };
|
|
108
|
+
case 'gte':
|
|
109
|
+
return { label: withScope(scope, 'Minimum'), value: formattedValue };
|
|
110
|
+
case 'lt':
|
|
111
|
+
return { label: withScope(scope, 'Less than'), value: formattedValue };
|
|
112
|
+
case 'lte':
|
|
113
|
+
return { label: withScope(scope, 'Maximum'), value: formattedValue };
|
|
114
|
+
case 'in':
|
|
115
|
+
return { label: withScope(scope, 'Allowed values'), value: formattedValue };
|
|
116
|
+
case 'not_in':
|
|
117
|
+
return { label: withScope(scope, 'Excluded values'), value: formattedValue };
|
|
118
|
+
case 'defined_only':
|
|
119
|
+
return value === true
|
|
120
|
+
? { label: 'Defined enum values only' }
|
|
121
|
+
: { label: 'Defined enum values only', value: formattedValue };
|
|
122
|
+
case 'min_items':
|
|
123
|
+
return { label: 'Minimum items', value: formattedValue };
|
|
124
|
+
case 'max_items':
|
|
125
|
+
return { label: 'Maximum items', value: formattedValue };
|
|
126
|
+
case 'unique':
|
|
127
|
+
return value === true ? { label: 'Unique items' } : { label: 'Unique items', value: formattedValue };
|
|
128
|
+
case 'min_pairs':
|
|
129
|
+
return { label: 'Minimum map entries', value: formattedValue };
|
|
130
|
+
case 'max_pairs':
|
|
131
|
+
return { label: 'Maximum map entries', value: formattedValue };
|
|
132
|
+
case 'email':
|
|
133
|
+
case 'hostname':
|
|
134
|
+
case 'ip':
|
|
135
|
+
case 'ipv4':
|
|
136
|
+
case 'ipv6':
|
|
137
|
+
case 'uri':
|
|
138
|
+
case 'uri_ref':
|
|
139
|
+
case 'address':
|
|
140
|
+
case 'uuid':
|
|
141
|
+
case 'tuuid':
|
|
142
|
+
case 'protobuf_fqn':
|
|
143
|
+
return value === true
|
|
144
|
+
? { label: withScope(scope, 'Format'), value: FORMAT_NAMES[name] }
|
|
145
|
+
: { label: withScope(scope, humanize(name)), value: formattedValue };
|
|
146
|
+
default:
|
|
147
|
+
return { label: withScope(scope, humanize(name)), value: formattedValue };
|
|
148
|
+
}
|
|
149
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { glob } from 'astro/loaders';
|
|
1
|
+
import { glob, type Loader } from 'astro/loaders';
|
|
2
2
|
import picomatch from 'picomatch';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
@@ -36,7 +36,7 @@ const matchesGlobPattern = (entry: string, patterns: string | string[]) => {
|
|
|
36
36
|
return picomatch.isMatch(entry, positivePatterns) && !picomatch.isMatch(entry, negativePatterns);
|
|
37
37
|
};
|
|
38
38
|
|
|
39
|
-
export const globWithSafeWatcher = (options: GlobOptions) => {
|
|
39
|
+
export const globWithSafeWatcher = (options: GlobOptions): Loader => {
|
|
40
40
|
const loader = glob(options);
|
|
41
41
|
|
|
42
42
|
return {
|
|
@@ -17,6 +17,14 @@ export interface ProtobufField {
|
|
|
17
17
|
map?: { keyType: string; valueType: string };
|
|
18
18
|
oneof?: string;
|
|
19
19
|
doc?: string;
|
|
20
|
+
options?: ProtobufOption[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type ProtobufOptionValue = string | number | boolean | ProtobufOptionValue[] | { [key: string]: ProtobufOptionValue };
|
|
24
|
+
|
|
25
|
+
export interface ProtobufOption {
|
|
26
|
+
name: string;
|
|
27
|
+
value: ProtobufOptionValue;
|
|
20
28
|
}
|
|
21
29
|
|
|
22
30
|
export interface ProtobufEnumValue {
|
|
@@ -60,6 +68,81 @@ const isIdentifierChar = (char: string) => /[A-Za-z0-9_.]/.test(char);
|
|
|
60
68
|
const isNumberStart = (char: string) => /[0-9-]/.test(char);
|
|
61
69
|
const isNumberChar = (char: string) => /[0-9a-fA-FxX.+-]/.test(char);
|
|
62
70
|
|
|
71
|
+
const UTF8_ENCODER = new TextEncoder();
|
|
72
|
+
const UTF8_DECODER = new TextDecoder('utf-8', { fatal: true });
|
|
73
|
+
|
|
74
|
+
const SIMPLE_STRING_ESCAPES: Record<string, number> = {
|
|
75
|
+
a: 0x07,
|
|
76
|
+
b: 0x08,
|
|
77
|
+
f: 0x0c,
|
|
78
|
+
n: 0x0a,
|
|
79
|
+
r: 0x0d,
|
|
80
|
+
t: 0x09,
|
|
81
|
+
v: 0x0b,
|
|
82
|
+
'?': 0x3f,
|
|
83
|
+
'\\': 0x5c,
|
|
84
|
+
"'": 0x27,
|
|
85
|
+
'"': 0x22,
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
function decodeStringEscape(content: string, start: number, line: number): { bytes: number[]; nextIndex: number } {
|
|
89
|
+
const escapeType = content[start + 1];
|
|
90
|
+
if (!escapeType) throw new Error(`Unterminated string escape at line ${line}`);
|
|
91
|
+
|
|
92
|
+
if (escapeType in SIMPLE_STRING_ESCAPES) {
|
|
93
|
+
return { bytes: [SIMPLE_STRING_ESCAPES[escapeType]], nextIndex: start + 2 };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (escapeType === 'x' || escapeType === 'X') {
|
|
97
|
+
const digits = content.slice(start + 2).match(/^[0-9a-fA-F]{1,2}/)?.[0];
|
|
98
|
+
if (!digits) throw new Error(`Invalid hexadecimal string escape at line ${line}`);
|
|
99
|
+
return { bytes: [Number.parseInt(digits, 16)], nextIndex: start + 2 + digits.length };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (/[0-7]/.test(escapeType)) {
|
|
103
|
+
const digits = content.slice(start + 1).match(/^[0-7]{1,3}/)![0];
|
|
104
|
+
const byte = Number.parseInt(digits, 8);
|
|
105
|
+
if (byte > 0xff) throw new Error(`Octal string escape is out of byte range at line ${line}`);
|
|
106
|
+
return { bytes: [byte], nextIndex: start + 1 + digits.length };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (escapeType === 'u' || escapeType === 'U') {
|
|
110
|
+
const digitCount = escapeType === 'u' ? 4 : 8;
|
|
111
|
+
const digits = content.slice(start + 2, start + 2 + digitCount);
|
|
112
|
+
if (digits.length !== digitCount || !/^[0-9a-fA-F]+$/.test(digits)) {
|
|
113
|
+
throw new Error(`Invalid Unicode string escape at line ${line}`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const codePoint = Number.parseInt(digits, 16);
|
|
117
|
+
if (codePoint > 0x10ffff || (codePoint >= 0xd800 && codePoint <= 0xdfff)) {
|
|
118
|
+
throw new Error(`Unicode string escape is out of range at line ${line}`);
|
|
119
|
+
}
|
|
120
|
+
return { bytes: [...UTF8_ENCODER.encode(String.fromCodePoint(codePoint))], nextIndex: start + 2 + digitCount };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
throw new Error(`Invalid string escape "\\${escapeType}" at line ${line}`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function parseIntegerOptionValue(value: string): number | string | undefined {
|
|
127
|
+
const sign = value.startsWith('-') ? -1n : 1n;
|
|
128
|
+
const unsignedValue = value.startsWith('-') || value.startsWith('+') ? value.slice(1) : value;
|
|
129
|
+
let magnitude: bigint;
|
|
130
|
+
|
|
131
|
+
if (/^0[xX][0-9a-fA-F]+$/.test(unsignedValue)) {
|
|
132
|
+
magnitude = BigInt(unsignedValue);
|
|
133
|
+
} else if (/^0[0-7]*$/.test(unsignedValue)) {
|
|
134
|
+
magnitude = BigInt(`0o${unsignedValue.slice(1) || '0'}`);
|
|
135
|
+
} else if (/^[1-9][0-9]*$/.test(unsignedValue)) {
|
|
136
|
+
magnitude = BigInt(unsignedValue);
|
|
137
|
+
} else {
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const parsedValue = sign * magnitude;
|
|
142
|
+
if (parsedValue < BigInt(Number.MIN_SAFE_INTEGER) || parsedValue > BigInt(Number.MAX_SAFE_INTEGER)) return value;
|
|
143
|
+
return Number(parsedValue);
|
|
144
|
+
}
|
|
145
|
+
|
|
63
146
|
function tokenize(content: string): { tokens: Token[]; trailingComments: Map<number, string> } {
|
|
64
147
|
const tokens: Token[] = [];
|
|
65
148
|
const trailingComments = new Map<number, string>();
|
|
@@ -128,18 +211,31 @@ function tokenize(content: string): { tokens: Token[]; trailingComments: Map<num
|
|
|
128
211
|
// String literal
|
|
129
212
|
if (char === '"' || char === "'") {
|
|
130
213
|
const quote = char;
|
|
131
|
-
|
|
214
|
+
const bytes: number[] = [];
|
|
132
215
|
i++;
|
|
216
|
+
const rawValueStart = i;
|
|
217
|
+
let plainTextStart = i;
|
|
133
218
|
while (i < content.length && content[i] !== quote) {
|
|
134
219
|
if (content[i] === '\\') {
|
|
135
|
-
|
|
136
|
-
i
|
|
220
|
+
bytes.push(...UTF8_ENCODER.encode(content.slice(plainTextStart, i)));
|
|
221
|
+
const decodedEscape = decodeStringEscape(content, i, line);
|
|
222
|
+
bytes.push(...decodedEscape.bytes);
|
|
223
|
+
i = decodedEscape.nextIndex;
|
|
224
|
+
plainTextStart = i;
|
|
137
225
|
continue;
|
|
138
226
|
}
|
|
139
227
|
if (content[i] === '\n') line++;
|
|
140
|
-
value += content[i];
|
|
141
228
|
i++;
|
|
142
229
|
}
|
|
230
|
+
bytes.push(...UTF8_ENCODER.encode(content.slice(plainTextStart, i)));
|
|
231
|
+
if (i >= content.length) throw new Error(`Unterminated string literal at line ${line}`);
|
|
232
|
+
const rawValue = content.slice(rawValueStart, i);
|
|
233
|
+
let value: string;
|
|
234
|
+
try {
|
|
235
|
+
value = UTF8_DECODER.decode(Uint8Array.from(bytes));
|
|
236
|
+
} catch {
|
|
237
|
+
value = rawValue;
|
|
238
|
+
}
|
|
143
239
|
i++;
|
|
144
240
|
pushToken({ value, line, isString: true });
|
|
145
241
|
continue;
|
|
@@ -164,6 +260,18 @@ function tokenize(content: string): { tokens: Token[]; trailingComments: Map<num
|
|
|
164
260
|
continue;
|
|
165
261
|
}
|
|
166
262
|
|
|
263
|
+
// Signed symbolic floating-point values used by protobuf options (for example +inf).
|
|
264
|
+
if ((char === '+' || char === '-') && isIdentifierStart(content[i + 1] || '')) {
|
|
265
|
+
let value = char;
|
|
266
|
+
i++;
|
|
267
|
+
while (i < content.length && isIdentifierChar(content[i])) {
|
|
268
|
+
value += content[i];
|
|
269
|
+
i++;
|
|
270
|
+
}
|
|
271
|
+
pushToken({ value, line });
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
|
|
167
275
|
if (isNumberStart(char)) {
|
|
168
276
|
let value = content[i];
|
|
169
277
|
i++;
|
|
@@ -232,16 +340,101 @@ class ProtobufParser {
|
|
|
232
340
|
}
|
|
233
341
|
}
|
|
234
342
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
343
|
+
private parseAggregateOptionKey(): string {
|
|
344
|
+
if (this.peek()?.value !== '[') return this.next().value;
|
|
345
|
+
|
|
346
|
+
this.next();
|
|
347
|
+
const nameParts: string[] = [];
|
|
348
|
+
while (this.peek() && this.peek()!.value !== ']') nameParts.push(this.next().value);
|
|
349
|
+
this.expect(']');
|
|
350
|
+
return `[${nameParts.join('')}]`;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
private parseOptionValue(): ProtobufOptionValue {
|
|
354
|
+
const token = this.peek();
|
|
355
|
+
if (!token) throw new Error('Unexpected end of protobuf option');
|
|
356
|
+
|
|
357
|
+
if (token.value === '{' || token.value === '<') {
|
|
358
|
+
const openingToken = this.next().value;
|
|
359
|
+
const closingToken = openingToken === '{' ? '}' : '>';
|
|
360
|
+
const value: { [key: string]: ProtobufOptionValue } = {};
|
|
361
|
+
|
|
362
|
+
while (this.peek() && this.peek()!.value !== closingToken) {
|
|
363
|
+
if (this.peek()!.value === ',' || this.peek()!.value === ';') {
|
|
364
|
+
this.next();
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const key = this.parseAggregateOptionKey();
|
|
369
|
+
if (this.peek()?.value === ':') this.next();
|
|
370
|
+
const entryValue = this.parseOptionValue();
|
|
371
|
+
const existingValue = value[key];
|
|
372
|
+
value[key] =
|
|
373
|
+
existingValue === undefined
|
|
374
|
+
? entryValue
|
|
375
|
+
: Array.isArray(existingValue)
|
|
376
|
+
? [...existingValue, entryValue]
|
|
377
|
+
: [existingValue, entryValue];
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
this.expect(closingToken);
|
|
381
|
+
return value;
|
|
244
382
|
}
|
|
383
|
+
|
|
384
|
+
if (token.value === '[') {
|
|
385
|
+
this.next();
|
|
386
|
+
const values: ProtobufOptionValue[] = [];
|
|
387
|
+
|
|
388
|
+
while (this.peek() && this.peek()!.value !== ']') {
|
|
389
|
+
if (this.peek()!.value === ',' || this.peek()!.value === ';') {
|
|
390
|
+
this.next();
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
values.push(this.parseOptionValue());
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
this.expect(']');
|
|
397
|
+
return values;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const valueToken = this.next();
|
|
401
|
+
if (valueToken.isString) {
|
|
402
|
+
let value = valueToken.value;
|
|
403
|
+
while (this.peek()?.isString) value += this.next().value;
|
|
404
|
+
return value;
|
|
405
|
+
}
|
|
406
|
+
if (valueToken.value === 'true') return true;
|
|
407
|
+
if (valueToken.value === 'false') return false;
|
|
408
|
+
|
|
409
|
+
const integerValue = parseIntegerOptionValue(valueToken.value);
|
|
410
|
+
if (integerValue !== undefined) return integerValue;
|
|
411
|
+
|
|
412
|
+
const numericValue = Number(valueToken.value);
|
|
413
|
+
if (Number.isNaN(numericValue)) return valueToken.value;
|
|
414
|
+
if (Number.isInteger(numericValue) && !Number.isSafeInteger(numericValue)) return valueToken.value;
|
|
415
|
+
return numericValue;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
private parseFieldOptions(): ProtobufOption[] {
|
|
419
|
+
if (this.peek()?.value !== '[') return [];
|
|
420
|
+
|
|
421
|
+
this.next();
|
|
422
|
+
const options: ProtobufOption[] = [];
|
|
423
|
+
|
|
424
|
+
while (this.peek() && this.peek()!.value !== ']') {
|
|
425
|
+
if (this.peek()!.value === ',') {
|
|
426
|
+
this.next();
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
const nameParts: string[] = [];
|
|
431
|
+
while (this.peek() && this.peek()!.value !== '=') nameParts.push(this.next().value);
|
|
432
|
+
this.expect('=');
|
|
433
|
+
options.push({ name: nameParts.join(''), value: this.parseOptionValue() });
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
this.expect(']');
|
|
437
|
+
return options;
|
|
245
438
|
}
|
|
246
439
|
|
|
247
440
|
private getTrailingDoc(line: number): string | undefined {
|
|
@@ -384,7 +577,7 @@ class ProtobufParser {
|
|
|
384
577
|
const name = this.next().value;
|
|
385
578
|
this.expect('=');
|
|
386
579
|
const numberToken = this.next();
|
|
387
|
-
this.
|
|
580
|
+
const options = this.parseFieldOptions();
|
|
388
581
|
const terminator = this.expect(';');
|
|
389
582
|
|
|
390
583
|
const parsedNumber = Number.parseInt(numberToken.value, 10);
|
|
@@ -396,6 +589,7 @@ class ProtobufParser {
|
|
|
396
589
|
label,
|
|
397
590
|
oneof,
|
|
398
591
|
doc: firstToken.doc || typeToken.doc || this.getTrailingDoc(terminator.line),
|
|
592
|
+
...(options.length > 0 ? { options } : {}),
|
|
399
593
|
};
|
|
400
594
|
}
|
|
401
595
|
|
|
@@ -409,7 +603,7 @@ class ProtobufParser {
|
|
|
409
603
|
const name = this.next().value;
|
|
410
604
|
this.expect('=');
|
|
411
605
|
const numberToken = this.next();
|
|
412
|
-
this.
|
|
606
|
+
const options = this.parseFieldOptions();
|
|
413
607
|
const terminator = this.expect(';');
|
|
414
608
|
|
|
415
609
|
const parsedNumber = Number.parseInt(numberToken.value, 10);
|
|
@@ -420,6 +614,7 @@ class ProtobufParser {
|
|
|
420
614
|
map: { keyType, valueType },
|
|
421
615
|
number: Number.isNaN(parsedNumber) ? undefined : parsedNumber,
|
|
422
616
|
doc: keyword.doc || this.getTrailingDoc(terminator.line),
|
|
617
|
+
...(options.length > 0 ? { options } : {}),
|
|
423
618
|
};
|
|
424
619
|
}
|
|
425
620
|
|
|
@@ -452,7 +647,7 @@ class ProtobufParser {
|
|
|
452
647
|
const valueToken = this.next();
|
|
453
648
|
this.expect('=');
|
|
454
649
|
const numberToken = this.next();
|
|
455
|
-
this.
|
|
650
|
+
this.parseFieldOptions();
|
|
456
651
|
const terminator = this.expect(';');
|
|
457
652
|
|
|
458
653
|
const parsedNumber = Number.parseInt(numberToken.value, 10);
|
package/package.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
},
|
|
8
8
|
"license": "SEE LICENSE IN LICENSE",
|
|
9
9
|
"type": "module",
|
|
10
|
-
"version": "4.10.
|
|
10
|
+
"version": "4.10.15",
|
|
11
11
|
"publishConfig": {
|
|
12
12
|
"access": "public"
|
|
13
13
|
},
|
|
@@ -32,10 +32,10 @@
|
|
|
32
32
|
],
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@ai-sdk/react": "^3.0.17",
|
|
35
|
-
"@astrojs/markdown-remark": "^7.
|
|
36
|
-
"@astrojs/mdx": "^
|
|
37
|
-
"@astrojs/node": "^11.1.
|
|
38
|
-
"@astrojs/react": "^6.0.
|
|
35
|
+
"@astrojs/markdown-remark": "^7.3.1",
|
|
36
|
+
"@astrojs/mdx": "^8.0.1",
|
|
37
|
+
"@astrojs/node": "^11.1.6",
|
|
38
|
+
"@astrojs/react": "^6.0.6",
|
|
39
39
|
"@astrojs/rss": "^4.0.19",
|
|
40
40
|
"@asyncapi/avro-schema-parser": "3.0.24",
|
|
41
41
|
"@asyncapi/parser": "^3.6.0",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"@tanstack/react-table": "^8.17.3",
|
|
62
62
|
"@xyflow/react": "^12.3.6",
|
|
63
63
|
"ai": "^6.0.17",
|
|
64
|
-
"astro": "7.
|
|
64
|
+
"astro": "7.3.3",
|
|
65
65
|
"astro-expressive-code": "^0.44.0",
|
|
66
66
|
"astro-seo": "^0.8.4",
|
|
67
67
|
"auth-astro": "^4.2.0",
|
|
@@ -120,8 +120,8 @@
|
|
|
120
120
|
"uuid": "^11.1.1",
|
|
121
121
|
"zod": "^4.3.6",
|
|
122
122
|
"@eventcatalog/linter": "1.1.19",
|
|
123
|
-
"@eventcatalog/
|
|
124
|
-
"@eventcatalog/
|
|
123
|
+
"@eventcatalog/sdk": "2.29.0",
|
|
124
|
+
"@eventcatalog/visualiser": "^4.1.4"
|
|
125
125
|
},
|
|
126
126
|
"devDependencies": {
|
|
127
127
|
"@astrojs/check": "^0.9.10",
|