@nyc-transit-kit/cli 0.1.3 → 0.2.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.
@@ -1,616 +1,98 @@
1
+ // biome-ignore-all format: first-wave CLI compression budget
1
2
  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"
3
+ import { findMtaGtfsRealtimeFeed, findMtaGtfsStaticFeed, type MtaGtfsRealtimeFeedDescriptor, type MtaGtfsStaticFeedDescriptor, mtaGtfsRealtimeFeeds, mtaGtfsStaticFeeds } from "@nyc-transit-kit/mta/feeds"
15
4
  import { captureGtfsRealtime, probeGtfsRealtime } from "@nyc-transit-kit/mta/gtfs-realtime"
16
5
  import { fetchGtfsStaticResponse, probeGtfsStatic } from "@nyc-transit-kit/mta/gtfs-static"
17
- import { queryMtaOpenData } from "@nyc-transit-kit/mta/open-data"
6
+ import { findMtaOpenDataDataset, mtaOpenDataDatasets, mtaOpenDataDomain, queryMtaOpenData } from "@nyc-transit-kit/mta/open-data"
18
7
  import * as Effect from "effect/Effect"
19
8
  import * as Option from "effect/Option"
20
9
  import * as Command from "effect/unstable/cli/Command"
21
10
  import * as Flag from "effect/unstable/cli/Flag"
22
11
  import { atomicWriteGroup } 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
- }
12
+ import { CliCommandContext, type CommandContext, dryRunFlag, failCommand, fileWriteError, makeDatasetCommands, missingOption, outputFlag, runEffect, writeResponseToFile, writeSuccess } from "./shared"
71
13
 
14
+ type StaticFeedTarget = { readonly _tag: "Resolved"; readonly url: string; readonly source: "descriptor" | "url"; readonly descriptor?: MtaGtfsStaticFeedDescriptor } | { readonly _tag: "Missing" } | { readonly _tag: "Unsupported"; readonly feed: string }
15
+ type RealtimeFeedTarget = { readonly _tag: "Resolved"; readonly feed: GtfsFeedKind; readonly url: string; readonly source: "descriptor" | "url"; readonly descriptor?: MtaGtfsRealtimeFeedDescriptor } | { readonly _tag: "MissingUrl"; readonly feed: GtfsFeedKind } | { readonly _tag: "Unsupported"; readonly feed: string }
72
16
  const gtfsFeedKinds = new Set(["vehicle-positions", "trip-updates", "alerts"])
73
-
74
17
  const optionalString = (value: Option.Option<string>) => Option.getOrUndefined(value)
75
-
76
18
  const isGtfsFeedKind = (value: string): value is GtfsFeedKind => gtfsFeedKinds.has(value)
