@leverstack/arcq 1.0.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 (72) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +361 -0
  3. package/dist/bin/arcq.d.ts +2 -0
  4. package/dist/bin/arcq.js +11 -0
  5. package/dist/bin/arcq.js.map +1 -0
  6. package/dist/index.d.ts +5 -0
  7. package/dist/index.js +85 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/lib/active-cmd.d.ts +1 -0
  10. package/dist/lib/active-cmd.js +16 -0
  11. package/dist/lib/active-cmd.js.map +1 -0
  12. package/dist/lib/arcgis-core.d.ts +5 -0
  13. package/dist/lib/arcgis-core.js +89 -0
  14. package/dist/lib/arcgis-core.js.map +1 -0
  15. package/dist/lib/cache-core.d.ts +4 -0
  16. package/dist/lib/cache-core.js +13 -0
  17. package/dist/lib/cache-core.js.map +1 -0
  18. package/dist/lib/config-core.d.ts +4 -0
  19. package/dist/lib/config-core.js +21 -0
  20. package/dist/lib/config-core.js.map +1 -0
  21. package/dist/lib/context-core.d.ts +3 -0
  22. package/dist/lib/context-core.js +13 -0
  23. package/dist/lib/context-core.js.map +1 -0
  24. package/dist/lib/errors.d.ts +5 -0
  25. package/dist/lib/errors.js +13 -0
  26. package/dist/lib/errors.js.map +1 -0
  27. package/dist/lib/fields-cmd.d.ts +1 -0
  28. package/dist/lib/fields-cmd.js +22 -0
  29. package/dist/lib/fields-cmd.js.map +1 -0
  30. package/dist/lib/help-cmd.d.ts +1 -0
  31. package/dist/lib/help-cmd.js +71 -0
  32. package/dist/lib/help-cmd.js.map +1 -0
  33. package/dist/lib/interactive-cmd.d.ts +1 -0
  34. package/dist/lib/interactive-cmd.js +90 -0
  35. package/dist/lib/interactive-cmd.js.map +1 -0
  36. package/dist/lib/layer-resolve.d.ts +6 -0
  37. package/dist/lib/layer-resolve.js +34 -0
  38. package/dist/lib/layer-resolve.js.map +1 -0
  39. package/dist/lib/layers-cmd.d.ts +1 -0
  40. package/dist/lib/layers-cmd.js +15 -0
  41. package/dist/lib/layers-cmd.js.map +1 -0
  42. package/dist/lib/list-cmd.d.ts +1 -0
  43. package/dist/lib/list-cmd.js +19 -0
  44. package/dist/lib/list-cmd.js.map +1 -0
  45. package/dist/lib/query-cmd.d.ts +1 -0
  46. package/dist/lib/query-cmd.js +130 -0
  47. package/dist/lib/query-cmd.js.map +1 -0
  48. package/dist/lib/refresh-cmd.d.ts +1 -0
  49. package/dist/lib/refresh-cmd.js +16 -0
  50. package/dist/lib/refresh-cmd.js.map +1 -0
  51. package/dist/lib/services-cmd.d.ts +1 -0
  52. package/dist/lib/services-cmd.js +20 -0
  53. package/dist/lib/services-cmd.js.map +1 -0
  54. package/dist/lib/sync-cmd.d.ts +1 -0
  55. package/dist/lib/sync-cmd.js +25 -0
  56. package/dist/lib/sync-cmd.js.map +1 -0
  57. package/dist/lib/tls-core.d.ts +4 -0
  58. package/dist/lib/tls-core.js +47 -0
  59. package/dist/lib/tls-core.js.map +1 -0
  60. package/dist/lib/token-cmd.d.ts +1 -0
  61. package/dist/lib/token-cmd.js +22 -0
  62. package/dist/lib/token-cmd.js.map +1 -0
  63. package/dist/lib/token-core.d.ts +3 -0
  64. package/dist/lib/token-core.js +18 -0
  65. package/dist/lib/token-core.js.map +1 -0
  66. package/dist/lib/types.d.ts +65 -0
  67. package/dist/lib/types.js +3 -0
  68. package/dist/lib/types.js.map +1 -0
  69. package/dist/lib/version-cmd.d.ts +2 -0
  70. package/dist/lib/version-cmd.js +22 -0
  71. package/dist/lib/version-cmd.js.map +1 -0
  72. package/package.json +65 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Leverstack LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,361 @@
