@pikku/inspector 0.11.1 → 0.12.0

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.
Files changed (189) hide show
  1. package/CHANGELOG.md +26 -1
  2. package/OPTIMIZATION-PLAN.md +195 -0
  3. package/dist/add/add-ai-agent.d.ts +2 -0
  4. package/dist/add/add-ai-agent.js +314 -0
  5. package/dist/add/add-channel.js +69 -61
  6. package/dist/add/add-cli.js +36 -18
  7. package/dist/add/add-file-with-factory.js +2 -0
  8. package/dist/add/add-functions.js +327 -59
  9. package/dist/add/add-http-route.d.ts +19 -10
  10. package/dist/add/add-http-route.js +153 -44
  11. package/dist/add/add-http-routes.d.ts +5 -0
  12. package/dist/add/add-http-routes.js +159 -0
  13. package/dist/add/add-keyed-wiring.d.ts +12 -0
  14. package/dist/add/add-keyed-wiring.js +97 -0
  15. package/dist/add/add-mcp-prompt.js +14 -9
  16. package/dist/add/add-mcp-resource.js +14 -9
  17. package/dist/add/add-middleware.d.ts +1 -4
  18. package/dist/add/add-middleware.js +364 -79
  19. package/dist/add/add-permission.d.ts +1 -1
  20. package/dist/add/add-permission.js +152 -40
  21. package/dist/add/add-queue-worker.js +18 -12
  22. package/dist/add/add-rpc-invocations.d.ts +3 -0
  23. package/dist/add/add-rpc-invocations.js +65 -25
  24. package/dist/add/add-schedule.js +11 -5
  25. package/dist/add/add-secret.d.ts +3 -0
  26. package/dist/add/add-secret.js +82 -0
  27. package/dist/add/add-trigger.d.ts +2 -0
  28. package/dist/add/add-trigger.js +87 -0
  29. package/dist/add/add-variable.d.ts +1 -0
  30. package/dist/add/add-variable.js +8 -0
  31. package/dist/add/add-workflow-graph.d.ts +7 -0
  32. package/dist/add/add-workflow-graph.js +396 -0
  33. package/dist/add/add-workflow.js +124 -26
  34. package/dist/error-codes.d.ts +16 -1
  35. package/dist/error-codes.js +21 -1
  36. package/dist/index.d.ts +9 -5
  37. package/dist/index.js +5 -2
  38. package/dist/inspector.d.ts +1 -1
  39. package/dist/inspector.js +106 -13
  40. package/dist/schema-generator.d.ts +1 -0
  41. package/dist/schema-generator.js +1 -0
  42. package/dist/types-map.js +10 -1
  43. package/dist/types.d.ts +180 -30
  44. package/dist/utils/compute-required-schemas.d.ts +4 -0
  45. package/dist/utils/compute-required-schemas.js +41 -0
  46. package/dist/utils/contract-hashes.d.ts +35 -0
  47. package/dist/utils/contract-hashes.js +202 -0
  48. package/dist/utils/custom-types-generator.d.ts +9 -0
  49. package/dist/utils/custom-types-generator.js +71 -0
  50. package/dist/utils/detect-schema-vendor.d.ts +22 -0
  51. package/dist/utils/detect-schema-vendor.js +76 -0
  52. package/dist/utils/ensure-function-metadata.d.ts +5 -2
  53. package/dist/utils/ensure-function-metadata.js +220 -6
  54. package/dist/utils/extract-function-name.d.ts +5 -16
  55. package/dist/utils/extract-function-name.js +93 -298
  56. package/dist/utils/extract-services.d.ts +2 -1
  57. package/dist/utils/extract-services.js +25 -1
  58. package/dist/utils/filter-inspector-state.js +107 -23
  59. package/dist/utils/get-property-value.d.ts +8 -2
  60. package/dist/utils/get-property-value.js +33 -4
  61. package/dist/utils/hash.d.ts +2 -0
  62. package/dist/utils/hash.js +23 -0
  63. package/dist/utils/middleware.d.ts +7 -30
  64. package/dist/utils/middleware.js +80 -66
  65. package/dist/utils/permissions.d.ts +2 -2
  66. package/dist/utils/permissions.js +10 -10
  67. package/dist/utils/post-process.d.ts +9 -10
  68. package/dist/utils/post-process.js +231 -24
  69. package/dist/utils/resolve-external-package.d.ts +12 -0
  70. package/dist/utils/resolve-external-package.js +34 -0
  71. package/dist/utils/resolve-function-types.d.ts +6 -0
  72. package/dist/utils/resolve-function-types.js +29 -0
  73. package/dist/utils/resolve-identifier.d.ts +10 -0
  74. package/dist/utils/resolve-identifier.js +36 -0
  75. package/dist/utils/resolve-versions.d.ts +2 -0
  76. package/dist/utils/resolve-versions.js +78 -0
  77. package/dist/utils/schema-generator.d.ts +9 -0
  78. package/dist/utils/schema-generator.js +209 -0
  79. package/dist/utils/serialize-inspector-state.d.ts +73 -13
  80. package/dist/utils/serialize-inspector-state.js +102 -6
  81. package/dist/utils/serialize-mcp-json.d.ts +2 -0
  82. package/dist/utils/serialize-mcp-json.js +99 -0
  83. package/dist/utils/serialize-middleware-groups-meta.d.ts +12 -0
  84. package/dist/utils/serialize-middleware-groups-meta.js +28 -0
  85. package/dist/utils/serialize-openapi-json.d.ts +85 -0
  86. package/dist/utils/serialize-openapi-json.js +151 -0
  87. package/dist/utils/serialize-permissions-groups-meta.d.ts +6 -0
  88. package/dist/utils/serialize-permissions-groups-meta.js +31 -0
  89. package/dist/utils/workflow/dsl/deserialize-dsl-workflow.d.ts +24 -0
  90. package/dist/utils/workflow/dsl/deserialize-dsl-workflow.js +830 -0
  91. package/dist/{workflow/extract-simple-workflow.d.ts → utils/workflow/dsl/extract-dsl-workflow.d.ts} +4 -2
  92. package/dist/{workflow/extract-simple-workflow.js → utils/workflow/dsl/extract-dsl-workflow.js} +572 -72
  93. package/dist/utils/workflow/dsl/index.d.ts +7 -0
  94. package/dist/utils/workflow/dsl/index.js +7 -0
  95. package/dist/{workflow → utils/workflow/dsl}/patterns.d.ts +21 -0
  96. package/dist/{workflow → utils/workflow/dsl}/patterns.js +90 -10
  97. package/dist/{workflow → utils/workflow/dsl}/validation.d.ts +2 -0
  98. package/dist/{workflow → utils/workflow/dsl}/validation.js +25 -7
  99. package/dist/utils/workflow/graph/convert-dsl-to-graph.d.ts +13 -0
  100. package/dist/utils/workflow/graph/convert-dsl-to-graph.js +318 -0
  101. package/dist/utils/workflow/graph/finalize-workflow-wires.d.ts +3 -0
  102. package/dist/utils/workflow/graph/finalize-workflow-wires.js +276 -0
  103. package/dist/utils/workflow/graph/finalize-workflows.d.ts +2 -0
  104. package/dist/utils/workflow/graph/finalize-workflows.js +75 -0
  105. package/dist/utils/workflow/graph/index.d.ts +8 -0
  106. package/dist/utils/workflow/graph/index.js +8 -0
  107. package/dist/utils/workflow/graph/serialize-workflow-graph.d.ts +35 -0
  108. package/dist/utils/workflow/graph/serialize-workflow-graph.js +150 -0
  109. package/dist/utils/workflow/graph/workflow-graph.types.d.ts +203 -0
  110. package/dist/utils/workflow/graph/workflow-graph.types.js +38 -0
  111. package/dist/visit.js +13 -2
  112. package/package.json +26 -4
  113. package/src/add/add-ai-agent.ts +468 -0
  114. package/src/add/add-channel.ts +82 -79
  115. package/src/add/add-cli.ts +49 -20
  116. package/src/add/add-file-with-factory.ts +2 -0
  117. package/src/add/add-functions.ts +429 -71
  118. package/src/add/add-http-route.ts +246 -65
  119. package/src/add/add-http-routes.ts +228 -0
  120. package/src/add/add-keyed-wiring.ts +151 -0
  121. package/src/add/add-mcp-prompt.ts +26 -15
  122. package/src/add/add-mcp-resource.ts +27 -15
  123. package/src/add/add-middleware.ts +482 -80
  124. package/src/add/add-permission.ts +199 -40
  125. package/src/add/add-queue-worker.ts +24 -19
  126. package/src/add/add-rpc-invocations.ts +78 -31
  127. package/src/add/add-schedule.ts +16 -11
  128. package/src/add/add-secret.ts +140 -0
  129. package/src/add/add-trigger.ts +154 -0
  130. package/src/add/add-variable.ts +9 -0
  131. package/src/add/add-workflow-graph.ts +522 -0
  132. package/src/add/add-workflow.ts +117 -30
  133. package/src/error-codes.ts +26 -1
  134. package/src/index.ts +27 -8
  135. package/src/inspector.ts +145 -17
  136. package/src/schema-generator.ts +1 -0
  137. package/src/types-map.ts +12 -1
  138. package/src/types.ts +192 -51
  139. package/src/utils/compute-required-schemas.ts +49 -0
  140. package/src/utils/contract-hashes.test.ts +528 -0
  141. package/src/utils/contract-hashes.ts +290 -0
  142. package/src/utils/custom-types-generator.ts +88 -0
  143. package/src/utils/detect-schema-vendor.ts +90 -0
  144. package/src/utils/ensure-function-metadata.ts +324 -7
  145. package/src/utils/extract-function-name.ts +108 -358
  146. package/src/utils/extract-services.ts +35 -2
  147. package/src/utils/filter-inspector-state.test.ts +34 -20
  148. package/src/utils/filter-inspector-state.ts +140 -31
  149. package/src/utils/get-property-value.ts +50 -5
  150. package/src/utils/hash.ts +26 -0
  151. package/src/utils/middleware.test.ts +204 -0
  152. package/src/utils/middleware.ts +129 -67
  153. package/src/utils/permissions.test.ts +35 -12
  154. package/src/utils/permissions.ts +10 -10
  155. package/src/utils/post-process.ts +283 -43
  156. package/src/utils/resolve-external-package.ts +42 -0
  157. package/src/utils/resolve-function-types.ts +42 -0
  158. package/src/utils/resolve-identifier.ts +46 -0
  159. package/src/utils/resolve-versions.test.ts +249 -0
  160. package/src/utils/resolve-versions.ts +105 -0
  161. package/src/utils/schema-generator.ts +329 -0
  162. package/src/utils/serialize-inspector-state.ts +181 -20
  163. package/src/utils/serialize-mcp-json.ts +145 -0
  164. package/src/utils/serialize-middleware-groups-meta.ts +33 -0
  165. package/src/utils/serialize-openapi-json.ts +277 -0
  166. package/src/utils/serialize-permissions-groups-meta.ts +35 -0
  167. package/src/utils/test-data/inspector-state.json +69 -66
  168. package/src/utils/workflow/dsl/deserialize-dsl-workflow.ts +1104 -0
  169. package/src/{workflow/extract-simple-workflow.ts → utils/workflow/dsl/extract-dsl-workflow.ts} +678 -85
  170. package/src/utils/workflow/dsl/index.ts +11 -0
  171. package/src/{workflow → utils/workflow/dsl}/patterns.ts +108 -11
  172. package/src/{workflow → utils/workflow/dsl}/validation.ts +34 -7
  173. package/src/utils/workflow/graph/convert-dsl-to-graph.ts +422 -0
  174. package/src/utils/workflow/graph/finalize-workflow-wires.ts +310 -0
  175. package/src/utils/workflow/graph/finalize-workflows.ts +100 -0
  176. package/src/utils/workflow/graph/index.ts +11 -0
  177. package/src/utils/workflow/graph/serialize-workflow-graph.ts +216 -0
  178. package/src/utils/workflow/graph/workflow-graph.types.ts +231 -0
  179. package/src/visit.ts +14 -2
  180. package/tsconfig.tsbuildinfo +1 -1
  181. package/dist/add/add-mcp-tool.d.ts +0 -2
  182. package/dist/add/add-mcp-tool.js +0 -81
  183. package/dist/utils/extract-service-metadata.d.ts +0 -19
  184. package/dist/utils/extract-service-metadata.js +0 -244
  185. package/dist/utils/write-service-metadata.d.ts +0 -13
  186. package/dist/utils/write-service-metadata.js +0 -37
  187. package/src/add/add-mcp-tool.ts +0 -141
  188. package/src/utils/extract-service-metadata.ts +0 -353
  189. package/src/utils/write-service-metadata.ts +0 -51
