haveapi-typescript-client 0.31.2 → 0.32.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.
- checksums.yaml +4 -4
- data/README.md +31 -0
- data/docs/documentation.md +16 -8
- data/haveapi-typescript-client.gemspec +1 -1
- data/lib/haveapi/typescript_client/documentation.rb +66 -32
- data/lib/haveapi/typescript_client/formatter.rb +57 -0
- data/lib/haveapi/typescript_client/generator.rb +6 -2
- data/lib/haveapi/typescript_client/prettier.json +1 -0
- data/lib/haveapi/typescript_client/version.rb +1 -1
- metadata +5 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f82e8d0c5f9a906a98f55b2969f7a00a9f69efaa7aea7586eccb19ca03022102
|
|
4
|
+
data.tar.gz: ad342219999ef8533db09fd6aba8ccd546102af69c907916226e91bd6dba4d61
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6656aa62ba57f2cae74bd8f64d9e9b528860384b43f8d03624b97c0024360d8f6e24facd256892ab6bd000788ecc17a2c15154c5d1b8f9ebcf0ad7153dff406f
|
|
7
|
+
data.tar.gz: 8fb7f096cc83859f4a4415ebf33d387247367f8f2c8b05e93d560f5c41f10c9470b6334cb7fb2dc72b7cd999691decd8bb2b134584cd9bd59896393cc2f6a557
|
data/README.md
CHANGED
|
@@ -38,6 +38,22 @@ using the prepared dependencies and runs without contacting the API.
|
|
|
38
38
|
Applications need a TypeScript compiler and the `haveapi-client` JavaScript
|
|
39
39
|
runtime. The supported runtime version is exactly `0.30.2`.
|
|
40
40
|
|
|
41
|
+
Both generation and `generate --check` require **Prettier 3.9.10** and Node.
|
|
42
|
+
The repository's default Nix shell supplies them. For an installed Ruby gem,
|
|
43
|
+
prepare the formatter in your application's tooling before generating:
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
npm install --save-dev --save-exact prettier@3.9.10
|
|
47
|
+
export PRETTIER="$PWD/node_modules/.bin/prettier"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`PRETTIER` is an executable path, not a shell command. Alternatively, put the
|
|
51
|
+
exact version's `prettier` executable on PATH. The Ruby gem does not install npm
|
|
52
|
+
dependencies. Generation checks the formatter version and never downloads or
|
|
53
|
+
installs tools. Fetch, help, library loading and documentation rendering do not
|
|
54
|
+
require Prettier. Running an already generated client needs neither Prettier nor
|
|
55
|
+
the Ruby generator.
|
|
56
|
+
|
|
41
57
|
## Fetch, save and generate
|
|
42
58
|
|
|
43
59
|
The example API at `https://example.test/api/v1.0/` has `Widget#Update` at
|
|
@@ -69,6 +85,11 @@ The explicit generation version must match the saved snapshot. `--check` reports
|
|
|
69
85
|
missing or stale output without changing it. Keep handwritten files outside
|
|
70
86
|
`src/generated`.
|
|
71
87
|
|
|
88
|
+
Generation formats only its temporary emitted source tree, using the packaged
|
|
89
|
+
`lib/haveapi/typescript_client/prettier.json` defaults. Application Prettier
|
|
90
|
+
configuration, EditorConfig and ignore files do not change generated output.
|
|
91
|
+
The pure emitter and documentation adapters remain formatter-free.
|
|
92
|
+
|
|
72
93
|
For Basic capture, set `WIDGET_PASSWORD` in your environment, then run:
|
|
73
94
|
|
|
74
95
|
```sh
|
|
@@ -190,10 +211,20 @@ Commit the whole generated tree and manifest together. Regenerate with the
|
|
|
190
211
|
recorded schema and HaveAPI revision, and keep that generator/client pair
|
|
191
212
|
together when reverting versions.
|
|
192
213
|
|
|
214
|
+
The format-1 manifest records the formatter name, exact version and configuration
|
|
215
|
+
SHA-256 alongside hashes of the final formatted files. Existing format-1 trees
|
|
216
|
+
without formatter provenance can be regenerated normally; `--check` reports
|
|
217
|
+
them as stale without writing. Missing tools, version mismatches and formatting
|
|
218
|
+
errors leave the destination unchanged. Identical regeneration preserves file
|
|
219
|
+
timestamps.
|
|
220
|
+
|
|
193
221
|
An interrupted write can leave mixed output. Restore the committed generated
|
|
194
222
|
tree before regenerating. If the first generation was interrupted before an
|
|
195
223
|
ownership manifest was written, preserve that partial directory and generate
|
|
196
224
|
into a fresh empty `src/generated`. Do not edit the manifest to bypass a refusal.
|
|
225
|
+
When rolling back, restore the matching generator revision, generated tree and
|
|
226
|
+
manifest together. An older generator's `--check` need not accept source
|
|
227
|
+
formatted by a newer generator.
|
|
197
228
|
|
|
198
229
|
The Ruby server can render examples using this generated interface; see
|
|
199
230
|
[generated documentation profiles](docs/documentation.md).
|
data/docs/documentation.md
CHANGED
|
@@ -69,10 +69,16 @@ input wire names map to generated input names. The DSL has no separate input
|
|
|
69
69
|
metadata carrier, so actions requiring input metadata produce a limitation.
|
|
70
70
|
Inputs preserve omission, null, false, zero and empty strings. Unsafe integer IDs,
|
|
71
71
|
invalid JSON and structured Custom query values cannot be rendered. Output keys
|
|
72
|
-
remain wire names
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
72
|
+
remain wire names. Simple examples print the supplied output fields directly;
|
|
73
|
+
thorough examples guard optional, null and unknown values before using them.
|
|
74
|
+
|
|
75
|
+
Simple action examples assume that initialization and authentication have
|
|
76
|
+
established `api`. They use `await` in an async context and show the exact call
|
|
77
|
+
and sample output. Rejection examples import the runtime's `AsyncError` class.
|
|
78
|
+
**With error handling** includes setup, error handling and the response receipt's
|
|
79
|
+
`data`, `meta`, `envelope` and `httpStatus`. Both levels use the same validation
|
|
80
|
+
and limitations. Rejection examples inspect the runtime's error receipt without
|
|
81
|
+
printing server diagnostics.
|
|
76
82
|
Requests are not retried. A successful request does not establish completion of
|
|
77
83
|
a server-side operation; raw receipts cannot be passed to `waitForCompletion`.
|
|
78
84
|
|
|
@@ -81,8 +87,9 @@ a server-side operation; raw receipts cannot be passed to `waitForCompletion`.
|
|
|
81
87
|
The generator provides `HaveAPI::TypeScriptClient::Documentation` through
|
|
82
88
|
`require 'haveapi/typescript_client/documentation'`. Its factories accept a full
|
|
83
89
|
description or saved snapshot. `resolve` returns a frozen canonical action;
|
|
84
|
-
`render` returns the server's code or limitation result.
|
|
85
|
-
|
|
90
|
+
`render` returns the server's code or limitation result. Its context must expose
|
|
91
|
+
`detail` as `:simple` or `:thorough`. Rendering performs no file, network,
|
|
92
|
+
generator or compiler work.
|
|
86
93
|
|
|
87
94
|
Rendering selects the current action, authentication provider and metadata
|
|
88
95
|
namespace before recursively normalizing their contents. It leaves unrelated
|
|
@@ -90,8 +97,9 @@ description descendants untouched and keeps authorization decisions local to
|
|
|
90
97
|
each render. Deterministic work checks grow unrelated subtrees while keeping
|
|
91
98
|
the selected inputs and rendered source fixed.
|
|
92
99
|
|
|
93
|
-
|
|
94
|
-
|
|
100
|
+
The compilation checks compose simple action fragments with separately rendered
|
|
101
|
+
setup and compile both detail levels against generated fixture modules with
|
|
102
|
+
TypeScript 6, NodeNext/ES2022, `exactOptionalPropertyTypes`,
|
|
95
103
|
`noUncheckedIndexedAccess` and `verbatimModuleSyntax`. They cover aliases, nested
|
|
96
104
|
paths, filtered fields, stale contracts, supported authentication and errors.
|
|
97
105
|
|
|
@@ -2,7 +2,7 @@ require_relative '../typescript_client'
|
|
|
2
2
|
require 'uri'
|
|
3
3
|
|
|
4
4
|
module HaveAPI::TypeScriptClient
|
|
5
|
-
# Names belong to the complete
|
|
5
|
+
# Names belong to the complete API client; request-local help only limits their use.
|
|
6
6
|
class Documentation
|
|
7
7
|
class Unsupported < StandardError; end
|
|
8
8
|
|
|
@@ -49,14 +49,20 @@ module HaveAPI::TypeScriptClient
|
|
|
49
49
|
unsupported! unless mode == :async && context.version.to_s == @snapshot.fetch('api_version')
|
|
50
50
|
source = case kind
|
|
51
51
|
when :init then program(context, ['void api;'])
|
|
52
|
-
when :auth then program(context, ['void api;'], authentication(context, auth_method, auth_description))
|
|
52
|
+
when :auth then program(context, ['void api;'], authentication(context, auth_method, auth_description), errors: context.detail == :thorough)
|
|
53
53
|
when :example then example(context)
|
|
54
54
|
else unsupported!
|
|
55
55
|
end
|
|
56
56
|
{ status: :code, code: source.freeze }.freeze
|
|
57
57
|
rescue Unsupported, JSON::GeneratorError, EncodingError
|
|
58
|
-
# Never name fields or actions from the private
|
|
59
|
-
{ status: :limitation, reason: 'This sample is unavailable or incompatible with the configured TypeScript
|
|
58
|
+
# Never name fields or actions from the private API client in public diagnostics.
|
|
59
|
+
{ status: :limitation, reason: 'This sample is unavailable or incompatible with the configured TypeScript API client.'.freeze }.freeze
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def detail_levels_for(kind:, context:, result:, mode:, auth_method: nil, auth_description: nil)
|
|
63
|
+
return [:simple].freeze unless result[:status] == :code && %i[auth example].include?(kind) && mode == :async
|
|
64
|
+
|
|
65
|
+
%i[simple thorough].freeze
|
|
60
66
|
end
|
|
61
67
|
|
|
62
68
|
private
|
|
@@ -103,28 +109,42 @@ module HaveAPI::TypeScriptClient
|
|
|
103
109
|
end
|
|
104
110
|
|
|
105
111
|
def literal(value)
|
|
106
|
-
|
|
107
|
-
|
|
112
|
+
if value.is_a?(Hash)
|
|
113
|
+
return '{}' if value.empty?
|
|
114
|
+
|
|
115
|
+
return "{ #{value.map { |key, item| "#{key == '__proto__' ? '["__proto__"]' : literal(key)}: #{literal(item)}" }.join(', ')} }"
|
|
116
|
+
end
|
|
117
|
+
return "[#{value.map { |item| literal(item) }.join(', ')}]" if value.is_a?(Array)
|
|
108
118
|
|
|
109
119
|
JSON.generate(value).gsub("\u2028", '\\u2028').gsub("\u2029", '\\u2029')
|
|
110
120
|
end
|
|
111
121
|
|
|
112
|
-
def
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
+
def member_access(name)
|
|
123
|
+
name.match?(/\A[$_\p{ID_Start}][$\p{ID_Continue}\u200c\u200d]*\z/u) ? ".#{name}" : "[#{literal(name)}]"
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def program(context, body, auth = [], errors: false)
|
|
127
|
+
version_url = version_url(context)
|
|
128
|
+
dependencies = "// Configured API client: #{@package_name}#{"@#{@package_version}" if @package_version}."
|
|
129
|
+
runtime_imports = errors ? 'AsyncClient, AsyncError' : 'AsyncClient'
|
|
130
|
+
lines = [dependencies, "import { #{runtime_imports} } from #{literal('haveapi-client')};", "import { createClient } from #{literal(@package_name)};"]
|
|
131
|
+
lines += ['', 'async function main(): Promise<void> {']
|
|
132
|
+
operation = ["const runtime = new AsyncClient(#{literal(version_url)});"] + auth + ['const api = createClient({ execute: spec => runtime.request(spec) });'] + body
|
|
133
|
+
if errors
|
|
134
|
+
operation = ['try {'] + operation.map { |line| " #{line}" } +
|
|
135
|
+
['} catch (error) {', ' if (!(error instanceof AsyncError)) throw error;',
|
|
136
|
+
' console.error(error.kind, error.httpStatus);', ' throw error;', '}']
|
|
122
137
|
end
|
|
123
|
-
lines
|
|
124
|
-
lines.concat(auth.map { |line| " #{line}" })
|
|
125
|
-
lines << ' const api = createClient({execute: spec => runtime.request(spec)});'
|
|
126
|
-
lines.concat(body.map { |line| " #{line}" })
|
|
138
|
+
lines.concat(operation.map { |line| " #{line}" })
|
|
127
139
|
lines.push('}', '', 'main().catch(error => { throw error; });', '').join("\n")
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def version_url(context)
|
|
143
|
+
value = context.version_url.to_s.sub(%r{/+\z}, '')
|
|
144
|
+
uri = URI.parse(value)
|
|
145
|
+
unsupported! unless uri.is_a?(URI::HTTP) && uri.host && !uri.userinfo && !uri.query && !uri.fragment &&
|
|
146
|
+
uri.path.end_with?("/v#{@snapshot.fetch('api_version')}") && Snapshot.safe_path?(uri.path)
|
|
147
|
+
value
|
|
128
148
|
rescue URI::InvalidURIError
|
|
129
149
|
unsupported!
|
|
130
150
|
end
|
|
@@ -139,11 +159,11 @@ module HaveAPI::TypeScriptClient
|
|
|
139
159
|
unsupported! if description && normalize(description) != live
|
|
140
160
|
case method.to_s
|
|
141
161
|
when 'basic'
|
|
142
|
-
['await runtime.authenticate(
|
|
162
|
+
['await runtime.authenticate("basic", { user: "user", password: "secret" });']
|
|
143
163
|
when 'token'
|
|
144
164
|
header = live['http_header']
|
|
145
165
|
unsupported! unless header.is_a?(String) && header.match?(/\A[!#$%&'*+.^_`|~0-9A-Za-z-]+\z/) && stored['http_header'] == header
|
|
146
|
-
['await runtime.authenticate(
|
|
166
|
+
['await runtime.authenticate("token", { token: "example-token" });']
|
|
147
167
|
else unsupported!
|
|
148
168
|
end
|
|
149
169
|
end
|
|
@@ -186,7 +206,7 @@ module HaveAPI::TypeScriptClient
|
|
|
186
206
|
|
|
187
207
|
def compatible_action!(action, saved, live, context)
|
|
188
208
|
unsupported! unless live.is_a?(Hash)
|
|
189
|
-
expected_path = URI.parse(context
|
|
209
|
+
expected_path = URI.parse(version_url(context)).path + action.fetch(:path_template)
|
|
190
210
|
unsupported! unless live['path'] == expected_path && live['method'] == action.fetch(:method) &&
|
|
191
211
|
(live['auth'] != false) == action.fetch(:auth) && (live['blocking'] == true) == action.fetch(:blocking)
|
|
192
212
|
namespace = normalized_members(normalized_members(context.description)['meta'])['namespace']
|
|
@@ -292,32 +312,46 @@ module HaveAPI::TypeScriptClient
|
|
|
292
312
|
validate_object!(saved['output'], current['output'], value, input: false)
|
|
293
313
|
end
|
|
294
314
|
end
|
|
295
|
-
members = (@accessors.fetch(action.fetch(:schema_path)) + [action.fetch(:accessor_name)]).map { |name|
|
|
315
|
+
members = (@accessors.fetch(action.fetch(:schema_path)) + [action.fetch(:accessor_name)]).map { |name| member_access(name) }.join
|
|
296
316
|
call = "api#{members}"
|
|
297
317
|
call += "(#{literal(options) unless options.empty?})"
|
|
298
318
|
if sample['status'] == false || (sample['http_status'].is_a?(Integer) && sample['http_status'] >= 400)
|
|
299
319
|
body = ['try {', " await #{call};", ' throw new Error("Expected the documented request to fail");',
|
|
300
320
|
'} catch (error) {', ' if (!(error instanceof AsyncError)) throw error;',
|
|
301
|
-
'
|
|
321
|
+
' console.error(error.kind, error.httpStatus);']
|
|
322
|
+
body << ' void [error.errors, error.envelope, error.response];' if context.detail == :thorough
|
|
323
|
+
body << '}'
|
|
324
|
+
failure = true
|
|
302
325
|
else
|
|
303
|
-
body = ["const result = await #{call};"
|
|
304
|
-
body.
|
|
326
|
+
body = ["const result = await #{call};"]
|
|
327
|
+
body << 'void [result.data, result.meta, result.envelope, result.httpStatus];' if context.detail == :thorough
|
|
328
|
+
body.concat(output_lines(action, response, thorough: context.detail == :thorough))
|
|
305
329
|
end
|
|
306
330
|
body << '// Request success does not establish completion of a server-side operation.' if action.fetch(:blocking)
|
|
307
|
-
|
|
331
|
+
auth = action_authentication(action, context)
|
|
332
|
+
if context.detail == :thorough
|
|
333
|
+
return program(context, body, auth, errors: true)
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
imports = failure ? ["import { AsyncError } from #{literal('haveapi-client')};", ''] : []
|
|
337
|
+
(imports + ['// Requires an initialized api and an async context.'] + body + ['']).join("\n")
|
|
308
338
|
end
|
|
309
339
|
|
|
310
|
-
def output_lines(action, response)
|
|
340
|
+
def output_lines(action, response, thorough:)
|
|
311
341
|
return [] unless response && action[:output]
|
|
312
342
|
|
|
313
343
|
list = action[:output][:layout].end_with?('_list')
|
|
314
344
|
values = list ? response : [response]
|
|
315
345
|
names = values.flat_map(&:keys).uniq
|
|
316
346
|
body = names.flat_map do |name|
|
|
317
|
-
expression = "item
|
|
318
|
-
["
|
|
347
|
+
expression = "#{list || thorough ? 'item' : 'result.data'}#{member_access(name)}"
|
|
348
|
+
next ["console.log(#{expression});"] unless thorough
|
|
349
|
+
|
|
350
|
+
["const value#{names.index(name)}: unknown = #{expression};", "if (value#{names.index(name)} !== null && value#{names.index(name)} !== undefined) console.log(value#{names.index(name)});"]
|
|
319
351
|
end
|
|
320
|
-
|
|
352
|
+
return ['for (const item of result.data) {'] + body.map { |line| " #{line}" } + ['}'] if list
|
|
353
|
+
|
|
354
|
+
(thorough ? ['const item = result.data;'] : []) + body
|
|
321
355
|
end
|
|
322
356
|
end
|
|
323
357
|
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
require 'digest'
|
|
2
|
+
require 'fileutils'
|
|
3
|
+
require 'open3'
|
|
4
|
+
require 'tmpdir'
|
|
5
|
+
|
|
6
|
+
module HaveAPI::TypeScriptClient
|
|
7
|
+
# Format only the validated emission tree, before the generator hashes it.
|
|
8
|
+
class Formatter
|
|
9
|
+
VERSION = '3.9.10'.freeze
|
|
10
|
+
CONFIG = File.expand_path('prettier.json', __dir__).freeze
|
|
11
|
+
|
|
12
|
+
def initialize
|
|
13
|
+
@executable = ENV.fetch('PRETTIER', 'prettier')
|
|
14
|
+
@executable = File.expand_path(@executable) if @executable.include?(File::SEPARATOR)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def version
|
|
18
|
+
actual = capture('--version').strip
|
|
19
|
+
raise Error, "Prettier #{VERSION} is required; found #{actual.inspect}" unless actual == VERSION
|
|
20
|
+
|
|
21
|
+
actual
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def provenance
|
|
25
|
+
{ 'name' => 'prettier', 'version' => VERSION, 'config_sha256' => Digest::SHA256.file(CONFIG).hexdigest }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def format(files)
|
|
29
|
+
version
|
|
30
|
+
Dir.mktmpdir('haveapi-typescript-format-') do |directory|
|
|
31
|
+
files.each do |path, content|
|
|
32
|
+
target = File.join(directory, path)
|
|
33
|
+
FileUtils.mkdir_p(File.dirname(target))
|
|
34
|
+
File.write(target, content)
|
|
35
|
+
end
|
|
36
|
+
capture('--config', CONFIG, '--no-editorconfig', '--ignore-path', File::NULL,
|
|
37
|
+
'--parser', 'typescript', '--write', '**/*.ts', chdir: directory)
|
|
38
|
+
files.to_h do |path, _content|
|
|
39
|
+
[path, File.read(File.join(directory, path), encoding: 'UTF-8')]
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
rescue SystemCallError => e
|
|
43
|
+
raise Error, "Prettier formatting failed: #{e.message}"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def capture(*, **)
|
|
49
|
+
stdout, stderr, status = Open3.capture3(@executable, *, **)
|
|
50
|
+
raise Error, "Prettier failed: #{stderr.strip}" unless status.success?
|
|
51
|
+
|
|
52
|
+
stdout
|
|
53
|
+
rescue SystemCallError => e
|
|
54
|
+
raise Error, "Unable to run Prettier #{@executable.inspect}: #{e.message}"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -11,7 +11,7 @@ module HaveAPI::TypeScriptClient
|
|
|
11
11
|
|
|
12
12
|
def self.source_files
|
|
13
13
|
Dir.chdir(ROOT) do
|
|
14
|
-
Dir['bin/**/*', 'lib/**/*.rb', 'template/**/*', 'Gemfile', '*.gemspec', 'Rakefile']
|
|
14
|
+
Dir['bin/**/*', 'lib/**/*.{rb,json}', 'template/**/*', 'Gemfile', '*.gemspec', 'Rakefile']
|
|
15
15
|
.select { |path| File.file?(path) }.sort
|
|
16
16
|
end
|
|
17
17
|
end
|
|
@@ -41,12 +41,16 @@ module HaveAPI::TypeScriptClient
|
|
|
41
41
|
require_relative 'emitter'
|
|
42
42
|
files = Emitter.new(@ir, @config).files
|
|
43
43
|
validate_files(files)
|
|
44
|
+
require_relative 'formatter'
|
|
45
|
+
formatter = Formatter.new
|
|
46
|
+
files = formatter.format(files)
|
|
47
|
+
validate_files(files)
|
|
44
48
|
manifest = {
|
|
45
49
|
'owner' => OWNER, 'format' => 1, 'generator_version' => VERSION,
|
|
46
50
|
'generator_source_sha256' => self.class.source_digest,
|
|
47
51
|
'schema_sha256' => Snapshot.sha256(@schema), 'api_version' => @schema.fetch('api_version'),
|
|
48
52
|
'protocol_version' => @schema.fetch('protocol_version'), 'profile' => @schema.fetch('profile'),
|
|
49
|
-
'config' => @config.transform_keys(&:to_s), 'coverage' => coverage,
|
|
53
|
+
'config' => @config.transform_keys(&:to_s), 'formatter' => formatter.provenance, 'coverage' => coverage,
|
|
50
54
|
'files' => files.sort.to_h.transform_values { |content| Digest::SHA256.hexdigest(content) }
|
|
51
55
|
}
|
|
52
56
|
candidate = files.merge(OWNER_FILE => Snapshot.canonical(manifest))
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: haveapi-typescript-client
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.32.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jakub Skokan
|
|
@@ -15,14 +15,14 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - "~>"
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: 0.
|
|
18
|
+
version: 0.32.0
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: 0.
|
|
25
|
+
version: 0.32.0
|
|
26
26
|
executables:
|
|
27
27
|
- haveapi-typescript-client
|
|
28
28
|
extensions: []
|
|
@@ -39,9 +39,11 @@ files:
|
|
|
39
39
|
- lib/haveapi/typescript_client/cli.rb
|
|
40
40
|
- lib/haveapi/typescript_client/documentation.rb
|
|
41
41
|
- lib/haveapi/typescript_client/emitter.rb
|
|
42
|
+
- lib/haveapi/typescript_client/formatter.rb
|
|
42
43
|
- lib/haveapi/typescript_client/generator.rb
|
|
43
44
|
- lib/haveapi/typescript_client/ir.rb
|
|
44
45
|
- lib/haveapi/typescript_client/naming.rb
|
|
46
|
+
- lib/haveapi/typescript_client/prettier.json
|
|
45
47
|
- lib/haveapi/typescript_client/snapshot.rb
|
|
46
48
|
- lib/haveapi/typescript_client/version.rb
|
|
47
49
|
- template/action.ts.erb
|