massimo-cli 1.3.0 → 1.5.1

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.
@@ -68,6 +68,7 @@ function generateFrontendImplementationFromOpenAPI ({
68
68
  useTabs: false,
69
69
  useSingleQuote: true
70
70
  })
71
+ const hasPathParameters = operations.some(operation => operation.path.includes('{'))
71
72
 
72
73
  writer.write('// This client was generated by Platformatic from an OpenAPI specification.')
73
74
  writer.blankLine()
@@ -132,6 +133,18 @@ function generateFrontendImplementationFromOpenAPI ({
132
133
  writer.writeLine('return output')
133
134
  })
134
135
  }
136
+ if (hasPathParameters) {
137
+ writer.newLine()
138
+ writer
139
+ .write(`function encodePathParameter(value${isTsLang ? ': unknown' : ''})${isTsLang ? ': string' : ''} `)
140
+ .block(() => {
141
+ writer.writeLine(`const encoded = encodeURIComponent(value${isTsLang ? ' as string' : ''})`)
142
+ writer.write('if (encoded === \'.\' || encoded === \'..\') ').block(() => {
143
+ writer.writeLine('throw new Error(\'Path parameters cannot be "." or ".."\')')
144
+ })
145
+ writer.writeLine('return encoded')
146
+ })
147
+ }
135
148
  writer.blankLine()
136
149
  const allOperations = []
137
150
  const originalFullResponse = fullResponse