1
+ # arcq
2
+
3
+ [![CI](https://github.com/jameseaster/arcq/actions/workflows/lint-and-test.yml/badge.svg)](https://github.com/jameseaster/arcq/actions/workflows/lint-and-test.yml)
4
+ [![npm version](https://img.shields.io/npm/v/%40leverstack%2Farcq)](https://www.npmjs.com/package/@leverstack/arcq)
5
+
6
+ A fast CLI for querying ArcGIS feature services, designed to be driven by both humans and coding agents.
7
+
8
+ ## Why arcq
9
+
10
+ ArcGIS REST is awkward to drive from a terminal or a script: query strings are fiddly, failures hide inside HTTP-200 response bodies, and discovering what layers and fields exist usually means clicking through a services directory. arcq is built to be scripted and, in particular, to be safe for a coding agent to run unattended:
11
+
12
+ - **Strict exit-code contract.** `0` success (an empty `[]` result is success, not an error), `1` error, `2` token invalid or expired. An agent can branch on the exit code instead of parsing prose.
13
+ - **JSON-only stdout.** Query results are the only thing on stdout, so `arcq query ... | jq` always works. The human-readable query summary goes to stderr.
14
+ - **Loud errors.** ArcGIS reports failures (a malformed `where` clause, an expired token) inside HTTP-200 bodies; arcq inspects every response and exits non-zero, so a `[]` you see is trustworthy.
15
+ - **Non-interactive discovery.** `arcq layers --names`, `arcq active`, `arcq fields`, and `arcq list` expose the catalog and schema without the fzf picker, so an agent can find its way around a server on its own.
16
+ - **One-command setup for a whole server.** `arcq refresh && arcq sync` indexes every service in your config and turns each layer into a named shortcut.
17
+
18
+ ## Installation
19
+
20
+ **Prerequisites**
21
+
22
+ - [Node.js](https://nodejs.org/) **18 or newer**
23
+ - [`fzf`](https://github.com/junegunn/fzf) - only needed for the interactive `arcq use` picker
24
+
25
+ Install globally from npm:
26
+
27
+ ```bash
28
+ npm install -g @leverstack/arcq
29
+ ```
30
+
31
+ `arcq` is now on your PATH (the package is scoped; the command is still `arcq`).
32
+
33
+ ### From source
34
+
35
+ ```bash
36
+ git clone https://github.com/jameseaster/arcq.git
37
+ cd arcq
38
+ npm install # installs deps AND compiles TypeScript -> dist/ (via the "prepare" script)
39
+ npm install -g . # put `arcq` on your PATH (also recompiles dist/ automatically)
40
+ ```
41
+
42
+ You do **not** need to create any symlink by hand; `npm install -g .` (or `npm link` below) does that for you.
43
+
44
+ > **arcq is written in TypeScript and runs from compiled output in `dist/`.** The
45
+ > `npm run build` step (`tsc`) is wired into npm's `prepare` lifecycle, so it runs
46
+ > automatically on `npm install`, `npm install -g .`, and `npm link` - you normally
47
+ > never invoke it directly. The one exception is development (see below): after
48
+ > editing any `.ts` source you must rebuild for the global command to pick it up.
49
+
50
+ **Local development**
51
+
52
+ Link the working tree instead of installing a copy:
53
+
54
+ ```bash
55
+ npm link # also runs the build via "prepare"
56
+ ```
57
+
58
+ Because the linked `arcq` runs the compiled `dist/`, **rebuild after editing source**:
59
+
60
+ ```bash
61
+ npm run build # recompile dist/ so the global `arcq` reflects your changes
62
+ ```
63
+
64
+ Handy scripts:
65
+
66
+ ```bash
67
+ npm run build # compile TypeScript -> dist/
68
+ npm run typecheck # type-check source + tests without emitting
69
+ npm test # run the test suite
70
+ npm run lint # lint
71
+ ```
72
+
73
+ ## Using arcq with coding agents
74
+
75
+ The repo ships a ready-made [Agent Skill](skills/arcq/SKILL.md) covering discovery, the quoting rule, output shaping, the exit-code contract, and TLS behavior. For Claude Code, install it with:
76
+
77
+ ```bash
78
+ cp -r skills/arcq ~/.claude/skills/
79
+ ```
80
+
81
+ (Any agent that reads markdown instructions can use the same file.)
82
+
83
+ Prefer something lighter? Drop this into your agent's instructions (e.g. a `CLAUDE.md`) to teach it the workflow:
84
+
85
+ ```markdown
86
+ ## Querying ArcGIS with arcq
87
+
88
+ - Discover configured layers: `arcq layers --names`
89
+ - Inspect a layer's schema: `arcq fields <layer>`
90
+ - Query data: `arcq query <layer> "<where>" --quiet`
91
+ - `where` is ArcGIS SQL; string literals need SINGLE quotes, so double-quote
92
+ the shell argument: `arcq query parcels "STATUS = 'ACTIVE'" --quiet`
93
+ - Keep output small: `--limit <N>` caps rows, `--count` returns `{"count":N}`,
94
+ `--out-fields a,b,c` returns only those fields.
95
+ - stdout is pure JSON (pipe to `jq`); the summary goes to stderr.
96
+ - Exit codes: 0 = success (an empty `[]` is a valid answer), 1 = error,
97
+ 2 = token invalid/expired (fix with `arcq token set <token>`).
98
+ ```
99
+
100
+ ## Security
101
+
102
+ arcq **verifies TLS certificates by default.** Requests to a server with an untrusted or invalid certificate fail with a clear error naming the host.
103
+
104
+ Some ArcGIS deployments (typically local or on-premises servers) use self-signed certificates. For those trusted hosts you can disable certificate verification, in order of precedence:
105
+
106
+ 1. the `--insecure` flag on any command: `arcq --insecure list my-service`
107
+ 2. the `ARCQ_INSECURE=1` environment variable
108
+ 3. `"insecure": true` at the top level of `~/.arcq.json`
109
+
110
+ When insecure mode is active, arcq prints a single warning to stderr on every invocation:
111
+
112
+ ```
113
+ [arcq] WARNING: TLS certificate verification is disabled
114
+ ```
115
+
116
+ Verification is relaxed only for arcq's own requests (via a scoped `https.Agent`); it never sets `NODE_TLS_REJECT_UNAUTHORIZED` process-wide. Only use insecure mode against trusted hosts on trusted networks.
117
+
118
+ Two more hardening details:
119
+
120
+ - The auth token is stored at `~/.arcq-token` with file mode `600` (owner read/write only).
121
+ - Requests are sent as HTTP `POST` with form-encoded bodies, so the token is never placed in a URL query string where it would land in server or proxy access logs.
122
+
123
+ ## Configuration
124
+
125
+ arcq reads a JSON config file from `~/.arcq.json` by default. Override the path with the `ARCQ_CONFIG` environment variable.
126
+
127
+ ```json
128
+ {
129
+ "services": {
130
+ "my-service": "https://example.com/arcgis/rest/services/MyService/FeatureServer"
131
+ },
132
+ "layers": {
133
+ "parcels": "https://example.com/arcgis/rest/services/MyService/FeatureServer/0"
134
+ }
135
+ }
136
+ ```
137
+
138
+ - **`services`** - named shortcuts used by `arcq list` and `arcq refresh`
139
+ - **`layers`** - named shortcuts used by `arcq query <layer>`
140
+ - **`insecure`** - optional; set to `true` to disable TLS verification (see [Security](#security))
141
+
142
+ `services` and `layers` both accept a raw URL in place of a name.
143
+
144
+ ## Authentication
145
+
146
+ ```bash
147
+ arcq token set <token> # save a token to ~/.arcq-token (mode 600)
148
+ arcq token show # print the stored token
149
+ ```
150
+
151
+ If a token is stored it is sent with every request. Unauthenticated services work without one. The token is sent in the request body, not the URL.
152
+
153
+ ## Commands
154
+
155
+ ### `arcq services add <name> <url>`
156
+
157
+ Add a named service to the config. Creates `~/.arcq.json` if it doesn't exist yet.
158
+
159
+ ```bash
160
+ arcq services add my-service https://example.com/arcgis/rest/services/MyService/FeatureServer
161
+ ```
162
+
163
+ ### `arcq refresh`
164
+
165
+ Fetches the layer/table catalog for every service in the config and writes it to `~/.arcq-cache.json`. Run this once after updating the config, and again whenever services change.
166
+
167
+ ```bash
168
+ arcq refresh
169
+ ```
170
+
171
+ ### `arcq use [name]`
172
+
173
+ Selects the _active layer_ (saved to `~/.arcq-context.json`), used by subsequent `arcq query` and `arcq fields` calls.
174
+
175
+ With no argument, opens an interactive [fzf](https://github.com/junegunn/fzf) picker over the cached catalog (requires `fzf` on your `PATH`):
176
+
177
+ ```bash
178
+ arcq use
179
+ # [arcq] active layer set → Parcels
180
+ ```
181
+
182
+ With a name, selects non-interactively. The name is resolved in this order, first match wins:
183
+
184
+ 1. a config layer key (`my-service-parcels`)
185
+ 2. a cached `service:id` pair (`my-service:0`)
186
+ 3. an exact cached layer name (`Parcels`) - if the same name exists in multiple services, arcq errors and lists the qualified candidates
187
+
188
+ ```bash
189
+ arcq use my-service-parcels
190
+ arcq use my-service:0
191
+ arcq use Parcels
192
+ ```
193
+
194
+ An unknown name exits 1 with closest-match suggestions.
195
+
196
+ ### `arcq active`
197
+
198
+ Prints the active layer and its URL, or a `no active layer` line if none is set (exit 0 either way - absence is a valid answer).
199
+
200
+ ```bash
201
+ arcq active
202
+ # my-service:0 → Parcels
203
+ # https://example.com/arcgis/rest/services/MyService/FeatureServer/0
204
+ ```
205
+
206
+ ### `arcq fields [layer]`
207
+
208
+ Prints a layer's field metadata as a JSON array of `{name, type, alias, length}`. Defaults to the active layer; accepts a config layer name or raw URL.
209
+
210
+ ```bash
211
+ arcq fields
212
+ arcq fields my-service-parcels
213
+ # [
214
+ # { "name": "OBJECTID", "type": "esriFieldTypeOID", "alias": "OBJECTID" },
215
+ # { "name": "STATUS", "type": "esriFieldTypeString", "alias": "Status", "length": 50 },
216
+ # ...
217
+ # ]
218
+ ```
219
+
220
+ ### `arcq query`
221
+
222
+ Query a layer and print feature attributes as a JSON array. Results are automatically paginated (1000 records per page).
223
+
224
+ ```bash
225
+ # Query the active layer
226
+ arcq query "1=1"
227
+ arcq query "AREA > 1000"
228
+
229
+ # Query a named layer from config
230
+ arcq query parcels "1=1"
231
+
232
+ # Query a raw layer URL
233
+ arcq query https://example.com/.../FeatureServer/0 "STATUS = 'ACTIVE'"
234
+
235
+ # Shorthand - layer name or URL as the first argument
236
+ arcq parcels "1=1"
237
+ ```
238
+
239
+ Output is a JSON array of attribute objects:
240
+
241
+ ```json
242
+ [
243
+ { "OBJECTID": 1, "AREA": 2048, "STATUS": "ACTIVE" },
244
+ ...
245
+ ]
246
+ ```
247
+
248
+ A layer argument must be a config layer name or contain `://` to be treated as a raw URL - an unknown name exits 1 with closest-match suggestions instead of being sent as a URL. ArcGIS string literals use **single quotes** (double-quote the shell argument): `"STATUS = 'ACTIVE'"` works, `"STATUS = \"ACTIVE\""` is an ArcGIS syntax error and exits 1.
249
+
250
+ #### Query flags
251
+
252
+ ```bash
253
+ arcq query --out-fields OBJECTID,STATUS "1=1" # return only the listed fields
254
+ arcq query --limit 50 "1=1" # stop after 50 rows
255
+ arcq query --count "1=1" # print {"count":N} only
256
+ arcq query --order-by "NAME DESC" "1=1" # sort server-side
257
+ ```
258
+
259
+ `--count` makes a single `returnCountOnly` request; `--out-fields` and `--limit` are ignored when combined with it.
260
+
261
+ #### Global flags
262
+
263
+ `--insecure` is valid on any command and disables TLS certificate verification for arcq's own requests (see [Security](#security)).
264
+
265
+ #### Query summary
266
+
267
+ By default `arcq query` prints a short summary of what it queried to **stderr** - the resolved layer, the `where` clause, and the exact endpoint:
268
+
269
+ ```
270
+ ────────────────────────────────────────────────────────
271
+ layer: my-service:0 → Parcels
272
+ where: STATUS = 'ACTIVE'
273
+ endpoint: https://example.com/.../FeatureServer/0/query
274
+ ────────────────────────────────────────────────────────
275
+ ```
276
+
277
+ Because it goes to stderr, it never contaminates the JSON on stdout - `arcq query "1=1" | jq` works unchanged. Pass `-q` / `--quiet` to suppress it (useful in scripts):
278
+
279
+ ```bash
280
+ arcq query "1=1" --quiet # JSON only, no summary
281
+ ```
282
+
283
+ ### `arcq sync`
284
+
285
+ Fetches the layer/table catalog for every service in the config and rewrites the `layers` section of `~/.arcq.json` with auto-generated keys in the form `servicename-layername` (lowercase, hyphenated). Existing `layers` entries are overwritten.
286
+
287
+ ```bash
288
+ arcq sync
289
+ # [arcq] syncing my-service...
290
+ # [arcq] config layers updated
291
+ ```
292
+
293
+ After syncing you can query any layer by its generated name:
294
+
295
+ ```bash
296
+ arcq query my-service-parcels "1=1"
297
+ ```
298
+
299
+ ### `arcq layers`
300
+
301
+ List all named layers currently in the config.
302
+
303
+ ```bash
304
+ arcq layers
305
+ # my-service-parcels → https://example.com/.../FeatureServer/0
306
+ # my-service-roads → https://example.com/.../FeatureServer/1
307
+
308
+ arcq layers --names
309
+ # my-service-parcels
310
+ # my-service-roads
311
+ ```
312
+
313
+ ### `arcq list <service>`
314
+
315
+ List all layers and tables in a service, showing their IDs and names.
316
+
317
+ ```bash
318
+ arcq list my-service
319
+ # 0 → Parcels
320
+ # 1 → Addresses
321
+ # 2 → Boundaries
322
+
323
+ # Or use a raw URL
324
+ arcq list https://example.com/arcgis/rest/services/MyService/FeatureServer
325
+ ```
326
+
327
+ ### `arcq version`
328
+
329
+ Print the arcq version (also `--version` / `-V`).
330
+
331
+ ## Exit codes
332
+
333
+ | Code | Meaning |
334
+ | ---- | ---------------------------------------------------------------------------------- |
335
+ | 0 | success - an empty `[]` result is a real answer, not an error |
336
+ | 1 | error: bad where clause, unknown layer, no active layer, server or request failure |
337
+ | 2 | token invalid or expired - run `arcq token set` |
338
+
339
+ Errors print `error: <message>` to stderr. ArcGIS reports failures inside HTTP-200 response bodies; arcq surfaces those as errors instead of printing `[]`.
340
+
341
+ ## Typical workflow
342
+
343
+ ```bash
344
+ # 1. Add services to ~/.arcq.json
345
+ # 2. Populate the fzf cache and named layer shortcuts
346
+ arcq refresh && arcq sync
347
+
348
+ # 3a. Pick a layer interactively
349
+ arcq use
350
+
351
+ # 3b. Or query a named layer directly
352
+ arcq query my-service-parcels "STATUS = 'ACTIVE'" | jq '.[0]'
353
+ ```
354
+
355
+ ## Contributing
356
+
357
+ Bug reports and pull requests are welcome - see [CONTRIBUTING.md](CONTRIBUTING.md).
358
+
359
+ ## License
360
+
361
+ [MIT](LICENSE)
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ import { run } from '../index.js';
3
+ import { ArcqError } from '../lib/errors.js';
4
+ try {
5
+ await run(process.argv.slice(2));
6
+ }
7
+ catch (err) {
8
+ console.error(`error: ${err instanceof Error ? err.message : String(err)}`);
9
+ process.exitCode = err instanceof ArcqError ? err.exitCode : 1;
10
+ }
11
+ //# sourceMappingURL=arcq.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"arcq.js","sourceRoot":"","sources":["../../bin/arcq.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAClC,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,IAAI,CAAC;IACH,MAAM,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AACnC,CAAC;AAAC,OAAO,GAAG,EAAE,CAAC;IACb,OAAO,CAAC,KAAK,CAAC,UAAU,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC5E,OAAO,CAAC,QAAQ,GAAG,GAAG,YAAY,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;AACjE,CAAC"}
@@ -0,0 +1,5 @@
1
+ export declare function resolveInsecure(args: string[]): {
2
+ insecure: boolean;
3
+ args: string[];
4
+ };
5
+ export declare function run(rawArgs: string[]): Promise<void>;
package/dist/index.js ADDED
@@ -0,0 +1,85 @@
1
+ import tokenCmd from './lib/token-cmd.js';
2
+ import listCmd from './lib/list-cmd.js';
3
+ import layersCmd from './lib/layers-cmd.js';
4
+ import servicesCmd from './lib/services-cmd.js';
5
+ import queryCmd from './lib/query-cmd.js';
6
+ import fieldsCmd from './lib/fields-cmd.js';
7
+ import refreshCmd from './lib/refresh-cmd.js';
8
+ import syncCmd from './lib/sync-cmd.js';
9
+ import interactiveCmd from './lib/interactive-cmd.js';
10
+ import activeCmd from './lib/active-cmd.js';
11
+ import helpCmd from './lib/help-cmd.js';
12
+ import versionCmd from './lib/version-cmd.js';
13
+ import { loadConfig } from './lib/config-core.js';
14
+ import { setInsecureTls } from './lib/tls-core.js';
15
+ import { ArcqError } from './lib/errors.js';
16
+ // Query flags that may legally appear before the where clause in the
17
+ // `arcq -q "1=1"` shorthand; everything else dash-prefixed is an error here.
18
+ const QUERY_PASSTHROUGH_FLAGS = ['-q', '--quiet', '-v', '--verbose'];
19
+ // `--insecure` is a top-level flag valid for every command. It is resolved
20
+ // and stripped here so it never reaches a command's own flag parser or gets
21
+ // swallowed as a layer name or where clause. Precedence (highest first): the
22
+ // flag, then ARCQ_INSECURE=1, then "insecure": true in the config. Returns
23
+ // the remaining args with any --insecure occurrences removed.
24
+ export function resolveInsecure(args) {
25
+ const flag = args.includes('--insecure');
26
+ const env = process.env.ARCQ_INSECURE;
27
+ const envOn = env === '1' || env?.toLowerCase() === 'true';
28
+ let configOn = false;
29
+ try {
30
+ configOn = loadConfig().insecure === true;
31
+ }
32
+ catch {
33
+ // A malformed config is surfaced by the command that loads it; the TLS
34
+ // decision defaults to secure here.
35
+ }
36
+ return {
37
+ insecure: flag || envOn || configOn,
38
+ args: args.filter((a) => a !== '--insecure'),
39
+ };
40
+ }
41
+ export async function run(rawArgs) {
42
+ const { insecure, args } = resolveInsecure(rawArgs);
43
+ setInsecureTls(insecure);
44
+ if (insecure) {
45
+ // One warning per invocation, always on stderr, regardless of --quiet.
46
+ console.error('[arcq] WARNING: TLS certificate verification is disabled');
47
+ }
48
+ const command = args[0];
49
+ if (!command || command === '--help' || command === '-h') {
50
+ return helpCmd();
51
+ }
52
+ if (command === '--version' || command === '-V' || command === 'version') {
53
+ return versionCmd();
54
+ }
55
+ switch (command) {
56
+ case 'refresh':
57
+ return refreshCmd();
58
+ case 'sync':
59
+ return syncCmd();
60
+ case 'use':
61
+ return interactiveCmd(args.slice(1));
62
+ case 'active':
63
+ return activeCmd();
64
+ case 'query':
65
+ return queryCmd(args.slice(1));
66
+ case 'fields':
67
+ return fieldsCmd(args.slice(1));
68
+ case 'list':
69
+ return listCmd(args.slice(1));
70
+ case 'layers':
71
+ return layersCmd(args.slice(1));
72
+ case 'token':
73
+ return tokenCmd(args.slice(1));
74
+ case 'services':
75
+ return servicesCmd(args.slice(1));
76
+ default:
77
+ if (command.startsWith('-') &&
78
+ !QUERY_PASSTHROUGH_FLAGS.includes(command)) {
79
+ throw new ArcqError(`unknown flag '${command}' - run: arcq --help`);
80
+ }
81
+ // fallback: treat as query shorthand
82
+ return queryCmd(args);
83
+ }
84
+ }
85
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export default function activeCmd(): void;
@@ -0,0 +1,16 @@
1
+ import { loadContext } from './context-core.js';
2
+ // Prints the active layer. No context is a valid answer for this command,
3
+ // so it stays informational: stdout, exit 0.
4
+ export default function activeCmd() {
5
+ const ctx = loadContext();
6
+ if (!ctx) {
7
+ console.log('no active layer - run: arcq use <name>');
8
+ return;
9
+ }
10
+ const label = ctx.name && ctx.service != null && ctx.layerId != null
11
+ ? `${ctx.service}:${ctx.layerId} → ${ctx.name}`
12
+ : ctx.name || ctx.url;
13
+ console.log(label);
14
+ console.log(ctx.url);
15
+ }
16
+ //# sourceMappingURL=active-cmd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"active-cmd.js","sourceRoot":"","sources":["../../lib/active-cmd.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,0EAA0E;AAC1E,6CAA6C;AAC7C,MAAM,CAAC,OAAO,UAAU,SAAS;IAC/B,MAAM,GAAG,GAAG,WAAW,EAAE,CAAC;IAE1B,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,CAAC,GAAG,CAAC,wCAAwC,CAAC,CAAC;QACtD,OAAO;IACT,CAAC;IAED,MAAM,KAAK,GACT,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,OAAO,IAAI,IAAI,IAAI,GAAG,CAAC,OAAO,IAAI,IAAI;QACpD,CAAC,CAAC,GAAG,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,OAAO,MAAM,GAAG,CAAC,IAAI,EAAE;QAC/C,CAAC,CAAC,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,GAAG,CAAC;IAE1B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACnB,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AACvB,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { CatalogLayer, LayerField, QueryParams, QueryResponse } from './types.js';
2
+ export declare function validateToken(layerUrl: string, token: string | null): Promise<void>;
3
+ export declare function fetchServiceCatalog(serviceUrl: string, token: string | null): Promise<CatalogLayer[]>;
4
+ export declare function fetchLayerMetadata(layerUrl: string, token: string | null): Promise<LayerField[]>;
5
+ export declare function queryLayer(layerUrl: string, params: QueryParams): Promise<QueryResponse>;
@@ -0,0 +1,89 @@
1
+ import axios from 'axios';
2
+ import { ArcqError, tokenError } from './errors.js';
3
+ import { getHttpsAgent, tlsErrorMessage } from './tls-core.js';
4
+ // ArcGIS reports failures inside a 200 response body; surface them loudly.
5
+ function throwArcGisError(error) {
6
+ if (error.code === 498 || error.code === 499) {
7
+ throw tokenError();
8
+ }
9
+ throw new ArcqError(error.code != null
10
+ ? `ArcGIS error ${error.code}: ${error.message}`
11
+ : error.message);
12
+ }
13
+ // Params go in a form-encoded POST body rather than the URL query string, so
14
+ // the token never lands in server/proxy access logs. Null/undefined values
15
+ // are omitted rather than serialized as "null"/"undefined".
16
+ function formBody(params) {
17
+ const body = new URLSearchParams();
18
+ for (const [key, value] of Object.entries(params)) {
19
+ if (value === null || value === undefined)
20
+ continue;
21
+ body.append(key, String(value));
22
+ }
23
+ return body;
24
+ }
25
+ async function postForm(url, params) {
26
+ const agent = getHttpsAgent();
27
+ try {
28
+ const res = await axios.post(url, formBody(params), {
29
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
30
+ ...(agent ? { httpsAgent: agent } : {}),
31
+ });
32
+ return res.data;
33
+ }
34
+ catch (error) {
35
+ const tlsMessage = tlsErrorMessage(error, url);
36
+ throw tlsMessage ? new ArcqError(tlsMessage) : error;
37
+ }
38
+ }
39
+ export async function validateToken(layerUrl, token) {
40
+ const data = await postForm(layerUrl, {
41
+ f: 'json',
42
+ token,
43
+ });
44
+ if (data.error) {
45
+ throwArcGisError(data.error);
46
+ }
47
+ }
48
+ export async function fetchServiceCatalog(serviceUrl, token) {
49
+ const data = await postForm(serviceUrl, {
50
+ f: 'json',
51
+ token,
52
+ });
53
+ if (data.error) {
54
+ throwArcGisError(data.error);
55
+ }
56
+ const layers = data.layers || [];
57
+ const tables = data.tables || [];
58
+ return [...layers, ...tables].map((l) => ({
59
+ id: l.id,
60
+ name: l.name,
61
+ type: l.type || 'layer',
62
+ url: `${serviceUrl}/${l.id}`,
63
+ }));
64
+ }
65
+ export async function fetchLayerMetadata(layerUrl, token) {
66
+ const data = await postForm(layerUrl, {
67
+ f: 'json',
68
+ token,
69
+ });
70
+ if (data.error) {
71
+ throwArcGisError(data.error);
72
+ }
73
+ return (data.fields ?? []).map((f) => {
74
+ const field = { name: f.name, type: f.type };
75
+ if (f.alias != null)
76
+ field.alias = f.alias;
77
+ if (f.length != null)
78
+ field.length = f.length;
79
+ return field;
80
+ });
81
+ }
82
+ export async function queryLayer(layerUrl, params) {
83
+ const data = await postForm(`${layerUrl}/query`, params);
84
+ if (data.error) {
85
+ throwArcGisError(data.error);
86
+ }
87
+ return data;
88
+ }
89
+ //# sourceMappingURL=arcgis-core.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"arcgis-core.js","sourceRoot":"","sources":["../../lib/arcgis-core.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { Cache } from './types.js';
2
+ export declare const CACHE_PATH: string;
3
+ export declare function loadCache(): Cache;
4
+ export declare function saveCache(cache: Cache): void;
@@ -0,0 +1,13 @@
1
+ import fs from 'fs';
2
+ import os from 'os';
3
+ import path from 'path';
4
+ export const CACHE_PATH = path.join(os.homedir(), '.arcq-cache.json');
5
+ export function loadCache() {
6
+ if (!fs.existsSync(CACHE_PATH))
7
+ return {};
8
+ return JSON.parse(fs.readFileSync(CACHE_PATH, 'utf-8'));
9
+ }
10
+ export function saveCache(cache) {
11
+ fs.writeFileSync(CACHE_PATH, JSON.stringify(cache, null, 2));
12
+ }
13
+ //# sourceMappingURL=cache-core.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-core.js","sourceRoot":"","sources":["../../lib/cache-core.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAGxB,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,kBAAkB,CAAC,CAAC;AAEtE,MAAM,UAAU,SAAS;IACvB,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC;QAAE,OAAO,EAAE,CAAC;IAC1C,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAU,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAAY;IACpC,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAC/D,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { Config } from './types.js';
2
+ export declare function resolveConfigPath(): string;
3
+ export declare function loadConfig(): Config;
4
+ export declare function saveConfig(config: Config): void;