@saschabrunnerch/arcgis-maps-sdk-js-ai-context 0.2.0 → 0.4.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.
@@ -0,0 +1,109 @@
1
+ # Agent Guide: arcgis-authentication
2
+
3
+ Quick-reference decisions, checklists, and tables for authentication and authorization.
4
+
5
+ ## API Key vs OAuth vs Identity Manager Decision Tree
6
+
7
+ 1. **Does the app access only public basemaps, geocoding, or routing?**
8
+ - Yes --> **API Key** (simplest)
9
+
10
+ 2. **Does the app need to access the user's private content (web maps, feature services)?**
11
+ - Yes --> **OAuth 2.0** (user signs in)
12
+
13
+ 3. **Is this a server-side app with no user interaction?**
14
+ - Yes --> **App token** (client credentials OAuth flow, server-side only)
15
+
16
+ 4. **Is the app connecting to ArcGIS Enterprise (on-premise)?**
17
+ - Yes --> **OAuth 2.0** with custom `portalUrl`
18
+
19
+ 5. **Do you need to access multiple secured services and want automatic token management?**
20
+ - Yes --> **IdentityManager** handles this automatically with OAuth or registered tokens
21
+
22
+ | Approach | User Sign-In | Setup Complexity | Use Case |
23
+ |----------|-------------|-----------------|----------|
24
+ | API Key | No | Low | Public apps with basemaps/services |
25
+ | OAuth 2.0 (redirect) | Yes | Medium | Web apps needing user content |
26
+ | OAuth 2.0 (popup) | Yes | Medium | SPAs that stay on page |
27
+ | `<arcgis-oauth>` component | Yes | Low | Quick OAuth with Map Components |
28
+ | Registered token | No | Low | Testing / known tokens |
29
+
30
+ ## OAuth Setup Checklist
31
+
32
+ ### Register your app
33
+ - [ ] Go to [developers.arcgis.com](https://developers.arcgis.com) (or your Enterprise portal)
34
+ - [ ] Create a new OAuth application
35
+ - [ ] Note the **App ID** (client ID)
36
+ - [ ] Add **redirect URIs** for your app (e.g., `http://localhost:5173`, `https://yourapp.com`)
37
+ - [ ] For production: add **referrer restrictions** on the app registration
38
+
39
+ ### Implement in code
40
+ - [ ] Import `OAuthInfo` and `esriId` (IdentityManager)
41
+ - [ ] Create `OAuthInfo`: `new OAuthInfo({ appId: "YOUR_APP_ID" })`
42
+ - [ ] Set `popup: false` for redirect flow, `popup: true` for popup window
43
+ - [ ] Register: `esriId.registerOAuthInfos([oauthInfo])`
44
+ - [ ] Check existing sign-in: `esriId.checkSignInStatus(portalUrl + "/sharing")`
45
+ - [ ] Trigger sign-in: `esriId.getCredential(portalUrl + "/sharing")`
46
+ - [ ] Load portal: `new Portal({ authMode: "immediate" })` then `await portal.load()`
47
+
48
+ ### For Enterprise Portal
49
+ - [ ] Set `portalUrl` on OAuthInfo: `new OAuthInfo({ appId, portalUrl: "https://your-portal/portal" })`
50
+ - [ ] Or set globally: `esriConfig.portalUrl = "https://your-portal/portal"`
51
+
52
+ ### Using the OAuth Component
53
+ - [ ] Add `<arcgis-oauth app-id="YOUR_APP_ID">` to HTML
54
+ - [ ] Listen for `arcgisSignIn` and `arcgisSignOut` events
55
+ - [ ] Access credential from `event.detail.credential`
56
+
57
+ ## Security Checklist
58
+
59
+ ### Token handling
60
+ - [ ] Never commit API keys or tokens to source control
61
+ - [ ] Use environment variables (e.g., `VITE_ARCGIS_API_KEY`) for API keys
62
+ - [ ] For OAuth: tokens are managed by IdentityManager (no manual storage needed)
63
+ - [ ] Do not store tokens in `localStorage` for production apps
64
+
65
+ ### CORS and referrers
66
+ - [ ] Configure `esriConfig.request.trustedServers` for servers that should receive credentials
67
+ - [ ] Set up proxy (`esriConfig.request.proxyUrl`) for services that do not support CORS
68
+ - [ ] Configure **allowed referrers** on your API key at developers.arcgis.com
69
+ - [ ] For Enterprise: ensure CORS is enabled on the portal and services
70
+
71
+ ### OAuth security
72
+ - [ ] Use HTTPS in production (OAuth redirects require it)
73
+ - [ ] Register only the redirect URIs you actually use
74
+ - [ ] Use `popup: false` (redirect flow) when popup blockers are a concern
75
+ - [ ] Handle token expiration: IdentityManager auto-refreshes, but test edge cases
76
+
77
+ ### API Key scoping
78
+ - [ ] Generate API keys with minimum required privileges (only basemaps, only geocoding, etc.)
79
+ - [ ] Set referrer restrictions to limit where the key can be used
80
+ - [ ] Use separate keys for development and production
81
+ - [ ] Rotate keys periodically
82
+
83
+ ## Sign-In / Sign-Out Flow Summary
84
+
85
+ ```
86
+ App loads
87
+ --> checkSignInStatus()
88
+ --> Signed in? Load Portal, display user info
89
+ --> Not signed in? Show sign-in button
90
+ --> User clicks sign-in
91
+ --> getCredential() triggers OAuth redirect/popup
92
+ --> User authenticates at ArcGIS
93
+ --> Redirect back with token
94
+ --> Load Portal, display user info
95
+
96
+ Sign out:
97
+ --> esriId.destroyCredentials()
98
+ --> window.location.reload()
99
+ ```
100
+
101
+ ## Common Token Errors
102
+
103
+ | Error | Likely Cause | Fix |
104
+ |-------|-------------|-----|
105
+ | `identity-manager:not-authorized` | Missing or expired token | Trigger re-authentication |
106
+ | `403 Forbidden` | API key lacks required privilege | Add privilege at developers.arcgis.com |
107
+ | `498 Invalid Token` | Token expired or revoked | Refresh or re-authenticate |
108
+ | `499 Token Required` | Secured service, no token provided | Register OAuthInfo or set API key |
109
+ | CORS error on token request | Portal not configured for CORS | Add to `trustedServers` or use proxy |
@@ -292,11 +292,48 @@ esriConfig.request.proxyRules.push({
292
292
 
293
293
  1. **App ID registration**: App ID must be registered with correct redirect URIs
294
294
 
295
- 2. **Popup blockers**: OAuth popups may be blocked - use redirect flow as fallback
295
+ 2. **Popup blockers**: OAuth popup-based sign-in fails on mobile browsers.
296
+
297
+ ```javascript
298
+ // Anti-pattern: using popup flow on mobile
299
+ OAuthInfo.create({
300
+ appId: "YOUR_APP_ID",
301
+ popup: true // Blocked by most mobile browsers
302
+ });
303
+ ```
304
+
305
+ ```javascript
306
+ // Correct: use redirect flow for mobile, popup for desktop
307
+ const isMobile = /iPhone|iPad|Android/i.test(navigator.userAgent);
308
+ OAuthInfo.create({
309
+ appId: "YOUR_APP_ID",
310
+ popup: !isMobile // Redirect flow on mobile, popup on desktop
311
+ });
312
+ ```
313
+
314
+ **Impact:** On mobile browsers (Safari, Chrome for iOS/Android), the OAuth popup is silently blocked. The user sees no sign-in dialog and cannot authenticate, with no error message explaining why.
296
315
 
297
316
  3. **Token expiration**: Tokens expire - handle refresh or re-authentication
298
317
 
299
318
  4. **CORS errors**: Configure trusted servers or use proxy for cross-origin
300
319
 
301
- 5. **Portal URL mismatch**: Ensure portal URL matches exactly (trailing slashes matter)
320
+ 5. **Portal URL trailing slash**: A trailing slash in the portal URL causes token validation to fail.
321
+
322
+ ```javascript
323
+ // Anti-pattern: portal URL with trailing slash
324
+ const portal = new Portal({
325
+ url: "https://myorg.maps.arcgis.com/" // Trailing slash
326
+ });
327
+ await portal.load(); // Token validation may fail
328
+ ```
329
+
330
+ ```javascript
331
+ // Correct: portal URL without trailing slash
332
+ const portal = new Portal({
333
+ url: "https://myorg.maps.arcgis.com" // No trailing slash
334
+ });
335
+ await portal.load();
336
+ ```
337
+
338
+ **Impact:** The portal URL with a trailing slash does not match the token's server URL during validation. Authentication silently fails, or the user is prompted to sign in repeatedly because the token is never accepted.
302
339
 
@@ -0,0 +1,86 @@
1
+ # Agent Guide: arcgis-core-maps
2
+
3
+ Quick-reference decisions, checklists, and tables for creating 2D/3D maps.
4
+
5
+ ## Map Components vs Core API Decision Tree
6
+
7
+ 1. **Are you starting a new project?**
8
+ - Yes --> Use Map Components (`<arcgis-map>` / `<arcgis-scene>`)
9
+ - No, maintaining existing Core API code --> Stay with Core API
10
+
11
+ 2. **Do you need programmatic control over view construction timing?**
12
+ - Yes (e.g., dynamic container, conditional rendering) --> Core API (`new MapView()`)
13
+ - No --> Map Components
14
+
15
+ 3. **Are you using a framework?**
16
+ - React 19 / Angular / Vue --> Map Components work as custom elements
17
+ - Older React (<19) --> Core API (custom element event handling is limited)
18
+
19
+ 4. **Do you need to embed the map inside a Calcite shell layout?**
20
+ - Yes --> Map Components with `reference-element` attribute for external widgets
21
+ - No --> Either approach works
22
+
23
+ **Default:** Map Components unless you have a specific reason for Core API.
24
+
25
+ ## Setup Checklist
26
+
27
+ ### Map Components (npm)
28
+ - [ ] `npm install @arcgis/map-components @esri/calcite-components`
29
+ - [ ] Import components: `import "@arcgis/map-components/dist/components/arcgis-map"`
30
+ - [ ] Set Calcite asset path: `setAssetPath("https://js.arcgis.com/calcite-components/3.3.3/assets")`
31
+ - [ ] Set API key: `esriConfig.apiKey = "YOUR_KEY"`
32
+ - [ ] Ensure `html, body { height: 100%; margin: 0; }` in CSS
33
+ - [ ] Set `"moduleResolution": "bundler"` in tsconfig.json
34
+ - [ ] Use `arcgisViewReadyChange` event before accessing `view`
35
+
36
+ ### Core API (npm)
37
+ - [ ] `npm install @arcgis/core`
38
+ - [ ] Import CSS: `@import "@arcgis/core/assets/esri/themes/light/main.css"`
39
+ - [ ] Create container div with full height: `#viewDiv { height: 100%; }`
40
+ - [ ] Import classes: `import Map from "@arcgis/core/Map.js"`
41
+ - [ ] Set API key: `esriConfig.apiKey = "YOUR_KEY"`
42
+ - [ ] Await `view.when()` before accessing view properties
43
+
44
+ ### Map Components (CDN)
45
+ - [ ] Add Calcite script: `<script type="module" src="https://js.arcgis.com/calcite-components/3.3.3/calcite.esm.js"></script>`
46
+ - [ ] Add SDK script: `<script src="https://js.arcgis.com/4.34/"></script>`
47
+ - [ ] Add components script: `<script type="module" src="https://js.arcgis.com/4.34/map-components/"></script>`
48
+ - [ ] Use `<arcgis-map>` or `<arcgis-scene>` in HTML
49
+ - [ ] Use `$arcgis.import()` for dynamic imports inside `<script type="module">`
50
+
51
+ ### Core API (CDN)
52
+ - [ ] Add CSS: `<link rel="stylesheet" href="https://js.arcgis.com/4.34/esri/themes/light/main.css" />`
53
+ - [ ] Add SDK script: `<script src="https://js.arcgis.com/4.34/"></script>`
54
+ - [ ] Create `#viewDiv` with full height
55
+ - [ ] Use `<script type="module">` for async/await support
56
+
57
+ ## CDN vs npm Comparison
58
+
59
+ | Aspect | CDN | npm + Build Tool |
60
+ |--------|-----|-----------------|
61
+ | Setup speed | Fastest (3 script tags) | Requires project scaffolding |
62
+ | Tree-shaking | No (loads full SDK) | Yes (smaller bundles) |
63
+ | TypeScript | No type checking | Full type support |
64
+ | Offline dev | No | Yes |
65
+ | Production use | Prototypes and demos | Recommended |
66
+ | Import style | `$arcgis.import()` | `import X from "@arcgis/core/..."` |
67
+ | CSS handling | Manual link tag (Core API) | `@import` in CSS (Core API), automatic (Components) |
68
+ | Versioning | URL-pinned (`/4.34/`) | `package.json` pinned |
69
+
70
+ ## Quick Reference: Coordinate Order
71
+
72
+ ArcGIS uses **[longitude, latitude]**, not [latitude, longitude].
73
+
74
+ ```
75
+ center: [-118.24, 34.05] // Los Angeles: longitude first, latitude second
76
+ ```
77
+
78
+ ## Quick Reference: View Containers
79
+
80
+ | Container | Dimension | Class |
81
+ |-----------|-----------|-------|
82
+ | `<arcgis-map>` | 2D | Web component (MapView internally) |
83
+ | `<arcgis-scene>` | 3D | Web component (SceneView internally) |
84
+ | `<arcgis-video>` | Video | Web component (VideoView internally) |
85
+ | `new MapView()` | 2D | Core API |
86
+ | `new SceneView()` | 3D | Core API |
@@ -733,18 +733,67 @@ reactiveUtils.when(
733
733
 
734
734
  ## Common Pitfalls
735
735
 
736
- 1. **Missing CSS for Core API**: The Core API requires `main.css`:
736
+ 1. **Missing CSS for Core API**: The Core API requires `main.css` for widgets and popups to render correctly.
737
+
738
+ ```html
739
+ <!-- Anti-pattern: no CSS import -->
740
+ <script type="module">
741
+ import MapView from "@arcgis/core/views/MapView";
742
+ import Map from "@arcgis/core/Map";
743
+ const view = new MapView({ container: "viewDiv", map: new Map({ basemap: "topo-vector" }) });
744
+ </script>
745
+ ```
746
+
737
747
  ```html
748
+ <!-- Correct: include the CSS -->
738
749
  <link rel="stylesheet" href="https://js.arcgis.com/4.34/esri/themes/light/main.css" />
750
+ <script type="module">
751
+ import MapView from "@arcgis/core/views/MapView";
752
+ import Map from "@arcgis/core/Map";
753
+ const view = new MapView({ container: "viewDiv", map: new Map({ basemap: "topo-vector" }) });
754
+ </script>
739
755
  ```
740
756
 
741
- 2. **Not awaiting viewOnReady()**: Always wait for the view before accessing properties:
757
+ **Impact:** The map itself renders, but widgets (Zoom, Legend, Search) and popups appear unstyled or completely broken. Layouts collapse and controls become unusable.
758
+
759
+ 2. **Not awaiting viewOnReady()**: View properties are not available until the view is ready.
760
+
742
761
  ```javascript
762
+ // Anti-pattern: accessing view before it is ready
763
+ const mapElement = document.querySelector("arcgis-map");
764
+ const view = mapElement.view; // undefined - view is not ready yet
765
+ view.goTo({ center: [-118, 34] }); // TypeError: Cannot read properties of undefined
766
+ ```
767
+
768
+ ```javascript
769
+ // Correct: wait for the view to be ready
770
+ const mapElement = document.querySelector("arcgis-map");
743
771
  await mapElement.viewOnReady();
744
- const view = mapElement.view; // Safe to access now
772
+ const view = mapElement.view; // MapView instance, safe to use
773
+ view.goTo({ center: [-118, 34] });
774
+ ```
775
+
776
+ **Impact:** `view` is `null` or `undefined` before the component initializes, causing runtime errors on any property access or method call.
777
+
778
+ 3. **Coordinate order**: ArcGIS uses `[longitude, latitude]`, not `[latitude, longitude]`.
779
+
780
+ ```javascript
781
+ // Anti-pattern: lat/lng order (Google Maps convention)
782
+ const view = new MapView({
783
+ center: [34.05, -118.24], // lat first, lng second - WRONG
784
+ zoom: 12
785
+ });
786
+ ```
787
+
788
+ ```javascript
789
+ // Correct: lng/lat order (ArcGIS convention)
790
+ const view = new MapView({
791
+ center: [-118.24, 34.05], // lng first, lat second
792
+ zoom: 12
793
+ });
745
794
  ```
746
795
 
747
- 3. **Coordinate order**: ArcGIS uses `[longitude, latitude]`, not `[latitude, longitude]`
796
+ **Impact:** The map centers on the wrong location, often in the middle of the ocean or on a different continent, with no error message to indicate the mistake.
748
797
 
749
798
  4. **Missing viewDiv height**: Ensure the container has height:
750
799
  ```css
@@ -0,0 +1,98 @@
1
+ # Agent Guide: arcgis-editing
2
+
3
+ Quick-reference decisions, checklists, and tables for feature editing and versioning.
4
+
5
+ ## Editor Component vs Core API Decision
6
+
7
+ 1. **Do you need a basic editing UI with default behavior?**
8
+ - Yes --> `<arcgis-editor slot="top-right">` (Map Component)
9
+ - No, need custom form layout or logic --> Core API `Editor` widget or `FeatureForm`
10
+
11
+ 2. **Do you need a standalone form (not inside the Editor panel)?**
12
+ - Yes --> `FeatureForm` widget or `<arcgis-feature-form>` component
13
+ - No --> Editor handles forms internally
14
+
15
+ 3. **Do you need programmatic edits without any UI?**
16
+ - Yes --> `featureLayer.applyEdits()` directly
17
+
18
+ 4. **Do you need version management?**
19
+ - Yes --> `VersionManagementService` + `<arcgis-version-management>` component
20
+
21
+ | Approach | UI Provided | Geometry Editing | Form Editing | Flexibility |
22
+ |----------|------------|-----------------|--------------|-------------|
23
+ | `<arcgis-editor>` component | Full | Yes | Yes | Low (defaults) |
24
+ | `Editor` widget (Core API) | Full | Yes | Yes | High (layerInfos config) |
25
+ | `FeatureForm` widget | Form only | No | Yes | High (custom form layout) |
26
+ | `applyEdits()` | None | Via code | Via code | Maximum |
27
+
28
+ ## Edit Workflow Steps
29
+
30
+ ### Interactive editing (Editor widget)
31
+ 1. **Setup** -- Add Editor to view with `layerInfos` config
32
+ 2. **Create** -- User selects template, draws geometry, fills form
33
+ 3. **Update** -- User clicks feature, modifies geometry or attributes
34
+ 4. **Delete** -- User selects feature and deletes
35
+
36
+ ### Programmatic editing (applyEdits)
37
+ 1. **Create Graphic** -- Build `Graphic` with geometry and attributes
38
+ 2. **Call applyEdits** -- `layer.applyEdits({ addFeatures: [graphic] })`
39
+ 3. **Handle result** -- Check `result.addFeatureResults` for success/errors
40
+ 4. **Refresh** -- Layer auto-refreshes; call `layer.refresh()` if needed
41
+
42
+ ### Form-based editing (FeatureForm)
43
+ 1. **Create form** -- `new FeatureForm({ layer, formTemplate })`
44
+ 2. **Set feature** -- `form.feature = graphic`
45
+ 3. **User edits** -- Form fields are populated and editable
46
+ 4. **Validate** -- Check `form.valid` before proceeding
47
+ 5. **Get values** -- `form.getValues()` returns updated attributes
48
+ 6. **Apply** -- `layer.applyEdits({ updateFeatures: [graphic] })`
49
+
50
+ ## Version Management Checklist
51
+
52
+ ### Setup
53
+ - [ ] Have a branch-versioned feature service
54
+ - [ ] Create `VersionManagementService` with the service URL
55
+ - [ ] Await `vms.load()`
56
+
57
+ ### Create and edit in a version
58
+ - [ ] Create version: `vms.createVersion({ versionName, access: "private" })`
59
+ - [ ] Switch layer: `featureLayer.gdbVersion = version.versionName`
60
+ - [ ] Refresh layer: `await featureLayer.refresh()`
61
+ - [ ] Start edit session: `await vms.startEditing({ versionIdentifier })`
62
+ - [ ] Apply edits: `await featureLayer.applyEdits(edits)`
63
+ - [ ] Reconcile: `await vms.reconcile({ versionIdentifier, conflictResolution: "favorEditVersion" })`
64
+ - [ ] Check for conflicts: `reconcileResult.hasConflicts`
65
+ - [ ] Post to parent: `await vms.post({ versionIdentifier })`
66
+ - [ ] Stop editing: `await vms.stopEditing({ versionIdentifier, saveEdits: true })`
67
+
68
+ ### Cleanup
69
+ - [ ] Switch layer back: `featureLayer.gdbVersion = vms.defaultVersionIdentifier`
70
+ - [ ] Delete temp version: `await vms.deleteVersion({ versionName })`
71
+
72
+ ## Form Element Types
73
+
74
+ | Type | Purpose |
75
+ |------|---------|
76
+ | `field` | Single editable field |
77
+ | `group` | Group of fields under a heading |
78
+
79
+ ### Field Element Expression Types
80
+
81
+ | Expression Property | Controls |
82
+ |-------------------|----------|
83
+ | `visibilityExpression` | Whether the field is visible |
84
+ | `editableExpression` | Whether the field is editable |
85
+ | `requiredExpression` | Whether the field is required |
86
+ | `valueExpression` | Computed default value |
87
+
88
+ All expressions use Arcade syntax and must return `true`/`false`.
89
+
90
+ ## applyEdits Operations
91
+
92
+ | Operation | Property | Value |
93
+ |-----------|----------|-------|
94
+ | Add features | `addFeatures` | Array of Graphics |
95
+ | Update features | `updateFeatures` | Array of Graphics (with existing OID) |
96
+ | Delete features | `deleteFeatures` | Array of Graphics or `[{ objectId: N }]` |
97
+
98
+ All three can be combined in a single `applyEdits()` call.
@@ -517,6 +517,42 @@ if (form.valid) {
517
517
 
518
518
  ## Complete Example
519
519
 
520
+ ### Map Components (Recommended)
521
+
522
+ ```html
523
+ <!DOCTYPE html>
524
+ <html>
525
+ <head>
526
+ <script type="module" src="https://js.arcgis.com/calcite-components/3.3.3/calcite.esm.js"></script>
527
+ <script src="https://js.arcgis.com/4.34/"></script>
528
+ <script type="module" src="https://js.arcgis.com/4.34/map-components/"></script>
529
+ <style>
530
+ html, body { height: 100%; margin: 0; }
531
+ </style>
532
+ </head>
533
+ <body>
534
+ <arcgis-map basemap="streets-navigation-vector">
535
+ <arcgis-zoom slot="top-left"></arcgis-zoom>
536
+ <arcgis-editor slot="top-right"></arcgis-editor>
537
+ </arcgis-map>
538
+
539
+ <script type="module">
540
+ import FeatureLayer from "@arcgis/core/layers/FeatureLayer.js";
541
+
542
+ const mapElement = document.querySelector("arcgis-map");
543
+ await mapElement.viewOnReady();
544
+
545
+ const layer = new FeatureLayer({
546
+ url: "https://services.arcgis.com/.../FeatureServer/0"
547
+ });
548
+ mapElement.map.add(layer);
549
+ </script>
550
+ </body>
551
+ </html>
552
+ ```
553
+
554
+ ### Core API
555
+
520
556
  ```html
521
557
  <!DOCTYPE html>
522
558
  <html>
@@ -525,7 +561,6 @@ if (form.valid) {
525
561
  <script src="https://js.arcgis.com/4.34/"></script>
526
562
  <style>
527
563
  html, body, #viewDiv { height: 100%; margin: 0; }
528
- #formPanel { position: absolute; top: 10px; right: 10px; width: 300px; }
529
564
  </style>
530
565
  <script type="module">
531
566
  import Map from "@arcgis/core/Map.js";
@@ -617,11 +652,63 @@ const editor = new Editor({
617
652
 
618
653
  ## Common Pitfalls
619
654
 
620
- 1. **Editing permissions**: User must have edit permissions on the layer
655
+ 1. **Editing permissions**: Calling `applyEdits` on a layer without checking edit capabilities causes silent failures.
656
+
657
+ ```javascript
658
+ // Anti-pattern: calling applyEdits without checking capabilities
659
+ const layer = new FeatureLayer({ url: "https://services.arcgis.com/.../FeatureServer/0" });
660
+ await layer.applyEdits({
661
+ addFeatures: [newFeature]
662
+ });
663
+ // May fail silently or throw a cryptic server error
664
+ ```
665
+
666
+ ```javascript
667
+ // Correct: check capabilities before editing
668
+ const layer = new FeatureLayer({ url: "https://services.arcgis.com/.../FeatureServer/0" });
669
+ await layer.load();
670
+ if (layer.editingEnabled && layer.capabilities?.editing?.supportsAddingFeatures) {
671
+ await layer.applyEdits({
672
+ addFeatures: [newFeature]
673
+ });
674
+ } else {
675
+ console.error("Layer does not support adding features");
676
+ }
677
+ ```
678
+
679
+ **Impact:** The `applyEdits` call fails with a cryptic server error or silently returns an error result that is easy to miss, leaving the user believing the edit was saved when it was not.
621
680
 
622
681
  2. **Subtype field**: Must match the subtype configuration in the service
623
682
 
624
- 3. **Version locking**: Branch versions may lock during editing sessions
683
+ 3. **Version locking**: Editing branch-versioned data without proper session management causes version locks.
684
+
685
+ ```javascript
686
+ // Anti-pattern: editing without version management session
687
+ const layer = new FeatureLayer({ url: "https://services.arcgis.com/.../FeatureServer/0" });
688
+ layer.gdbVersion = "editor1.design_v1";
689
+ await layer.applyEdits({ updateFeatures: [updatedFeature] });
690
+ // Version remains locked, blocking other users
691
+ ```
692
+
693
+ ```javascript
694
+ // Correct: start and stop version management session
695
+ const versionManagement = new VersionManagementService({
696
+ url: "https://services.arcgis.com/.../VersionManagementServer"
697
+ });
698
+ const versionName = "editor1.design_v1";
699
+
700
+ // Start editing session
701
+ await versionManagement.startEditing(versionName);
702
+
703
+ const layer = new FeatureLayer({ url: "https://services.arcgis.com/.../FeatureServer/0" });
704
+ layer.gdbVersion = versionName;
705
+ await layer.applyEdits({ updateFeatures: [updatedFeature] });
706
+
707
+ // Stop editing session to release the lock
708
+ await versionManagement.stopEditing(versionName, true); // true = save edits
709
+ ```
710
+
711
+ **Impact:** The version stays locked after editing, preventing other users from accessing or editing it. Accumulated locks require administrator intervention to release.
625
712
 
626
713
  4. **Validation expressions**: Must return true/false or error object
627
714
 
@@ -0,0 +1,108 @@
1
+ # Agent Guide: arcgis-interaction
2
+
3
+ Quick-reference decisions, checklists, and tables for user interaction patterns.
4
+
5
+ ## Hit Test vs Query vs Popup Decision Matrix
6
+
7
+ | Need | Approach | Method |
8
+ |------|----------|--------|
9
+ | User clicks a feature and sees info | Popup (built-in) | Set `popupTemplate` on layer, it works automatically |
10
+ | User clicks and you run custom logic | Hit test | `view.hitTest(event)` in a click handler |
11
+ | Find all features in an area | Spatial query | `featureLayer.queryFeatures(query)` |
12
+ | Highlight features by attribute | Attribute query + highlight | `queryObjectIds()` then `layerView.highlight(oids)` |
13
+ | Show a cursor change on hover | Hit test on pointer-move | `view.hitTest(event)` in a pointer-move handler |
14
+ | Draw a shape and select features in it | Sketch + spatial query | Sketch widget for drawing, then query with the geometry |
15
+
16
+ ### When to use each:
17
+
18
+ - **Popup** -- Simplest. Just configure `popupTemplate` on the layer. No code for the interaction itself.
19
+ - **Hit test** -- Returns the graphic at a screen point. Use when you need the actual feature object for custom behavior (not just a popup).
20
+ - **Server query** -- Returns features matching SQL/spatial criteria from the service. Use for bulk operations or features not visible on screen.
21
+ - **Client query** -- `layerView.queryFeatures()` queries only loaded features. Faster but limited to what is in the view.
22
+
23
+ ## Click-to-Select Implementation Checklist
24
+
25
+ - [ ] Get a reference to the target `FeatureLayer`
26
+ - [ ] Get the LayerView: `const layerView = await view.whenLayerView(featureLayer)`
27
+ - [ ] Store a highlight handle variable: `let highlightHandle = null`
28
+ - [ ] Add click handler: `view.on("click", async (event) => { ... })`
29
+ - [ ] Inside handler, run hit test: `const response = await view.hitTest(event, { include: [featureLayer] })`
30
+ - [ ] Remove previous highlight: `highlightHandle?.remove()`
31
+ - [ ] If results found, highlight: `highlightHandle = layerView.highlight(graphic)`
32
+ - [ ] Optionally open popup or update side panel with feature attributes
33
+
34
+ ## Cleanup Pattern
35
+
36
+ Always clean up handles and event listeners to prevent memory leaks.
37
+
38
+ ### Handle-based cleanup (highlight, watch)
39
+ ```
40
+ const handle = layerView.highlight(graphic);
41
+ // Later:
42
+ handle.remove();
43
+ ```
44
+
45
+ ### Event listener cleanup
46
+ ```
47
+ const handle = view.on("click", handler);
48
+ // Later:
49
+ handle.remove();
50
+ ```
51
+
52
+ ### reactiveUtils cleanup
53
+ ```
54
+ const handle = reactiveUtils.watch(() => view.zoom, callback);
55
+ // Later:
56
+ handle.remove();
57
+ ```
58
+
59
+ ### Destroy pattern (widgets)
60
+ ```
61
+ widget.destroy(); // Removes from DOM and cleans up
62
+ ```
63
+
64
+ ### Checklist for cleanup
65
+ - [ ] Store all `view.on()` return handles
66
+ - [ ] Store all `reactiveUtils.watch()` / `when()` / `once()` return handles
67
+ - [ ] Store all `layerView.highlight()` return handles
68
+ - [ ] Call `.remove()` on handles when the interaction is no longer needed
69
+ - [ ] Call `.destroy()` on widgets when removing them
70
+ - [ ] In frameworks (React/Angular): clean up in unmount/destroy lifecycle hooks
71
+
72
+ ## Event Types Quick Reference
73
+
74
+ | Event | Fires when | Common use |
75
+ |-------|-----------|------------|
76
+ | `click` | User clicks the map | Feature selection, custom popups |
77
+ | `double-click` | User double-clicks | Override default zoom behavior |
78
+ | `pointer-move` | Mouse moves over map | Hover highlighting, coordinate display |
79
+ | `pointer-down` / `pointer-up` | Mouse button pressed/released | Custom drag interactions |
80
+ | `drag` | User drags the map | Custom pan behavior |
81
+ | `key-down` / `key-up` | Keyboard input while map focused | Keyboard shortcuts |
82
+ | `hold` | Long press | Mobile context menu |
83
+
84
+ ### Preventing Default Behavior
85
+ ```
86
+ view.on("double-click", (event) => {
87
+ event.stopPropagation(); // Prevents default zoom-in
88
+ });
89
+ ```
90
+
91
+ ## Coordinate Conversion Quick Reference
92
+
93
+ | Direction | Method |
94
+ |-----------|--------|
95
+ | Screen (pixels) to Map (geographic) | `view.toMap({ x, y })` |
96
+ | Map (geographic) to Screen (pixels) | `view.toScreen(mapPoint)` |
97
+ | Click event to Map point | `event.mapPoint` (already available) |
98
+ | Pointer event to Map point | `view.toMap(event)` (must convert) |
99
+
100
+ ## Highlight Options
101
+
102
+ | Property | Default | Purpose |
103
+ |----------|---------|---------|
104
+ | `color` | `[0, 255, 255, 1]` (cyan) | Highlight outline/halo color |
105
+ | `haloOpacity` | `1` | Opacity of the halo around features |
106
+ | `fillOpacity` | `0.25` | Opacity of the fill inside features |
107
+
108
+ Set on `layerView.highlightOptions` or at the `MapView` / `SceneView` level.