@nyc-transit-kit/cli 0.1.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.
- package/LICENSE +21 -0
- package/README.md +5 -0
- package/bin/ntk +3 -0
- package/bin/nyc-transit +3 -0
- package/package.json +58 -0
- package/scripts/build-binary.ts +113 -0
- package/scripts/build.ts +1 -0
- package/src/build-info.ts +2 -0
- package/src/commands/catalog.ts +59 -0
- package/src/commands/mta.ts +610 -0
- package/src/commands/nyc-dot.ts +212 -0
- package/src/commands/nyc-open-data.ts +243 -0
- package/src/commands/root.ts +38 -0
- package/src/commands/shared.ts +144 -0
- package/src/commands/socrata.ts +206 -0
- package/src/commands/soda3-shared.ts +103 -0
- package/src/commands/types.ts +33 -0
- package/src/config.ts +21 -0
- package/src/files.ts +27 -0
- package/src/index.ts +2 -0
- package/src/main.ts +205 -0
- package/src/output.ts +39 -0
- package/src/runtime.ts +14 -0
|
@@ -0,0 +1,610 @@
|
|
|
1
|
+
import { type GtfsFeedKind, MtaGtfsRealtimeCaptureManifest } from "@nyc-transit-kit/contracts/mta"
|
|
2
|
+
import {
|
|
3
|
+
findMtaOpenDataDataset,
|
|
4
|
+
mtaOpenDataDatasets,
|
|
5
|
+
mtaOpenDataDomain
|
|
6
|
+
} from "@nyc-transit-kit/mta/datasets"
|
|
7
|
+
import {
|
|
8
|
+
findMtaGtfsRealtimeFeed,
|
|
9
|
+
findMtaGtfsStaticFeed,
|
|
10
|
+
type MtaGtfsRealtimeFeedDescriptor,
|
|
11
|
+
type MtaGtfsStaticFeedDescriptor,
|
|
12
|
+
mtaGtfsRealtimeFeeds,
|
|
13
|
+
mtaGtfsStaticFeeds
|
|
14
|
+
} from "@nyc-transit-kit/mta/feeds"
|
|
15
|
+
import { captureGtfsRealtime, probeGtfsRealtime } from "@nyc-transit-kit/mta/gtfs-realtime"
|
|
16
|
+
import { fetchGtfsStaticResponse, probeGtfsStatic } from "@nyc-transit-kit/mta/gtfs-static"
|
|
17
|
+
import { queryMtaOpenData } from "@nyc-transit-kit/mta/open-data"
|
|
18
|
+
import * as Effect from "effect/Effect"
|
|
19
|
+
import * as Option from "effect/Option"
|
|
20
|
+
import * as Command from "effect/unstable/cli/Command"
|
|
21
|
+
import * as Flag from "effect/unstable/cli/Flag"
|
|
22
|
+
import { atomicWrite } from "../files"
|
|
23
|
+
import { failCommand, missingOption, runEffect, soqlFromSelect, writeSuccess } from "./shared"
|
|
24
|
+
import { fileWriteError, writeResponseToFile, writeSoda3QueryDryRun } from "./soda3-shared"
|
|
25
|
+
import { CliCommandContext, type CommandContext } from "./types"
|
|
26
|
+
|
|
27
|
+
export const mtaCommandPaths: ReadonlyArray<ReadonlyArray<string>> = [
|
|
28
|
+
["mta", "open-data", "dataset", "list"],
|
|
29
|
+
["mta", "open-data", "dataset", "info"],
|
|
30
|
+
["mta", "open-data", "dataset", "query"],
|
|
31
|
+
["mta", "gtfs-static", "list"],
|
|
32
|
+
["mta", "gtfs-static", "probe"],
|
|
33
|
+
["mta", "gtfs-static", "fetch"],
|
|
34
|
+
["mta", "gtfs-rt", "list"],
|
|
35
|
+
["mta", "gtfs-rt", "probe"],
|
|
36
|
+
["mta", "gtfs-rt", "decode"],
|
|
37
|
+
["mta", "gtfs-rt", "capture"]
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
type StaticFeedTarget =
|
|
41
|
+
| {
|
|
42
|
+
readonly _tag: "Resolved"
|
|
43
|
+
readonly url: string
|
|
44
|
+
readonly source: "descriptor" | "url"
|
|
45
|
+
readonly descriptor?: MtaGtfsStaticFeedDescriptor
|
|
46
|
+
}
|
|
47
|
+
| {
|
|
48
|
+
readonly _tag: "Missing"
|
|
49
|
+
}
|
|
50
|
+
| {
|
|
51
|
+
readonly _tag: "Unsupported"
|
|
52
|
+
readonly feed: string
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
type RealtimeFeedTarget =
|
|
56
|
+
| {
|
|
57
|
+
readonly _tag: "Resolved"
|
|
58
|
+
readonly feed: GtfsFeedKind
|
|
59
|
+
readonly url: string
|
|
60
|
+
readonly source: "descriptor" | "url"
|
|
61
|
+
readonly descriptor?: MtaGtfsRealtimeFeedDescriptor
|
|
62
|
+
}
|
|
63
|
+
| {
|
|
64
|
+
readonly _tag: "MissingUrl"
|
|
65
|
+
readonly feed: GtfsFeedKind
|
|
66
|
+
}
|
|
67
|
+
| {
|
|
68
|
+
readonly _tag: "Unsupported"
|
|
69
|
+
readonly feed: string
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const gtfsFeedKinds = new Set(["vehicle-positions", "trip-updates", "alerts"])
|
|
73
|
+
|
|
74
|
+
const optionalString = (value: Option.Option<string>) => Option.getOrUndefined(value)
|
|
75
|
+
|
|
76
|
+
const isGtfsFeedKind = (value: string): value is GtfsFeedKind => gtfsFeedKinds.has(value)
|
|
77
|
+
|
|
78
|
+
const resolveStaticFeedTarget = (
|
|
79
|
+
feed: Option.Option<string>,
|
|
80
|
+
url: Option.Option<string>
|
|
81
|
+
): StaticFeedTarget => {
|
|
82
|
+
const feedKey = optionalString(feed)
|
|
83
|
+
const urlOverride = optionalString(url)
|
|
84
|
+
const descriptor = feedKey === undefined ? undefined : findMtaGtfsStaticFeed(feedKey)
|
|
85
|
+
|
|
86
|
+
if (urlOverride !== undefined) {
|
|
87
|
+
return {
|
|
88
|
+
_tag: "Resolved",
|
|
89
|
+
url: urlOverride,
|
|
90
|
+
source: "url",
|
|
91
|
+
...(descriptor === undefined ? {} : { descriptor })
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (feedKey === undefined) {
|
|
96
|
+
return { _tag: "Missing" }
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (descriptor === undefined) {
|
|
100
|
+
return {
|
|
101
|
+
_tag: "Unsupported",
|
|
102
|
+
feed: feedKey
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
_tag: "Resolved",
|
|
108
|
+
url: descriptor.url,
|
|
109
|
+
source: "descriptor",
|
|
110
|
+
descriptor
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const resolveRealtimeFeedTarget = (
|
|
115
|
+
feedKey: string,
|
|
116
|
+
url: Option.Option<string>
|
|
117
|
+
): RealtimeFeedTarget => {
|
|
118
|
+
const urlOverride = optionalString(url)
|
|
119
|
+
const descriptor = findMtaGtfsRealtimeFeed(feedKey)
|
|
120
|
+
|
|
121
|
+
if (descriptor !== undefined) {
|
|
122
|
+
return {
|
|
123
|
+
_tag: "Resolved",
|
|
124
|
+
feed: descriptor.feed,
|
|
125
|
+
url: urlOverride ?? descriptor.url,
|
|
126
|
+
source: urlOverride === undefined ? "descriptor" : "url",
|
|
127
|
+
descriptor
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (isGtfsFeedKind(feedKey)) {
|
|
132
|
+
if (urlOverride === undefined) {
|
|
133
|
+
return {
|
|
134
|
+
_tag: "MissingUrl",
|
|
135
|
+
feed: feedKey
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
_tag: "Resolved",
|
|
141
|
+
feed: feedKey,
|
|
142
|
+
url: urlOverride,
|
|
143
|
+
source: "url"
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
_tag: "Unsupported",
|
|
149
|
+
feed: feedKey
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const failStaticTarget = (
|
|
154
|
+
context: CommandContext,
|
|
155
|
+
tokens: ReadonlyArray<string>,
|
|
156
|
+
target: StaticFeedTarget
|
|
157
|
+
) => {
|
|
158
|
+
switch (target._tag) {
|
|
159
|
+
case "Missing":
|
|
160
|
+
return missingOption(context, tokens, "feed")
|
|
161
|
+
case "Unsupported":
|
|
162
|
+
return failCommand(
|
|
163
|
+
context,
|
|
164
|
+
"mta",
|
|
165
|
+
"unsupported-feed",
|
|
166
|
+
`Unsupported MTA GTFS static feed: ${target.feed}`,
|
|
167
|
+
tokens
|
|
168
|
+
)
|
|
169
|
+
case "Resolved":
|
|
170
|
+
return Effect.void
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const failRealtimeTarget = (
|
|
175
|
+
context: CommandContext,
|
|
176
|
+
tokens: ReadonlyArray<string>,
|
|
177
|
+
target: RealtimeFeedTarget
|
|
178
|
+
) => {
|
|
179
|
+
switch (target._tag) {
|
|
180
|
+
case "MissingUrl":
|
|
181
|
+
return missingOption(context, tokens, "url")
|
|
182
|
+
case "Unsupported":
|
|
183
|
+
return failCommand(
|
|
184
|
+
context,
|
|
185
|
+
"mta",
|
|
186
|
+
"unsupported-feed",
|
|
187
|
+
`Unsupported MTA GTFS realtime feed: ${target.feed}`,
|
|
188
|
+
tokens
|
|
189
|
+
)
|
|
190
|
+
case "Resolved":
|
|
191
|
+
return Effect.void
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export const handleMtaOpenDataList = () =>
|
|
196
|
+
Effect.gen(function* () {
|
|
197
|
+
const context = yield* CliCommandContext
|
|
198
|
+
return yield* writeSuccess(context, "mta", {
|
|
199
|
+
datasets: mtaOpenDataDatasets
|
|
200
|
+
})
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
export const handleMtaOpenDataInfo = (config: { readonly datasetId: string }) =>
|
|
204
|
+
Effect.gen(function* () {
|
|
205
|
+
const context = yield* CliCommandContext
|
|
206
|
+
const descriptor = findMtaOpenDataDataset(config.datasetId)
|
|
207
|
+
|
|
208
|
+
return yield* writeSuccess(context, "mta", {
|
|
209
|
+
known: descriptor !== undefined,
|
|
210
|
+
descriptor:
|
|
211
|
+
descriptor === undefined
|
|
212
|
+
? {
|
|
213
|
+
id: config.datasetId,
|
|
214
|
+
domain: mtaOpenDataDomain,
|
|
215
|
+
backing: "socrata"
|
|
216
|
+
}
|
|
217
|
+
: descriptor
|
|
218
|
+
})
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
export const handleMtaOpenDataQuery = (config: {
|
|
222
|
+
readonly datasetId: string
|
|
223
|
+
readonly select: string
|
|
224
|
+
readonly dryRun: boolean
|
|
225
|
+
}) =>
|
|
226
|
+
Effect.gen(function* () {
|
|
227
|
+
const context = yield* CliCommandContext
|
|
228
|
+
const tokens = ["mta", "open-data", "dataset", "query"]
|
|
229
|
+
const request = {
|
|
230
|
+
datasetId: config.datasetId,
|
|
231
|
+
query: soqlFromSelect(config.select)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (config.dryRun) {
|
|
235
|
+
return yield* writeSoda3QueryDryRun({
|
|
236
|
+
context,
|
|
237
|
+
apiFamily: "mta",
|
|
238
|
+
tokens,
|
|
239
|
+
domain: mtaOpenDataDomain,
|
|
240
|
+
...request,
|
|
241
|
+
extra: {
|
|
242
|
+
known: findMtaOpenDataDataset(config.datasetId) !== undefined
|
|
243
|
+
}
|
|
244
|
+
})
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return yield* runEffect(
|
|
248
|
+
context,
|
|
249
|
+
"mta",
|
|
250
|
+
tokens,
|
|
251
|
+
queryMtaOpenData(request).pipe(Effect.provide(context.runtime.soda3Layer))
|
|
252
|
+
)
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
export const handleMtaGtfsStaticList = () =>
|
|
256
|
+
Effect.gen(function* () {
|
|
257
|
+
const context = yield* CliCommandContext
|
|
258
|
+
return yield* writeSuccess(context, "mta", {
|
|
259
|
+
feeds: mtaGtfsStaticFeeds
|
|
260
|
+
})
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
export const handleMtaGtfsStaticProbe = (config: {
|
|
264
|
+
readonly feed: Option.Option<string>
|
|
265
|
+
readonly url: Option.Option<string>
|
|
266
|
+
readonly dryRun: boolean
|
|
267
|
+
}) =>
|
|
268
|
+
Effect.gen(function* () {
|
|
269
|
+
const context = yield* CliCommandContext
|
|
270
|
+
const tokens = ["mta", "gtfs-static", "probe"]
|
|
271
|
+
const target = resolveStaticFeedTarget(config.feed, config.url)
|
|
272
|
+
|
|
273
|
+
if (target._tag !== "Resolved") {
|
|
274
|
+
return yield* failStaticTarget(context, tokens, target)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (config.dryRun) {
|
|
278
|
+
return yield* writeSuccess(context, "mta", {
|
|
279
|
+
dryRun: true,
|
|
280
|
+
method: "HEAD",
|
|
281
|
+
url: target.url,
|
|
282
|
+
source: target.source,
|
|
283
|
+
descriptor: target.descriptor
|
|
284
|
+
})
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return yield* runEffect(
|
|
288
|
+
context,
|
|
289
|
+
"mta",
|
|
290
|
+
tokens,
|
|
291
|
+
probeGtfsStatic({ url: target.url }).pipe(Effect.provide(context.runtime.mtaLayer))
|
|
292
|
+
)
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
export const handleMtaGtfsStaticFetch = (config: {
|
|
296
|
+
readonly feed: Option.Option<string>
|
|
297
|
+
readonly url: Option.Option<string>
|
|
298
|
+
readonly output: string
|
|
299
|
+
readonly dryRun: boolean
|
|
300
|
+
}) =>
|
|
301
|
+
Effect.gen(function* () {
|
|
302
|
+
const context = yield* CliCommandContext
|
|
303
|
+
const tokens = ["mta", "gtfs-static", "fetch"]
|
|
304
|
+
const target = resolveStaticFeedTarget(config.feed, config.url)
|
|
305
|
+
|
|
306
|
+
if (target._tag !== "Resolved") {
|
|
307
|
+
return yield* failStaticTarget(context, tokens, target)
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (config.dryRun) {
|
|
311
|
+
return yield* writeSuccess(context, "mta", {
|
|
312
|
+
dryRun: true,
|
|
313
|
+
method: "GET",
|
|
314
|
+
url: target.url,
|
|
315
|
+
output: config.output,
|
|
316
|
+
source: target.source,
|
|
317
|
+
descriptor: target.descriptor
|
|
318
|
+
})
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return yield* runEffect(
|
|
322
|
+
context,
|
|
323
|
+
"mta",
|
|
324
|
+
tokens,
|
|
325
|
+
Effect.gen(function* () {
|
|
326
|
+
const response = yield* fetchGtfsStaticResponse({ url: target.url }).pipe(
|
|
327
|
+
Effect.provide(context.runtime.mtaLayer)
|
|
328
|
+
)
|
|
329
|
+
return yield* writeResponseToFile(config.output, response)
|
|
330
|
+
})
|
|
331
|
+
)
|
|
332
|
+
})
|
|
333
|
+
|
|
334
|
+
export const handleMtaGtfsRealtimeList = () =>
|
|
335
|
+
Effect.gen(function* () {
|
|
336
|
+
const context = yield* CliCommandContext
|
|
337
|
+
return yield* writeSuccess(context, "mta", {
|
|
338
|
+
feeds: mtaGtfsRealtimeFeeds
|
|
339
|
+
})
|
|
340
|
+
})
|
|
341
|
+
|
|
342
|
+
const runMtaGtfsRealtimeRequest = (
|
|
343
|
+
tokens: ReadonlyArray<string>,
|
|
344
|
+
config: {
|
|
345
|
+
readonly feed: string
|
|
346
|
+
readonly url: Option.Option<string>
|
|
347
|
+
readonly dryRun: boolean
|
|
348
|
+
}
|
|
349
|
+
) =>
|
|
350
|
+
Effect.gen(function* () {
|
|
351
|
+
const context = yield* CliCommandContext
|
|
352
|
+
const target = resolveRealtimeFeedTarget(config.feed, config.url)
|
|
353
|
+
|
|
354
|
+
if (target._tag !== "Resolved") {
|
|
355
|
+
return yield* failRealtimeTarget(context, tokens, target)
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (config.dryRun) {
|
|
359
|
+
return yield* writeSuccess(context, "mta", {
|
|
360
|
+
dryRun: true,
|
|
361
|
+
method: "GET",
|
|
362
|
+
feed: target.feed,
|
|
363
|
+
url: target.url,
|
|
364
|
+
source: target.source,
|
|
365
|
+
descriptor: target.descriptor
|
|
366
|
+
})
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
return yield* runEffect(
|
|
370
|
+
context,
|
|
371
|
+
"mta",
|
|
372
|
+
tokens,
|
|
373
|
+
probeGtfsRealtime({ feed: target.feed, url: target.url }).pipe(
|
|
374
|
+
Effect.provide(context.runtime.mtaRealtimeLayer)
|
|
375
|
+
)
|
|
376
|
+
)
|
|
377
|
+
})
|
|
378
|
+
|
|
379
|
+
export const handleMtaGtfsRealtimeProbe = (config: {
|
|
380
|
+
readonly feed: string
|
|
381
|
+
readonly url: Option.Option<string>
|
|
382
|
+
readonly dryRun: boolean
|
|
383
|
+
}) => runMtaGtfsRealtimeRequest(["mta", "gtfs-rt", "probe"], config)
|
|
384
|
+
|
|
385
|
+
export const handleMtaGtfsRealtimeDecode = (config: {
|
|
386
|
+
readonly feed: string
|
|
387
|
+
readonly url: Option.Option<string>
|
|
388
|
+
readonly dryRun: boolean
|
|
389
|
+
}) => runMtaGtfsRealtimeRequest(["mta", "gtfs-rt", "decode"], config)
|
|
390
|
+
|
|
391
|
+
export const handleMtaGtfsRealtimeCapture = (config: {
|
|
392
|
+
readonly feed: string
|
|
393
|
+
readonly url: Option.Option<string>
|
|
394
|
+
readonly output: string
|
|
395
|
+
readonly manifestOutput: string
|
|
396
|
+
readonly dryRun: boolean
|
|
397
|
+
}) =>
|
|
398
|
+
Effect.gen(function* () {
|
|
399
|
+
const context = yield* CliCommandContext
|
|
400
|
+
const tokens = ["mta", "gtfs-rt", "capture"]
|
|
401
|
+
const target = resolveRealtimeFeedTarget(config.feed, config.url)
|
|
402
|
+
|
|
403
|
+
if (target._tag !== "Resolved") {
|
|
404
|
+
return yield* failRealtimeTarget(context, tokens, target)
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if (config.dryRun) {
|
|
408
|
+
return yield* writeSuccess(context, "mta", {
|
|
409
|
+
dryRun: true,
|
|
410
|
+
method: "GET",
|
|
411
|
+
feed: target.feed,
|
|
412
|
+
url: target.url,
|
|
413
|
+
output: config.output,
|
|
414
|
+
manifestOutput: config.manifestOutput,
|
|
415
|
+
source: target.source,
|
|
416
|
+
descriptor: target.descriptor
|
|
417
|
+
})
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
return yield* runEffect(
|
|
421
|
+
context,
|
|
422
|
+
"mta",
|
|
423
|
+
tokens,
|
|
424
|
+
Effect.gen(function* () {
|
|
425
|
+
const result = yield* captureGtfsRealtime({
|
|
426
|
+
feed: target.feed,
|
|
427
|
+
url: target.url
|
|
428
|
+
}).pipe(Effect.provide(context.runtime.mtaRealtimeLayer))
|
|
429
|
+
const manifest = MtaGtfsRealtimeCaptureManifest.make({
|
|
430
|
+
feed: result.feed,
|
|
431
|
+
status: result.status,
|
|
432
|
+
byteLength: result.byteLength,
|
|
433
|
+
sha256: result.sha256,
|
|
434
|
+
capturedAt: result.capturedAt,
|
|
435
|
+
url: result.url
|
|
436
|
+
})
|
|
437
|
+
|
|
438
|
+
yield* Effect.tryPromise({
|
|
439
|
+
try: () => atomicWrite(config.output, result.bytes),
|
|
440
|
+
catch: fileWriteError
|
|
441
|
+
})
|
|
442
|
+
yield* Effect.tryPromise({
|
|
443
|
+
try: () => atomicWrite(config.manifestOutput, `${JSON.stringify(manifest, null, 2)}\n`),
|
|
444
|
+
catch: fileWriteError
|
|
445
|
+
})
|
|
446
|
+
|
|
447
|
+
return {
|
|
448
|
+
output: config.output,
|
|
449
|
+
manifestOutput: config.manifestOutput,
|
|
450
|
+
manifest
|
|
451
|
+
}
|
|
452
|
+
})
|
|
453
|
+
)
|
|
454
|
+
})
|
|
455
|
+
|
|
456
|
+
const feedFlagDescription =
|
|
457
|
+
"Known feed id/name. Use --url with vehicle-positions, trip-updates, or alerts for manual URLs."
|
|
458
|
+
|
|
459
|
+
const optionalFeedFlag = Flag.optional(
|
|
460
|
+
Flag.string("feed").pipe(Flag.withDescription("Known feed id/name."))
|
|
461
|
+
)
|
|
462
|
+
|
|
463
|
+
const optionalUrlFlag = Flag.optional(
|
|
464
|
+
Flag.string("url").pipe(Flag.withDescription("Manual provider URL override."))
|
|
465
|
+
)
|
|
466
|
+
|
|
467
|
+
const mtaOpenDataDatasetListCommand = Command.make("list", {}, handleMtaOpenDataList).pipe(
|
|
468
|
+
Command.withShortDescription("List curated MTA Open Data descriptors.")
|
|
469
|
+
)
|
|
470
|
+
|
|
471
|
+
const mtaOpenDataDatasetInfoCommand = Command.make(
|
|
472
|
+
"info",
|
|
473
|
+
{
|
|
474
|
+
datasetId: Flag.string("dataset").pipe(
|
|
475
|
+
Flag.withDescription("MTA Open Data Socrata dataset id.")
|
|
476
|
+
)
|
|
477
|
+
},
|
|
478
|
+
handleMtaOpenDataInfo
|
|
479
|
+
).pipe(Command.withShortDescription("Show MTA Open Data dataset metadata."))
|
|
480
|
+
|
|
481
|
+
const mtaOpenDataDatasetQueryCommand = Command.make(
|
|
482
|
+
"query",
|
|
483
|
+
{
|
|
484
|
+
datasetId: Flag.string("dataset").pipe(
|
|
485
|
+
Flag.withDescription("MTA Open Data Socrata dataset id.")
|
|
486
|
+
),
|
|
487
|
+
select: Flag.string("select").pipe(
|
|
488
|
+
Flag.withDescription("SoQL select expression or full SELECT query.")
|
|
489
|
+
),
|
|
490
|
+
dryRun: Flag.boolean("dry-run").pipe(
|
|
491
|
+
Flag.withDescription("Print the planned request without contacting the provider.")
|
|
492
|
+
)
|
|
493
|
+
},
|
|
494
|
+
handleMtaOpenDataQuery
|
|
495
|
+
).pipe(Command.withShortDescription("Run an MTA Open Data SODA3 query."))
|
|
496
|
+
|
|
497
|
+
const mtaOpenDataDatasetCommand = Command.make("dataset").pipe(
|
|
498
|
+
Command.withShortDescription("MTA Open Data dataset commands."),
|
|
499
|
+
Command.withSubcommands([
|
|
500
|
+
mtaOpenDataDatasetListCommand,
|
|
501
|
+
mtaOpenDataDatasetInfoCommand,
|
|
502
|
+
mtaOpenDataDatasetQueryCommand
|
|
503
|
+
])
|
|
504
|
+
)
|
|
505
|
+
|
|
506
|
+
const mtaOpenDataCommand = Command.make("open-data").pipe(
|
|
507
|
+
Command.withShortDescription("MTA Open Data commands."),
|
|
508
|
+
Command.withSubcommands([mtaOpenDataDatasetCommand])
|
|
509
|
+
)
|
|
510
|
+
|
|
511
|
+
const mtaGtfsStaticListCommand = Command.make("list", {}, handleMtaGtfsStaticList).pipe(
|
|
512
|
+
Command.withShortDescription("List known MTA GTFS static feed descriptors.")
|
|
513
|
+
)
|
|
514
|
+
|
|
515
|
+
const mtaGtfsStaticProbeCommand = Command.make(
|
|
516
|
+
"probe",
|
|
517
|
+
{
|
|
518
|
+
feed: optionalFeedFlag,
|
|
519
|
+
url: optionalUrlFlag,
|
|
520
|
+
dryRun: Flag.boolean("dry-run").pipe(
|
|
521
|
+
Flag.withDescription("Print the planned request without contacting the provider.")
|
|
522
|
+
)
|
|
523
|
+
},
|
|
524
|
+
handleMtaGtfsStaticProbe
|
|
525
|
+
).pipe(Command.withShortDescription("Probe an MTA GTFS static feed."))
|
|
526
|
+
|
|
527
|
+
const mtaGtfsStaticFetchCommand = Command.make(
|
|
528
|
+
"fetch",
|
|
529
|
+
{
|
|
530
|
+
feed: optionalFeedFlag,
|
|
531
|
+
url: optionalUrlFlag,
|
|
532
|
+
output: Flag.string("output").pipe(
|
|
533
|
+
Flag.withDescription("Destination file path for the downloaded response body.")
|
|
534
|
+
),
|
|
535
|
+
dryRun: Flag.boolean("dry-run").pipe(
|
|
536
|
+
Flag.withDescription("Print the planned request without contacting the provider.")
|
|
537
|
+
)
|
|
538
|
+
},
|
|
539
|
+
handleMtaGtfsStaticFetch
|
|
540
|
+
).pipe(Command.withShortDescription("Fetch an MTA GTFS static file."))
|
|
541
|
+
|
|
542
|
+
const mtaGtfsStaticCommand = Command.make("gtfs-static").pipe(
|
|
543
|
+
Command.withShortDescription("MTA GTFS static commands."),
|
|
544
|
+
Command.withSubcommands([
|
|
545
|
+
mtaGtfsStaticListCommand,
|
|
546
|
+
mtaGtfsStaticProbeCommand,
|
|
547
|
+
mtaGtfsStaticFetchCommand
|
|
548
|
+
])
|
|
549
|
+
)
|
|
550
|
+
|
|
551
|
+
const mtaGtfsRealtimeListCommand = Command.make("list", {}, handleMtaGtfsRealtimeList).pipe(
|
|
552
|
+
Command.withShortDescription("List known MTA GTFS realtime feed descriptors.")
|
|
553
|
+
)
|
|
554
|
+
|
|
555
|
+
const mtaGtfsRealtimeProbeCommand = Command.make(
|
|
556
|
+
"probe",
|
|
557
|
+
{
|
|
558
|
+
feed: Flag.string("feed").pipe(Flag.withDescription(feedFlagDescription)),
|
|
559
|
+
url: optionalUrlFlag,
|
|
560
|
+
dryRun: Flag.boolean("dry-run").pipe(
|
|
561
|
+
Flag.withDescription("Print the planned request without contacting the provider.")
|
|
562
|
+
)
|
|
563
|
+
},
|
|
564
|
+
handleMtaGtfsRealtimeProbe
|
|
565
|
+
).pipe(Command.withShortDescription("Probe an MTA GTFS realtime feed."))
|
|
566
|
+
|
|
567
|
+
const mtaGtfsRealtimeDecodeCommand = Command.make(
|
|
568
|
+
"decode",
|
|
569
|
+
{
|
|
570
|
+
feed: Flag.string("feed").pipe(Flag.withDescription(feedFlagDescription)),
|
|
571
|
+
url: optionalUrlFlag,
|
|
572
|
+
dryRun: Flag.boolean("dry-run").pipe(
|
|
573
|
+
Flag.withDescription("Print the planned request without contacting the provider.")
|
|
574
|
+
)
|
|
575
|
+
},
|
|
576
|
+
handleMtaGtfsRealtimeDecode
|
|
577
|
+
).pipe(Command.withShortDescription("Fetch and decode an MTA GTFS realtime feed."))
|
|
578
|
+
|
|
579
|
+
const mtaGtfsRealtimeCaptureCommand = Command.make(
|
|
580
|
+
"capture",
|
|
581
|
+
{
|
|
582
|
+
feed: Flag.string("feed").pipe(Flag.withDescription(feedFlagDescription)),
|
|
583
|
+
url: optionalUrlFlag,
|
|
584
|
+
output: Flag.string("output").pipe(
|
|
585
|
+
Flag.withDescription("Destination file path for the raw GTFS realtime protobuf bytes.")
|
|
586
|
+
),
|
|
587
|
+
manifestOutput: Flag.string("manifest-output").pipe(
|
|
588
|
+
Flag.withDescription("Destination file path for the capture manifest JSON.")
|
|
589
|
+
),
|
|
590
|
+
dryRun: Flag.boolean("dry-run").pipe(
|
|
591
|
+
Flag.withDescription("Print the planned request without contacting the provider.")
|
|
592
|
+
)
|
|
593
|
+
},
|
|
594
|
+
handleMtaGtfsRealtimeCapture
|
|
595
|
+
).pipe(Command.withShortDescription("Capture raw MTA GTFS realtime bytes and a manifest."))
|
|
596
|
+
|
|
597
|
+
const mtaGtfsRealtimeCommand = Command.make("gtfs-rt").pipe(
|
|
598
|
+
Command.withShortDescription("MTA GTFS realtime commands."),
|
|
599
|
+
Command.withSubcommands([
|
|
600
|
+
mtaGtfsRealtimeListCommand,
|
|
601
|
+
mtaGtfsRealtimeProbeCommand,
|
|
602
|
+
mtaGtfsRealtimeDecodeCommand,
|
|
603
|
+
mtaGtfsRealtimeCaptureCommand
|
|
604
|
+
])
|
|
605
|
+
)
|
|
606
|
+
|
|
607
|
+
export const mtaCommand = Command.make("mta").pipe(
|
|
608
|
+
Command.withShortDescription("MTA data commands."),
|
|
609
|
+
Command.withSubcommands([mtaOpenDataCommand, mtaGtfsStaticCommand, mtaGtfsRealtimeCommand])
|
|
610
|
+
)
|