77
19
 
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
- }
20
+ const resolveStaticFeedTarget = (feed: Option.Option<string>, url: Option.Option<string>): StaticFeedTarget => {
21
+ const feedKey = optionalString(feed), urlOverride = optionalString(url), descriptor = feedKey === undefined ? undefined : findMtaGtfsStaticFeed(feedKey)
22
+ if (urlOverride !== undefined) return { _tag: "Resolved", url: urlOverride, source: "url", ...(descriptor === undefined ? {} : { descriptor }) }
23
+ if (feedKey === undefined) return { _tag: "Missing" }
24
+ return descriptor === undefined ? { _tag: "Unsupported", feed: feedKey } : { _tag: "Resolved", url: descriptor.url, source: "descriptor", descriptor }
112
25
  }
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
- }
26
+ const resolveRealtimeFeedTarget = (feedKey: string, url: Option.Option<string>): RealtimeFeedTarget => {
27
+ const urlOverride = optionalString(url), descriptor = findMtaGtfsRealtimeFeed(feedKey)
28
+ if (descriptor !== undefined) return { _tag: "Resolved", feed: descriptor.feed, url: urlOverride ?? descriptor.url, source: urlOverride === undefined ? "descriptor" : "url", descriptor }
29
+ if (!isGtfsFeedKind(feedKey)) return { _tag: "Unsupported", feed: feedKey }
30
+ return urlOverride === undefined ? { _tag: "MissingUrl", feed: feedKey } : { _tag: "Resolved", feed: feedKey, url: urlOverride, source: "url" }
151
31
  }
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 = () =>
32
+ const failStaticTarget = (context: CommandContext, tokens: ReadonlyArray<string>, target: StaticFeedTarget) =>
33
+ target._tag === "Missing" ? missingOption(context, tokens, "feed") : target._tag === "Unsupported" ? failCommand(context, "mta", "unsupported-feed", `Unsupported MTA GTFS static feed: ${target.feed}`, tokens) : Effect.void
34
+ const failRealtimeTarget = (context: CommandContext, tokens: ReadonlyArray<string>, target: RealtimeFeedTarget) =>
35
+ target._tag === "MissingUrl" ? missingOption(context, tokens, "url") : target._tag === "Unsupported" ? failCommand(context, "mta", "unsupported-feed", `Unsupported MTA GTFS realtime feed: ${target.feed}`, tokens) : Effect.void
36
+ const feedTargetPayload = (target: { readonly url: string; readonly source: "descriptor" | "url"; readonly descriptor?: unknown }) => ({ url: target.url, source: target.source, descriptor: target.descriptor })
37
+ const feedListHandler = (feeds: unknown) => () => Effect.gen(function* () { const context = yield* CliCommandContext; return yield* writeSuccess(context, "mta", { feeds }) })
38
+
39
+ const handleMtaGtfsStaticProbe = (config: { readonly feed: Option.Option<string>; readonly url: Option.Option<string>; readonly dryRun: boolean }) =>
196
40
  Effect.gen(function* () {
197
- const context = yield* CliCommandContext
198
- return yield* writeSuccess(context, "mta", {
199
- datasets: mtaOpenDataDatasets
200
- })
41
+ const context = yield* CliCommandContext, tokens = ["mta", "gtfs-static", "probe"], target = resolveStaticFeedTarget(config.feed, config.url)
42
+ if (target._tag !== "Resolved") return yield* failStaticTarget(context, tokens, target)
43
+ return yield* config.dryRun ? writeSuccess(context, "mta", { dryRun: true, method: "HEAD", ...feedTargetPayload(target) }) : runEffect(context, "mta", tokens, probeGtfsStatic({ url: target.url }).pipe(Effect.provide(context.runtime.mtaLayer)))
201
44
  })
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 = () =>
45
+ const handleMtaGtfsStaticFetch = (config: { readonly feed: Option.Option<string>; readonly url: Option.Option<string>; readonly output: string; readonly dryRun: boolean }) =>
256
46
  Effect.gen(function* () {
257
- const context = yield* CliCommandContext
258
- return yield* writeSuccess(context, "mta", {
259
- feeds: mtaGtfsStaticFeeds
260
- })
47
+ const context = yield* CliCommandContext, tokens = ["mta", "gtfs-static", "fetch"], target = resolveStaticFeedTarget(config.feed, config.url)
48
+ if (target._tag !== "Resolved") return yield* failStaticTarget(context, tokens, target)
49
+ return yield* config.dryRun ? writeSuccess(context, "mta", { dryRun: true, method: "GET", output: config.output, ...feedTargetPayload(target) }) : runEffect(context, "mta", tokens, Effect.gen(function* () { const response = yield* fetchGtfsStaticResponse({ url: target.url }).pipe(Effect.provide(context.runtime.mtaLayer)); return yield* writeResponseToFile(config.output, response) }))
261
50
  })
262
-
263
- export const handleMtaGtfsStaticProbe = (config: {
264
- readonly feed: Option.Option<string>
265
- readonly url: Option.Option<string>
266
- readonly dryRun: boolean
267
- }) =>
51
+ const runMtaGtfsRealtimeRequest = (tokens: ReadonlyArray<string>, config: { readonly feed: string; readonly url: Option.Option<string>; readonly dryRun: boolean }) =>
268
52
  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
