{"_id":"@cairncms/extension-address-completion","name":"@cairncms/extension-address-completion","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@cairncms/extension-address-completion","version":"0.1.0","type":"module","description":"Google-backed address autocomplete that saves one compact address record","repository":{"type":"git","url":"git+https://github.com/CairnCMS/extensions.git","directory":"packages/address-completion"},"license":"GPL-3.0-only","author":{"name":"The CairnCMS Authors"},"cairncms:extension":{"type":"bundle","runtime":"confined-server","path":{"app":"dist/app.js","api":"dist/api.js"},"entries":[{"type":"interface","name":"address-completion","source":"src/address-completion/index.ts"},{"type":"display","name":"address-completion-display","source":"src/address-completion-display/index.ts"},{"type":"endpoint","name":"address-completion-api","source":"src/address-completion-api/index.ts","capabilities":{"endpoint":{"access":"app"},"request":{"urls":["https://places.googleapis.com"],"methods":["POST","GET"]}}}],"settings":{"google_maps_api_key":{"type":"string","secret":{"source":"inline"},"presentation":{"order":1,"width":"full"}},"google_maps_browser_key":{"type":"string","appReadable":true,"presentation":{"order":2,"width":"full"}},"google_maps_map_id":{"type":"string","appReadable":true,"presentation":{"order":3,"width":"half"}}},"host":"^1.3.0"},"devDependencies":{"@cairncms/extensions-sdk":"^1.3.1","@cairncms/extensions-server-api":"1.3.1","@types/google.maps":"^3.65.5","@vitejs/plugin-vue":"^6.0.8","@vue/test-utils":"^2.4.6","happy-dom":"^20.10.0","micromustache":"^8.0.3","sass":"^1.102.0","typescript":"5.6.3","vitest":"^3.2.0","vue":"3.5.38","vue-tsc":"^3.3.9"},"keywords":["cairncms","cairncms-extension","address","autocomplete","google","places"],"scripts":{"build":"cairncms-extension build","typecheck":"vue-tsc --noEmit","test":"vitest run"},"_id":"@cairncms/extension-address-completion@0.1.0","bugs":{"url":"https://github.com/CairnCMS/extensions/issues"},"homepage":"https://github.com/CairnCMS/extensions#readme","_integrity":"sha512-5VCs7gyY/nUBrmCev9G81V+mQ7mJci+/BrkSEHvWJDCkH59mhtPr8AMY4/T8MoMCQKtZyDE1o46GZYhc3VPMSw==","_resolved":"/tmp/c74008efc4a5089dc0035464a05363ef/cairncms-extension-address-completion-0.1.0.tgz","_from":"file:cairncms-extension-address-completion-0.1.0.tgz","_nodeVersion":"22.22.2","_npmVersion":"10.9.7","dist":{"integrity":"sha512-5VCs7gyY/nUBrmCev9G81V+mQ7mJci+/BrkSEHvWJDCkH59mhtPr8AMY4/T8MoMCQKtZyDE1o46GZYhc3VPMSw==","shasum":"2cf28af6c1f386247f980aae3ac9fa7b37413ff1","tarball":"https://registry.npmjs.org/@cairncms/extension-address-completion/-/extension-address-completion-0.1.0.tgz","fileCount":11,"unpackedSize":116014,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDLn5/xCCHKEKNLeQwaN6XUg4D9fg61IoIwFj12wUtvoAIgPD+v2JfiYhjnZByYeETkHhacYl0gHKy0DXDjqteFo98="}]},"_npmUser":{"name":"cairncms-dev","email":"hello@cairncms.dev"},"directories":{},"maintainers":[{"name":"cairncms-dev","email":"hello@cairncms.dev"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/extension-address-completion_0.1.0_1786365092145_0.4631024582777914"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-10T12:31:31.810Z","0.1.0":"2026-08-10T12:31:32.298Z","modified":"2026-08-10T12:31:32.541Z"},"maintainers":[{"name":"cairncms-dev","email":"hello@cairncms.dev"}],"description":"Google-backed address autocomplete that saves one compact address record","homepage":"https://github.com/CairnCMS/extensions#readme","keywords":["cairncms","cairncms-extension","address","autocomplete","google","places"],"repository":{"type":"git","url":"git+https://github.com/CairnCMS/extensions.git","directory":"packages/address-completion"},"author":{"name":"The CairnCMS Authors"},"bugs":{"url":"https://github.com/CairnCMS/extensions/issues"},"license":"GPL-3.0-only","readme":"# Address Completion\n\nAddress Completion is a field interface backed by Google Places. An editor types an address or place name, picks a suggestion from a dropdown, and the field saves one compact address record with the formatted address, the Google place ID, and coordinates. A matching display renders the saved address in tables and layouts.\n\nSearches run through the extension's own server endpoint inside CairnCMS. The Google key stays on the server, the admin app loads no Google JavaScript, and a base install needs no content security policy changes. An optional embedded map is available for fields that want visual confirmation, and enabling it is the one feature that loads Google JavaScript in the browser.\n\nUse this when editors enter real-world places, such as store locations, dealers, venues, or customer addresses, and your frontend needs coordinates or a durable place reference along with the address text.\n\n## Requirements\n\n- CairnCMS 1.3.0 or later, with `SECRETS_ENCRYPTION_KEY` set in the deployment environment. Saving the Google key fails with a configuration error without it. See [Configuration](https://cairncms.dev/docs/manage/configuration/).\n- A Google Cloud project with billing enabled.\n- The Places API (New) service enabled on that project.\n- For the optional map: the Maps JavaScript API service, a browser key, a Map ID, and the CSP additions described below.\n- Admin access to CairnCMS for the one-time configuration.\n\n## Installation\n\nCairnCMS discovers the extension either from the project's `extensions` folder or from its `node_modules`. Pick the method that fits your deployment. After any of them, restart CairnCMS and check that Settings > Extensions shows an Address Completion row with a Normal health indicator.\n\n### Into the extensions folder\n\nFits the default Docker project from `cairncms init`, which mounts `./extensions` into the container, and any other deployment with a writable extensions folder:\n\n```bash\ncd <cairncms-project-folder>\nnpm pack @cairncms/extension-address-completion\nmkdir -p extensions/cairncms-extension-address-completion\ntar -xzf cairncms-extension-address-completion-*.tgz -C extensions/cairncms-extension-address-completion --strip-components=1\nrm cairncms-extension-address-completion-*.tgz\ndocker compose restart cairncms\n```\n\n### As an npm dependency\n\nFits deployments where you control the CairnCMS project folder and its `node_modules`:\n\n```bash\ncd <cairncms-project-folder>\nnpm install @cairncms/extension-address-completion\n```\n\nThe loader auto-discovers the package from `node_modules` at startup.\n\n### In a custom Docker image\n\nFits image-based deployments without writable mounts:\n\n```dockerfile\nFROM node:22-alpine AS extensions\nWORKDIR /ext\nRUN npm pack @cairncms/extension-address-completion && \\\n\tmkdir -p address-completion && \\\n\ttar -xzf cairncms-extension-address-completion-*.tgz -C address-completion --strip-components=1\n\nFROM cairncms/cairncms:1.3.0\nCOPY --from=extensions --chown=node:node /ext/address-completion /cairncms/extensions/cairncms-extension-address-completion\n```\n\nIn a production Dockerfile, pin an exact version, for example `@cairncms/extension-address-completion@0.1.0`, so image rebuilds stay reproducible.\n\n## Set up Google Cloud\n\nAddress search requires the Places API (New) and a server API key. The optional map also requires the Maps JavaScript API, a browser API key, and a Map ID.\n\n1. Create or select a Google Cloud project and enable billing.\n2. Enable **Places API (New)** under APIs & Services > Library.\n3. Create an API key and restrict it to **Places API (New)**. If your server has stable egress IP addresses, add them as an application restriction.\n4. For the optional map, enable **Maps JavaScript API**, create a website-restricted key for your CairnCMS origin, and create a JavaScript Map ID under Google Maps Platform > Map Management.\n\n### Troubleshooting\n\n- If Google reports that the Places API has not been used or is disabled, confirm that **Places API (New)** is enabled. The legacy Places API is a different service.\n- If the browser reports `ApiNotActivatedMapError`, enable **Maps JavaScript API**.\n\n## Configure the extension settings\n\nOpen Settings > Extensions, find the Address Completion row, and open its Settings action:\n\n- `google_maps_api_key` (required): the server key. This is a declared secret. CairnCMS encrypts it at rest, never sends it to the browser, and shows a saved state instead of the value after you store it.\n- `google_maps_browser_key` (optional): the browser key for the map. This value is public. Every app user can read it, and it appears in the map script address in the browser. The settings drawer shows it in plain text because it is not a secret.\n- `google_maps_map_id` (optional): the Map ID for the map. Also public.\n\n[Google recommends](https://developers.google.com/maps/api-security-best-practices) separate API keys for each application and platform. Use separate server and browser keys when possible. A shared key works, but it cannot use an application restriction suitable for both server and browser requests.\n\n## Add a field\n\n1. Open Settings > Data Model and pick a collection.\n2. Create a field with type JSON and choose the Address Completion interface.\n3. Optionally set the field's display to Address Completion, so tables and layouts render the saved address instead of raw JSON.\n\n### Field options\n\n**Countries** restricts suggestions to up to 15 countries. Empty means worldwide.\n\n**Search scope** controls what kinds of places can be suggested:\n\n- All places: street addresses, businesses, points of interest, cities, and regions.\n- Addresses: street addresses, buildings, units, roads, and postal codes only.\n- Cities: cities and towns.\n- Regions: states, counties, and other larger areas.\n\nWhatever the scope, the field only ever saves the address record of the chosen place. Business names and other place details are not stored.\n\n**Location bias** ranks suggestions near a chosen area without excluding other results. Use it when editors usually search in one region. Enter latitude, longitude, and radius together, or leave all three blank. The radius can be 100 to 50,000 meters.\n\n**Show map** renders an embedded Google map under the field with a marker on the saved address. It requires the browser key and Map ID settings and the CSP additions below, and it is the only feature that loads Google JavaScript into the admin app.\n\n## What gets saved\n\nThe field holds one JSON value with exactly two shapes. Picking a suggestion saves the full record:\n\n```json\n{\n\t\"version\": 1,\n\t\"formattedAddress\": \"30 W Pershing Rd, Kansas City, MO 64108, USA\",\n\t\"placeId\": \"ChIJxbu2nD_wwIcR3cdZjUGmRhc\",\n\t\"location\": {\n\t\t\"latitude\": 39.0846332,\n\t\t\"longitude\": -94.5850238,\n\t\t\"observedAt\": \"2026-08-09T14:22:24.388Z\"\n\t},\n\t\"components\": {\n\t\t\"locality\": \"Kansas City\",\n\t\t\"region\": \"MO\",\n\t\t\"postalCode\": \"64108\",\n\t\t\"countryCode\": \"US\"\n\t}\n}\n```\n\nTyping text without picking a suggestion saves a manual record, so hand-typed text never carries stale coordinates:\n\n```json\n{ \"version\": 1, \"formattedAddress\": \"Warehouse dock 3, rear entrance\" }\n```\n\nClearing the field saves `null`. Keys with no data are omitted. `components` is optional, `region` uses the short form (`MO`), and `countryCode` is ISO 3166-1 alpha-2.\n\nA frontend reads the value directly, checking for the full record first, since a manual record has no `location`:\n\n```js\nif (dealer.address?.location) {\n\tconst { latitude, longitude } = dealer.address.location;\n}\n```\n\nThe display's template option renders any path inside the record, for example `{{ components.locality }}` or `{{ location.latitude }}`. Displays change rendering only. Sorting or filtering a collection by city or region needs those values in flat fields, which this extension does not write.\n\nIf a field holds a value from another source that does not match this shape, the interface shows a notice and leaves the value untouched until an editor clears it.\n\n## Data flow\n\nSearch requests travel from the editor's browser to your CairnCMS server, and from there to Google. The typed search text and the chosen place ID are sent to Google under your server key. The extension debounces requests while typing, asks Google for only the fields it stores, and manages Google's autocomplete session tokens, which group each search-and-selection sequence into one Google billing session.\n\nSearch is available to any signed-in user with app access, and the endpoint does not meter aggregate usage. Quota caps and billing alerts in the Google Cloud console are the aggregate spend controls. The optional CairnCMS [rate limiters](https://cairncms.dev/docs/manage/configuration/) can additionally slow individual clients, and the standard limiter is per client IP, with a separate global limiter for instance-wide caps.\n\nWith the map disabled, the admin app never connects to Google. With the map enabled, the browser additionally loads the Maps JavaScript API on Google's quarterly channel using the browser key.\n\n## Google Places data\n\nSelected records contain data returned by Google Places. Review [Google's Places policies](https://developers.google.com/maps/documentation/places/web-service/policies) and [service-specific terms](https://cloud.google.com/maps-platform/terms/maps-service-terms) for requirements that apply to your use. Each record includes `observedAt`, and editors can refresh it from its place ID.\n\n## CSP for the map\n\nA base install needs no content security policy changes. Enabling the map loads Google JavaScript, tiles, and imagery in the browser, which the default policy blocks. Set these three directives in the deployment environment:\n\n```\nCONTENT_SECURITY_POLICY_DIRECTIVES__SCRIPT_SRC=\"array:'self', 'unsafe-eval', https://*.googleapis.com, https://*.gstatic.com, *.google.com, https://*.ggpht.com, *.googleusercontent.com, blob:\"\nCONTENT_SECURITY_POLICY_DIRECTIVES__IMG_SRC=\"array:'self', data:, blob:, https://*.googleapis.com, https://*.gstatic.com, *.google.com, *.googleusercontent.com\"\nCONTENT_SECURITY_POLICY_DIRECTIVES__CONNECT_SRC=\"array:'self', https://a.tile.openstreetmap.org, https://b.tile.openstreetmap.org, https://c.tile.openstreetmap.org, https://fonts.openmaptiles.org, https://api.mapbox.com, https://*.googleapis.com, *.google.com, https://*.gstatic.com, data:, blob:\"\n```\n\nKeep the non-Google entries. `connect-src` in particular replaces the CairnCMS defaults instead of extending them, and the OpenStreetMap and Mapbox origins in the list are what keep the built-in map interface working.\n\n## License\n\nGPL-3.0-only. The built package bundles third-party material with its own terms, listed in `THIRD_PARTY_NOTICES.md`.\n","readmeFilename":"README.md","_rev":"1-f75ba40429770bef067b95cb811ca618"}