@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.
- package/LICENSE +21 -0
- package/README.md +361 -0
- package/dist/bin/arcq.d.ts +2 -0
- package/dist/bin/arcq.js +11 -0
- package/dist/bin/arcq.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +85 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/active-cmd.d.ts +1 -0
- package/dist/lib/active-cmd.js +16 -0
- package/dist/lib/active-cmd.js.map +1 -0
- package/dist/lib/arcgis-core.d.ts +5 -0
- package/dist/lib/arcgis-core.js +89 -0
- package/dist/lib/arcgis-core.js.map +1 -0
- package/dist/lib/cache-core.d.ts +4 -0
- package/dist/lib/cache-core.js +13 -0
- package/dist/lib/cache-core.js.map +1 -0
- package/dist/lib/config-core.d.ts +4 -0
- package/dist/lib/config-core.js +21 -0
- package/dist/lib/config-core.js.map +1 -0
- package/dist/lib/context-core.d.ts +3 -0
- package/dist/lib/context-core.js +13 -0
- package/dist/lib/context-core.js.map +1 -0
- package/dist/lib/errors.d.ts +5 -0
- package/dist/lib/errors.js +13 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/fields-cmd.d.ts +1 -0
- package/dist/lib/fields-cmd.js +22 -0
- package/dist/lib/fields-cmd.js.map +1 -0
- package/dist/lib/help-cmd.d.ts +1 -0
- package/dist/lib/help-cmd.js +71 -0
- package/dist/lib/help-cmd.js.map +1 -0
- package/dist/lib/interactive-cmd.d.ts +1 -0
- package/dist/lib/interactive-cmd.js +90 -0
- package/dist/lib/interactive-cmd.js.map +1 -0
- package/dist/lib/layer-resolve.d.ts +6 -0
- package/dist/lib/layer-resolve.js +34 -0
- package/dist/lib/layer-resolve.js.map +1 -0
- package/dist/lib/layers-cmd.d.ts +1 -0
- package/dist/lib/layers-cmd.js +15 -0
- package/dist/lib/layers-cmd.js.map +1 -0
- package/dist/lib/list-cmd.d.ts +1 -0
- package/dist/lib/list-cmd.js +19 -0
- package/dist/lib/list-cmd.js.map +1 -0
- package/dist/lib/query-cmd.d.ts +1 -0
- package/dist/lib/query-cmd.js +130 -0
- package/dist/lib/query-cmd.js.map +1 -0
- package/dist/lib/refresh-cmd.d.ts +1 -0
- package/dist/lib/refresh-cmd.js +16 -0
- package/dist/lib/refresh-cmd.js.map +1 -0
- package/dist/lib/services-cmd.d.ts +1 -0
- package/dist/lib/services-cmd.js +20 -0
- package/dist/lib/services-cmd.js.map +1 -0
- package/dist/lib/sync-cmd.d.ts +1 -0
- package/dist/lib/sync-cmd.js +25 -0
- package/dist/lib/sync-cmd.js.map +1 -0
- package/dist/lib/tls-core.d.ts +4 -0
- package/dist/lib/tls-core.js +47 -0
- package/dist/lib/tls-core.js.map +1 -0
- package/dist/lib/token-cmd.d.ts +1 -0
- package/dist/lib/token-cmd.js +22 -0
- package/dist/lib/token-cmd.js.map +1 -0
- package/dist/lib/token-core.d.ts +3 -0
- package/dist/lib/token-core.js +18 -0
- package/dist/lib/token-core.js.map +1 -0
- package/dist/lib/types.d.ts +65 -0
- package/dist/lib/types.js +3 -0
- package/dist/lib/types.js.map +1 -0
- package/dist/lib/version-cmd.d.ts +2 -0
- package/dist/lib/version-cmd.js +22 -0
- package/dist/lib/version-cmd.js.map +1 -0
- 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
|
+
[](https://github.com/jameseaster/arcq/actions/workflows/lint-and-test.yml)
|
|
4
|
+
[](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)
|
package/dist/bin/arcq.js
ADDED
|
@@ -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"}
|
package/dist/index.d.ts
ADDED
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,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"}
|