- )
53
+ const context = yield* CliCommandContext, target = resolveRealtimeFeedTarget(config.feed, config.url)
54
+ if (target._tag !== "Resolved") return yield* failRealtimeTarget(context, tokens, target)
55
+ return yield* config.dryRun ? writeSuccess(context, "mta", { dryRun: true, method: "GET", feed: target.feed, ...feedTargetPayload(target) }) : runEffect(context, "mta", tokens, probeGtfsRealtime({ feed: target.feed, url: target.url }).pipe(Effect.provide(context.runtime.mtaRealtimeLayer)))
293
56
  })
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
- }) =>
57
+ const handleMtaGtfsRealtimeProbe = (config: { readonly feed: string; readonly url: Option.Option<string>; readonly dryRun: boolean }) => runMtaGtfsRealtimeRequest(["mta", "gtfs-rt", "probe"], config)
58
+ const handleMtaGtfsRealtimeDecode = (config: { readonly feed: string; readonly url: Option.Option<string>; readonly dryRun: boolean }) => runMtaGtfsRealtimeRequest(["mta", "gtfs-rt", "decode"], config)
59
+ const handleMtaGtfsRealtimeCapture = (config: { readonly feed: string; readonly url: Option.Option<string>; readonly output: string; readonly manifestOutput: string; readonly dryRun: boolean }) =>
301
60
  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
- )
61
+ const context = yield* CliCommandContext, tokens = ["mta", "gtfs-rt", "capture"], target = resolveRealtimeFeedTarget(config.feed, config.url)
62
+ if (target._tag !== "Resolved") return yield* failRealtimeTarget(context, tokens, target)
63
+ if (config.dryRun) return yield* writeSuccess(context, "mta", { dryRun: true, method: "GET", feed: target.feed, output: config.output, manifestOutput: config.manifestOutput, ...feedTargetPayload(target) })
64
+ return yield* runEffect(context, "mta", tokens, Effect.gen(function* () {
65
+ const result = yield* captureGtfsRealtime({ feed: target.feed, url: target.url }).pipe(Effect.provide(context.runtime.mtaRealtimeLayer))
66
+ const manifest = MtaGtfsRealtimeCaptureManifest.make({ feed: result.feed, status: result.status, byteLength: result.byteLength, sha256: result.sha256, capturedAt: result.capturedAt, url: result.url })
67
+ yield* Effect.tryPromise({ try: () => atomicWriteGroup([{ path: config.output, body: result.bytes }, { path: config.manifestOutput, body: `${JSON.stringify(manifest, null, 2)}\n` }]), catch: fileWriteError })
68
+ return { output: config.output, manifestOutput: config.manifestOutput, manifest }
69
+ }))
332
70
  })