@@ -0,0 +1,28 @@
1
+ const serializeGroupMap = (groupMap) => {
2
+ const result = {};
3
+ for (const [key, meta] of groupMap.entries()) {
4
+ result[key] = {
5
+ exportName: meta.exportName,
6
+ sourceFile: meta.sourceFile,
7
+ position: meta.position,
8
+ services: meta.services,
9
+ count: meta.count,
10
+ instanceIds: meta.instanceIds,
11
+ isFactory: meta.isFactory,
12
+ };
13
+ }
14
+ return result;
15
+ };
16
+ export const serializeMiddlewareGroupsMeta = (state) => {
17
+ return {
18
+ definitions: state.middleware.definitions,
19
+ instances: state.middleware.instances,
20
+ httpGroups: serializeGroupMap(state.http.routeMiddleware),
21
+ tagGroups: serializeGroupMap(state.middleware.tagMiddleware),
22
+ channelMiddleware: {
23
+ definitions: state.channelMiddleware.definitions,
24
+ instances: state.channelMiddleware.instances,
25
+ tagGroups: serializeGroupMap(state.channelMiddleware.tagMiddleware),
26
+ },
27
+ };
28
+ };
@@ -0,0 +1,85 @@
1
+ import type { FunctionsMeta } from '@pikku/core';
2
+ import type { HTTPWiringsMeta } from '@pikku/core/http';
3
+ import type { InspectorLogger } from '../types.js';
4
+ interface ErrorDetails {
5
+ status: number;
6
+ message: string;
7
+ }
8
+ interface OpenAPISpec {
9
+ openapi: string;
10
+ info: {
11
+ title: string;
12
+ version: string;
13
+ description?: string;
14
+ termsOfService?: string;
15
+ contact?: {
16
+ name?: string;
17
+ url?: string;
18
+ email?: string;
19
+ };
20
+ license?: {
21
+ name: string;
22
+ url?: string;
23
+ };
24
+ };
25
+ servers: {
26
+ url: string;
27
+ description?: string;
28
+ }[];
29
+ paths: Record<string, any>;
30
+ components: {
31
+ schemas: Record<string, any>;
32
+ responses?: Record<string, any>;
33
+ parameters?: Record<string, any>;
34
+ examples?: Record<string, any>;
35
+ requestBodies?: Record<string, any>;
36
+ headers?: Record<string, any>;
37
+ securitySchemes?: Record<string, any>;
38
+ };
39
+ security?: {
40
+ [key: string]: any[];
41
+ }[];
42
+ tags?: {
43
+ name: string;
44
+ description?: string;
45
+ }[];
46
+ externalDocs?: {
47
+ description?: string;
48
+ url: string;
49
+ };
50
+ }
51
+ export interface OpenAPISpecInfo {
52
+ info: {
53
+ title: string;
54
+ version: string;
55
+ description: string;
56
+ termsOfService?: string;
57
+ contact?: {
58
+ name?: string;
59
+ url?: string;
60
+ email?: string;
61
+ };
62
+ license?: {
63
+ name: string;
64
+ url?: string;
65
+ };
66
+ };
67
+ servers: {
68
+ url: string;
69
+ description?: string;
70
+ }[];
71
+ tags?: {
72
+ name: string;
73
+ description?: string;
74
+ }[];
75
+ externalDocs?: {
76
+ description?: string;
77
+ url: string;
78
+ };
79
+ securitySchemes?: Record<string, any>;
80
+ security?: {
81
+ [key: string]: any[];
82
+ }[];
83
+ }
84
+ export declare function generateOpenAPISpec(logger: InspectorLogger, functionsMeta: FunctionsMeta, httpMeta: HTTPWiringsMeta, schemas: Record<string, any>, additionalInfo: OpenAPISpecInfo, errors?: Map<any, ErrorDetails>): Promise<OpenAPISpec>;
85
+ export {};
@@ -0,0 +1,151 @@
1
+ import _convertSchema from '@openapi-contrib/json-schema-to-openapi-schema';
2
+ const convertSchema = 'default' in _convertSchema ? _convertSchema.default : _convertSchema;
3
+ const getErrorResponseForConstructorName = (constructorName, errors) => {
4
+ const entries = Array.from(errors.entries());
5
+ const foundError = entries.find(([e]) => e.name === constructorName);
6
+ if (foundError) {
7
+ return foundError[1];
8
+ }
9
+ return undefined;
10
+ };
11
+ const convertSchemasToBodyPayloads = async (functionsMeta, routesMeta, schemas) => {
12
+ const requiredSchemas = new Set();
13
+ for (const routeMeta of Object.values(routesMeta)) {
14
+ for (const { inputTypes, pikkuFuncId } of Object.values(routeMeta)) {
15
+ const output = functionsMeta[pikkuFuncId]?.outputs?.[0];
16
+ if (inputTypes?.body) {
17
+ requiredSchemas.add(inputTypes?.body);
18
+ }
19
+ if (output) {
20
+ requiredSchemas.add(output);
21
+ }
22
+ }
23
+ }
24
+ const convertedEntries = await Promise.all(Object.entries(schemas).map(async ([key, schema]) => {
25
+ if (requiredSchemas.has(key)) {
26
+ const convertedSchema = await convertSchema(schema, {
27
+ convertUnreferencedDefinitions: false,
28
+ dereference: { circular: 'ignore' },
29
+ });
30
+ return [key, convertedSchema];
31
+ }
32
+ return;
33
+ }));
34
+ return Object.fromEntries(convertedEntries.filter((s) => !!s));
35
+ };
36
+ export async function generateOpenAPISpec(logger, functionsMeta, httpMeta, schemas, additionalInfo, errors) {
37
+ const paths = {};
38
+ const errorsMap = errors ?? new Map();
39
+ for (const routeMeta of Object.values(httpMeta)) {
40
+ for (const meta of Object.values(routeMeta)) {
41
+ const { route, method, inputTypes, pikkuFuncId, params, query, errors, description, tags, } = meta;
42
+ const functionMeta = functionsMeta[pikkuFuncId];
43
+ if (!functionMeta) {
44
+ logger.error(`• No function metadata found for '${pikkuFuncId}' in route '${route}'.`);
45
+ continue;
46
+ }
47
+ const output = functionMeta.outputs ? functionMeta.outputs[0] : undefined;
48
+ const path = route.replace(/:(\w+)/g, '{$1}');
49
+ if (!paths[path]) {
50
+ paths[path] = {};
51
+ }
52
+ const responses = {};
53
+ errors?.forEach((error) => {
54
+ const errorResponse = getErrorResponseForConstructorName(error, errorsMap);
55
+ if (errorResponse) {
56
+ responses[errorResponse.status] = {
57
+ description: errorResponse.message,
58
+ };
59
+ }
60
+ });
61
+ const operation = {
62
+ description: description ||
63
+ `This endpoint handles the ${method.toUpperCase()} request for the route ${route}.`,
64
+ tags: tags || [route.split('/')[1] || 'default'],
65
+ parameters: [],
66
+ responses: {
67
+ ...responses,
68
+ '200': {
69
+ description: 'Successful response',
70
+ content: output
71
+ ? {
72
+ 'application/json': {
73
+ schema: typeof output === 'string' &&
74
+ ['boolean', 'string', 'number'].includes(output)
75
+ ? { type: output }
76
+ : { $ref: `#/components/schemas/${output}` },
77
+ },
78
+ }
79
+ : undefined,
80
+ },
81
+ },
82
+ };
83
+ const bodyType = inputTypes?.body;
84
+ if (bodyType) {
85
+ operation.requestBody = {
86
+ required: true,
87
+ content: {
88
+ 'application/json': {
89
+ schema: typeof bodyType === 'string' &&
90
+ ['boolean', 'string', 'number'].includes(bodyType)
91
+ ? { type: bodyType }
92
+ : { $ref: `#/components/schemas/${bodyType}` },
93
+ },
94
+ },
95
+ };
96
+ }
97
+ if (params) {
98
+ operation.parameters = params.map((param) => ({
99
+ name: param,
100
+ in: 'path',
101
+ required: true,
102
+ schema: { type: 'string' },
103
+ }));
104
+ }
105
+ if (query) {
106
+ operation.parameters.push(...query.map((query) => ({
107
+ name: query,
108
+ in: 'query',
109
+ required: false,
110
+ schema: { type: 'string' },
111
+ })));
112
+ }
113
+ paths[path][method] = operation;
114
+ }
115
+ }
116
+ return {
117
+ openapi: '3.1.0',
118
+ info: additionalInfo.info,
119
+ servers: additionalInfo.servers,
120
+ paths,
121
+ components: {
122
+ schemas: await convertSchemasToBodyPayloads(functionsMeta, httpMeta, schemas),
123
+ responses: {},
124
+ parameters: {},
125
+ examples: {},
126
+ requestBodies: {},
127
+ headers: {},
128
+ securitySchemes: additionalInfo.securitySchemes || {
129
+ ApiKeyAuth: {
130
+ type: 'apiKey',
131
+ in: 'header',
132
+ name: 'x-api-key',
133
+ },
134
+ BearerAuth: {
135
+ type: 'http',
136
+ scheme: 'bearer',
137
+ },
138
+ },
139
+ },
140
+ security: additionalInfo.security || [
141
+ {
142
+ ApiKeyAuth: [],
143
+ },
144
+ {
145
+ BearerAuth: [],
146
+ },
147
+ ],
148
+ tags: additionalInfo.tags,
149
+ externalDocs: additionalInfo.externalDocs,
150
+ };
151
+ }
@@ -0,0 +1,6 @@
1
+ import type { InspectorState } from '../types.js';
2
+ export declare const serializePermissionsGroupsMeta: (state: InspectorState) => {
3
+ definitions: Record<string, import("../types.js").InspectorPermissionDefinition>;
4
+ httpGroups: Record<string, any>;
5
+ tagGroups: Record<string, any>;
6
+ };
@@ -0,0 +1,31 @@
1
+ export const serializePermissionsGroupsMeta = (state) => {
2
+ const httpGroups = {};
3
+ for (const [pattern, meta] of state.http.routePermissions.entries()) {
4
+ httpGroups[pattern] = {
5
+ exportName: meta.exportName,
6
+ sourceFile: meta.sourceFile,
7
+ position: meta.position,
8
+ services: meta.services,
9
+ count: meta.count,
10
+ instanceIds: meta.instanceIds,
11
+ isFactory: meta.isFactory,
12
+ };
13
+ }
14
+ const tagGroups = {};
15
+ for (const [tag, meta] of state.permissions.tagPermissions.entries()) {
16
+ tagGroups[tag] = {
17
+ exportName: meta.exportName,
18
+ sourceFile: meta.sourceFile,
19
+ position: meta.position,
20
+ services: meta.services,
21
+ count: meta.count,
22
+ instanceIds: meta.instanceIds,
23
+ isFactory: meta.isFactory,
24
+ };
25
+ }
26
+ return {
27
+ definitions: state.permissions.definitions,
28
+ httpGroups,
29
+ tagGroups,
30
+ };
31
+ };
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Deserialize workflow JSON back to DSL code
3
+ * Converts the serialized workflow graph format back to TypeScript DSL code
4
+ */
5
+ import type { SerializedWorkflowGraph } from '../graph/workflow-graph.types.js';
6
+ interface DeserializeOptions {
7
+ /** Import path for pikkuWorkflowFunc */
8
+ pikkuImportPath?: string;
9
+ /** Whether to include type annotations */
10
+ includeTypes?: boolean;
11
+ }
12
+ /**
13
+ * Deserialize a workflow graph to DSL code
14
+ */
15
+ export declare function deserializeDslWorkflow(workflow: SerializedWorkflowGraph, options?: DeserializeOptions): string;
16
+ /**
17
+ * Deserialize a graph workflow to pikkuWorkflowGraph code
18
+ */
19
+ export declare function deserializeGraphWorkflow(workflow: SerializedWorkflowGraph, options?: DeserializeOptions): string;
20
+ /**
21
+ * Deserialize all workflows from JSON to DSL code
22
+ */
23
+ export declare function deserializeAllDslWorkflows(workflows: Record<string, SerializedWorkflowGraph>, options?: DeserializeOptions): Record<string, string>;
24
+ export {};