@@ -192,7 +205,9 @@ function generateFrontendImplementationFromOpenAPI ({
192
205
  // /organizations/{orgId}/members/{memberId}
193
206
  // to
194
207
  // /organizations/${request.orgId}/members/${request.memberId}
195
- const stringLiteralPath = path.replace(/\{/gm, '${' + req + "['").replace(/\}/gm, "']}")
208
+ const stringLiteralPath = path
209
+ .replace(/\{/gm, '${encodePathParameter(' + req + "['")
210
+ .replace(/\}/gm, "'])}")
196
211
  // GET methods need query strings instead of JSON bodies
197
212
  if (queryParams.length) {
198
213
  // query parameters should be appended to the url
@@ -446,6 +461,12 @@ function generateTypesFromOpenAPI ({ schema, name, fullRequest, fullResponse, pr
446
461
  })
447
462
  })
448
463
 
464
+ // Declare the module-level named exports emitted by the implementation file,
465
+ // so consumers relying on the .d.ts can `import { setBaseUrl, ... }` without type errors.
466
+ writer.writeLine(`export declare const setBaseUrl: ${camelCaseName}['setBaseUrl'];`)
467
+ writer.writeLine(`export declare const setDefaultHeaders: ${camelCaseName}['setDefaultHeaders'];`)
468
+ writer.writeLine(`export declare const setDefaultFetchParams: ${camelCaseName}['setDefaultFetchParams'];`)
469
+
449
470
  writer.writeLine(`type PlatformaticFrontendClient = Omit<${camelCaseName}, 'setBaseUrl'>`)
450
471
  writer.write('type BuildOptions = ').block(() => {
451
472
  writer.writeLine('headers?: object')
package/lib/get-type.js CHANGED
@@ -1,16 +1,21 @@
1
1
  import jsonpointer from 'jsonpointer'
2
2
 
3
- export function getType (typeDef, methodType, spec) {
3
+ export function getType (typeDef, methodType, spec, seenRefs = new Set()) {
4
4
  if (typeDef.$ref) {
5
+ if (seenRefs.has(typeDef.$ref)) {
6
+ return 'unknown'
7
+ }
8
+ seenRefs = new Set(seenRefs)
9
+ seenRefs.add(typeDef.$ref)
5
10
  typeDef = jsonpointer.get(spec, typeDef.$ref.replace('#', ''))
6
11
  }
7
12
  if (typeDef.schema) {
8
- return getType(typeDef.schema, methodType, spec)
13
+ return getType(typeDef.schema, methodType, spec, seenRefs)
9
14
  }
10
15
  if (typeDef.anyOf) {
11
16
  // recursively call this function
12
17
  const mapped = typeDef.anyOf.map(t => {
13
- return getType(t, methodType, spec)
18
+ return getType(t, methodType, spec, seenRefs)
14
19
  })
15
20
  return mapped.join(' | ')
16
21
  }
@@ -18,7 +23,7 @@ export function getType (typeDef, methodType, spec) {
18
23
  if (typeDef.oneOf) {
19
24
  // recursively call this function
20
25
  const mapped = typeDef.oneOf.map(t => {
21
- return getType(t, methodType, spec)
26
+ return getType(t, methodType, spec, seenRefs)
22
27
  })
23
28
 
24
29
  if (typeDef.discriminator && typeDef.discriminator.propertyName) {
@@ -56,13 +61,13 @@ export function getType (typeDef, methodType, spec) {
56
61
  // recursively call this function
57
62
  return typeDef.allOf
58
63
  .map(t => {
59
- return getType(t, methodType, spec)
64
+ return getType(t, methodType, spec, seenRefs)
60
65
  })
61
66
  .join(' & ')
62
67
  }
63
68
  if (typeDef.type === 'array') {
64
69
  const nullable = typeDef.nullable
65
- return `Array<${getType(typeDef.items, methodType, spec)}>${nullable === true ? ' | null' : ''}`
70
+ return `Array<${getType(typeDef.items, methodType, spec, seenRefs)}>${nullable === true ? ' | null' : ''}`
66
71
  }
67
72
  if (typeDef.enum) {
68
73
  // Note: null type represented with an enum have no types and single enum element 'null'
@@ -73,11 +78,10 @@ export function getType (typeDef, methodType, spec) {
73
78
  const nullable = typeDef.nullable
74
79
  const chainedTypes = typeDef.enum
75
80
  .map(en => {
76
- if (typeDef.type === 'string') {
77
- return `'${en.replace(/'/g, "\\'")}'`
78
- } else {
79
- return en
80
- }
81
+ // Quote by the runtime type of the value: the schema may omit `type`
82
+ if (en === null) return 'null'
83
+ if (typeof en === 'string') return `'${en.replace(/'/g, "\\'")}'`
84
+ return en
81
85
  })
82
86
  .join(' | ')
83
87
  return nullable === true ? `${chainedTypes} | null` : chainedTypes
@@ -108,7 +112,7 @@ export function getType (typeDef, methodType, spec) {
108
112
  if (additionalPropsRequired) {
109
113
  required = required || !!additionalPropsRequired.includes(prop)
110
114
  }
111
- return `'${prop}'${required ? '' : '?'}: ${getType(objProperties[prop], methodType, spec)}`
115
+ return `'${prop}'${required ? '' : '?'}: ${getType(objProperties[prop], methodType, spec, seenRefs)}`
112
116
  })
113
117
  if (additionalProps === true) {
114
118
  props.push('[key: string]: unknown')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "massimo-cli",
3
- "version": "1.3.0",
3
+ "version": "1.5.1",
4
4
  "description": "A client for HTTP services.",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -25,21 +25,21 @@
25
25
  "graphql": "^16.8.1",
26
26
  "help-me": "^5.0.0",
27
27
  "jsonpointer": "^5.0.1",
28
+ "massimo": "1.5.1",
28
29
  "minimist": "^1.2.8",
29
30
  "pino": "^9.9.0",
30
31
  "pino-pretty": "^13.0.0",
31
32
  "undici": "^7.0.0",
32
- "yaml": "^2.4.1",
33
- "massimo": "1.3.0"
33
+ "yaml": "^2.4.1"
34
34
  },
35
35
  "devDependencies": {
36
- "@platformatic/composer": "3.54.0",
37
- "@platformatic/db": "3.0.0-alpha.6",
38
- "@platformatic/foundation": "3.54.0",
39
- "@platformatic/runtime": "3.52.4",
40
- "@platformatic/service": "3.0.0-alpha.6",
41
- "@platformatic/sql-graphql": "3.53.0",
42
- "@platformatic/sql-mapper": "3.52.4",
36
+ "@platformatic/composer": "3.68.0",
37
+ "@platformatic/db": "3.67.0",
38
+ "@platformatic/foundation": "3.68.0",
39
+ "@platformatic/runtime": "3.68.0",
40
+ "@platformatic/service": "3.67.0",
41
+ "@platformatic/sql-graphql": "3.68.0",
42
+ "@platformatic/sql-mapper": "3.68.0",
43
43
  "@playwright/test": "^1.42.1",
44
44
  "@types/react": "^19.1.6",
45
45
  "@types/react-dom": "^19.1.5",
@@ -48,18 +48,18 @@
48
48
  "dotenv": "^16.4.5",
49
49
  "eslint": "9",
50
50
  "execa": "^9.0.0",
51
- "fastify": "^5.0.0",
51
+ "fastify": "^5.12.0",
52
52
  "fastify-tsconfig": "^3.0.0",
53
53
  "fs-extra": "^11.2.0",
54
- "neostandard": "^0.12.0",
54
+ "neostandard": "^0.13.0",
55
55
  "react": "^19.1.0",
56
56
  "react-dom": "^19.1.0",
57
57
  "scheduler": "^0.27.0",
58
58
  "split2": "^4.2.0",
59
- "tsd": "^0.32.0",
59
+ "tsd": "^0.33.0",
60
60
  "typescript": "^5.5.4",
61
61
  "vite": "^5.1.6",
62
- "wattpm": "3.0.0-alpha.5",
62
+ "wattpm": "3.67.0",
63
63
  "why-is-node-running": "^2.2.2"
64
64
  },
65
65
  "engines": {