333
71
 
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: () =>
440
- atomicWriteGroup([
441
- {
442
- path: config.output,
443
- body: result.bytes
444
- },
445
- {
446
- path: config.manifestOutput,
447
- body: `${JSON.stringify(manifest, null, 2)}\n`
448
- }
449
- ]),
450
- catch: fileWriteError
451
- })
452
-
453
- return {
454
- output: config.output,
455
- manifestOutput: config.manifestOutput,
456
- manifest
457
- }
458
- })
459
- )
460
- })
461
-
462
- const feedFlagDescription =
463
- "Known feed id/name. Use --url with vehicle-positions, trip-updates, or alerts for manual URLs."
464
-
465
- const optionalFeedFlag = Flag.optional(
466
- Flag.string("feed").pipe(Flag.withDescription("Known feed id/name."))
467
- )
468
-
469
- const optionalUrlFlag = Flag.optional(
470
- Flag.string("url").pipe(Flag.withDescription("Manual provider URL override."))
471
- )
472
-
473
- const mtaOpenDataDatasetListCommand = Command.make("list", {}, handleMtaOpenDataList).pipe(
474
- Command.withShortDescription("List curated MTA Open Data descriptors.")
475
- )
476
-
477
- const mtaOpenDataDatasetInfoCommand = Command.make(
478
- "info",
479
- {
480
- datasetId: Flag.string("dataset").pipe(
481
- Flag.withDescription("MTA Open Data Socrata dataset id.")
482
- )
483
- },
484
- handleMtaOpenDataInfo
485
- ).pipe(Command.withShortDescription("Show MTA Open Data dataset metadata."))
486
-
487
- const mtaOpenDataDatasetQueryCommand = Command.make(
488
- "query",
489
- {
490
- datasetId: Flag.string("dataset").pipe(
491
- Flag.withDescription("MTA Open Data Socrata dataset id.")
492
- ),
493
- select: Flag.string("select").pipe(
494
- Flag.withDescription("SoQL select expression or full SELECT query.")
495
- ),
496
- dryRun: Flag.boolean("dry-run").pipe(
497
- Flag.withDescription("Print the planned request without contacting the provider.")
498
- )
499
- },
500
- handleMtaOpenDataQuery
501
- ).pipe(Command.withShortDescription("Run an MTA Open Data SODA3 query."))
502
-
503
- const mtaOpenDataDatasetCommand = Command.make("dataset").pipe(
504
- Command.withShortDescription("MTA Open Data dataset commands."),
505
- Command.withSubcommands([
506
- mtaOpenDataDatasetListCommand,
507
- mtaOpenDataDatasetInfoCommand,
508
- mtaOpenDataDatasetQueryCommand
509
- ])
510
- )
511
-
512
- const mtaOpenDataCommand = Command.make("open-data").pipe(
513
- Command.withShortDescription("MTA Open Data commands."),
514
- Command.withSubcommands([mtaOpenDataDatasetCommand])
515
- )
516
-
517
- const mtaGtfsStaticListCommand = Command.make("list", {}, handleMtaGtfsStaticList).pipe(
518
- Command.withShortDescription("List known MTA GTFS static feed descriptors.")
519
- )
520
-
521
- const mtaGtfsStaticProbeCommand = Command.make(
522
- "probe",
523
- {
524
- feed: optionalFeedFlag,
525
- url: optionalUrlFlag,
526
- dryRun: Flag.boolean("dry-run").pipe(
527
- Flag.withDescription("Print the planned request without contacting the provider.")
528
- )
529
- },
530
- handleMtaGtfsStaticProbe
531
- ).pipe(Command.withShortDescription("Probe an MTA GTFS static feed."))
532
-
533
- const mtaGtfsStaticFetchCommand = Command.make(
534
- "fetch",
535
- {
536
- feed: optionalFeedFlag,
537
- url: optionalUrlFlag,
538
- output: Flag.string("output").pipe(
539
- Flag.withDescription("Destination file path for the downloaded response body.")
540
- ),
541
- dryRun: Flag.boolean("dry-run").pipe(
542
- Flag.withDescription("Print the planned request without contacting the provider.")
543
- )
544
- },
545
- handleMtaGtfsStaticFetch
546
- ).pipe(Command.withShortDescription("Fetch an MTA GTFS static file."))
547
-
548
- const mtaGtfsStaticCommand = Command.make("gtfs-static").pipe(
549
- Command.withShortDescription("MTA GTFS static commands."),
550
- Command.withSubcommands([
551
- mtaGtfsStaticListCommand,
552
- mtaGtfsStaticProbeCommand,
553
- mtaGtfsStaticFetchCommand
554
- ])
555
- )
556
-
557
- const mtaGtfsRealtimeListCommand = Command.make("list", {}, handleMtaGtfsRealtimeList).pipe(
558
- Command.withShortDescription("List known MTA GTFS realtime feed descriptors.")
559
- )
560
-
561
- const mtaGtfsRealtimeProbeCommand = Command.make(
562
- "probe",
563
- {
564
- feed: Flag.string("feed").pipe(Flag.withDescription(feedFlagDescription)),
565
- url: optionalUrlFlag,
566
- dryRun: Flag.boolean("dry-run").pipe(
567
- Flag.withDescription("Print the planned request without contacting the provider.")
568
- )
569
- },
570
- handleMtaGtfsRealtimeProbe
571
- ).pipe(Command.withShortDescription("Probe an MTA GTFS realtime feed."))
572
-
573
- const mtaGtfsRealtimeDecodeCommand = Command.make(
574
- "decode",
575
- {
576
- feed: Flag.string("feed").pipe(Flag.withDescription(feedFlagDescription)),
577
- url: optionalUrlFlag,
578
- dryRun: Flag.boolean("dry-run").pipe(
579
- Flag.withDescription("Print the planned request without contacting the provider.")
580
- )
581
- },
582
- handleMtaGtfsRealtimeDecode
583
- ).pipe(Command.withShortDescription("Fetch and decode an MTA GTFS realtime feed."))
584
-
585
- const mtaGtfsRealtimeCaptureCommand = Command.make(
586
- "capture",
587
- {
588
- feed: Flag.string("feed").pipe(Flag.withDescription(feedFlagDescription)),
589
- url: optionalUrlFlag,
590
- output: Flag.string("output").pipe(
591
- Flag.withDescription("Destination file path for the raw GTFS realtime protobuf bytes.")
592
- ),
593
- manifestOutput: Flag.string("manifest-output").pipe(
594
- Flag.withDescription("Destination file path for the capture manifest JSON.")
595
- ),
596
- dryRun: Flag.boolean("dry-run").pipe(
597
- Flag.withDescription("Print the planned request without contacting the provider.")
598
- )
599
- },
600
- handleMtaGtfsRealtimeCapture
601
- ).pipe(Command.withShortDescription("Capture raw MTA GTFS realtime bytes and a manifest."))
602
-
603
- const mtaGtfsRealtimeCommand = Command.make("gtfs-rt").pipe(
604
- Command.withShortDescription("MTA GTFS realtime commands."),
605
- Command.withSubcommands([
606
- mtaGtfsRealtimeListCommand,
607
- mtaGtfsRealtimeProbeCommand,
608
- mtaGtfsRealtimeDecodeCommand,
609
- mtaGtfsRealtimeCaptureCommand
610
- ])
611
- )
612
-
613
- export const mtaCommand = Command.make("mta").pipe(
614
- Command.withShortDescription("MTA data commands."),
615
- Command.withSubcommands([mtaOpenDataCommand, mtaGtfsStaticCommand, mtaGtfsRealtimeCommand])
616
- )
72
+ const feedFlagDescription = "Known feed id/name. Use --url with vehicle-positions, trip-updates, or alerts for manual URLs."
73
+ const optionalFeedFlag = Flag.optional(Flag.string("feed").pipe(Flag.withDescription("Known feed id/name.")))
74
+ const optionalUrlFlag = Flag.optional(Flag.string("url").pipe(Flag.withDescription("Manual provider URL override.")))
75
+ const realtimeFeedFlag = Flag.string("feed").pipe(Flag.withDescription(feedFlagDescription))
76
+ const realtimeRequestCommand = (name: "probe" | "decode", description: string, handler: (config: { readonly feed: string; readonly url: Option.Option<string>; readonly dryRun: boolean }) => Effect.Effect<void, never, CliCommandContext>) =>
77
+ Command.make(name, { feed: realtimeFeedFlag, url: optionalUrlFlag, dryRun: dryRunFlag }, handler).pipe(Command.withShortDescription(description))
78
+
79
+ const mtaOpenDataDatasetCommands = makeDatasetCommands({
80
+ family: "mta", tokens: ["mta", "open-data", "dataset"], idConfigKey: "datasetId", idFlagName: "dataset", idFlagDescription: "MTA Open Data Socrata dataset id.", domain: mtaOpenDataDomain,
81
+ listShortDescription: "List curated MTA Open Data descriptors.", infoShortDescription: "Show MTA Open Data dataset metadata.", queryShortDescription: "Run an MTA Open Data SODA3 query.",
82
+ listPayload: () => ({ datasets: mtaOpenDataDatasets }),
83
+ infoPayload: (datasetId) => { const descriptor = findMtaOpenDataDataset(datasetId); return { payload: { known: descriptor !== undefined, descriptor: descriptor === undefined ? { id: datasetId, domain: mtaOpenDataDomain, backing: "socrata" } : descriptor } } },
84
+ resolveDataset: (datasetId) => ({ payload: { datasetId, extra: { known: findMtaOpenDataDataset(datasetId) !== undefined } } }),
85
+ query: ({ id, query }) => queryMtaOpenData({ datasetId: id, query })
86
+ })
87
+ const mtaOpenDataDatasetCommand = Command.make("dataset").pipe(Command.withShortDescription("MTA Open Data dataset commands."), Command.withSubcommands(mtaOpenDataDatasetCommands))
88
+ const mtaOpenDataCommand = Command.make("open-data").pipe(Command.withShortDescription("MTA Open Data commands."), Command.withSubcommands([mtaOpenDataDatasetCommand]))
89
+ const mtaGtfsStaticListCommand = Command.make("list", {}, feedListHandler(mtaGtfsStaticFeeds)).pipe(Command.withShortDescription("List known MTA GTFS static feed descriptors."))
90
+ const mtaGtfsStaticProbeCommand = Command.make("probe", { feed: optionalFeedFlag, url: optionalUrlFlag, dryRun: dryRunFlag }, handleMtaGtfsStaticProbe).pipe(Command.withShortDescription("Probe an MTA GTFS static feed."))
91
+ const mtaGtfsStaticFetchCommand = Command.make("fetch", { feed: optionalFeedFlag, url: optionalUrlFlag, output: outputFlag, dryRun: dryRunFlag }, handleMtaGtfsStaticFetch).pipe(Command.withShortDescription("Fetch an MTA GTFS static file."))
92
+ const mtaGtfsStaticCommand = Command.make("gtfs-static").pipe(Command.withShortDescription("MTA GTFS static commands."), Command.withSubcommands([mtaGtfsStaticListCommand, mtaGtfsStaticProbeCommand, mtaGtfsStaticFetchCommand]))
93
+ const mtaGtfsRealtimeListCommand = Command.make("list", {}, feedListHandler(mtaGtfsRealtimeFeeds)).pipe(Command.withShortDescription("List known MTA GTFS realtime feed descriptors."))
94
+ const mtaGtfsRealtimeProbeCommand = realtimeRequestCommand("probe", "Probe an MTA GTFS realtime feed.", handleMtaGtfsRealtimeProbe)
95
+ const mtaGtfsRealtimeDecodeCommand = realtimeRequestCommand("decode", "Fetch and decode an MTA GTFS realtime feed.", handleMtaGtfsRealtimeDecode)
96
+ const mtaGtfsRealtimeCaptureCommand = Command.make("capture", { feed: realtimeFeedFlag, url: optionalUrlFlag, output: Flag.string("output").pipe(Flag.withDescription("Destination file path for the raw GTFS realtime protobuf bytes.")), manifestOutput: Flag.string("manifest-output").pipe(Flag.withDescription("Destination file path for the capture manifest JSON.")), dryRun: dryRunFlag }, handleMtaGtfsRealtimeCapture).pipe(Command.withShortDescription("Capture raw MTA GTFS realtime bytes and a manifest."))
97
+ const mtaGtfsRealtimeCommand = Command.make("gtfs-rt").pipe(Command.withShortDescription("MTA GTFS realtime commands."), Command.withSubcommands([mtaGtfsRealtimeListCommand, mtaGtfsRealtimeProbeCommand, mtaGtfsRealtimeDecodeCommand, mtaGtfsRealtimeCaptureCommand]))
98
+ export const mtaCommand = Command.make("mta").pipe(Command.withShortDescription("MTA data commands."), Command.withSubcommands([mtaOpenDataCommand, mtaGtfsStaticCommand, mtaGtfsRealtimeCommand]))