@unngh/google-vision 1.0.0 → 1.0.1

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 (2) hide show
  1. package/README.md +361 -61
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,111 +1,411 @@
1
- # Google Vision JS
1
+ # @unngh/google-vision
2
2
 
3
- ![banner](https://raw.githubusercontent.com/cdavis-code/google_vision_workspace/main/packages/google_vision_js/images/banner.svg)
3
+ ![google_vision banner](https://raw.githubusercontent.com/cdavis-code/google_vision_workspace/main/packages/google_vision_js/images/banner.svg)
4
4
 
5
- [![Pub Version](https://img.shields.io/pub/v/google_vision_js)](https://pub.dev/packages/google_vision_js)
6
- [![npm Version](https://img.shields.io/npm/v/@unngh%2Fgoogle-vision)](https://www.npmjs.com/package/@unngh/google-vision)
5
+ **Google Vision API client for Node.js and browsers** — image labeling, face detection, OCR, safe search, and more with Promise-based API and full type safety.
7
6
 
8
- JavaScript interop wrapper for the Google Vision API. Enables Vision AI features image labeling, face detection, OCR, safe search, and more in Node.js and browser environments.
7
+ Built on the battle-tested [`google_vision`](https://pub.dev/packages/google_vision) Dart SDK, compiled to JavaScript via `dart2js`. Every request is parsed and validated by the same codebase that powers the official Dart package and CLI tools.
9
8
 
10
- This package wraps the [google_vision](https://pub.dev/packages/google_vision) Dart library using `dart:js_interop` and compiles to JavaScript via `dart compile js`.
9
+ [![npm version](https://img.shields.io/npm/v/@unngh/google-vision.svg)](https://www.npmjs.com/package/@unngh/google-vision)
10
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.4-blue.svg)](https://www.typescriptlang.org/)
11
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
11
12
 
12
- ## Features
13
+ ---
13
14
 
14
- - Label Detection
15
- - Face Detection
16
- - Text Detection (OCR)
17
- - Document Text Detection
18
- - Safe Search Detection
19
- - Landmark Detection
20
- - Logo Detection
21
- - Image Properties
22
- - Crop Hints
23
- - Web Detection
24
- - Object Localization
25
- - API Key and JWT authentication
15
+ ## Why @unngh/google-vision?
26
16
 
27
- ## Installation
17
+ | Feature | @unngh/google-vision | @google-cloud/vision |
18
+ |---------|---------------------|----------------------|
19
+ | **Protocol Core** | Shared Dart SDK → dart2js | Node.js native |
20
+ | **Type Safety** | Full TypeScript declarations | TypeScript support |
21
+ | **Promise-based API** | ✅ Modern async/await | ✅ Promise-based |
22
+ | **Dual Entrypoints** | ✅ Separate `node` and `browser` builds | Node.js only |
23
+ | **Browser Support** | ✅ Native `fetch` + `XMLHttpRequest` | ❌ |
24
+ | **Node.js Support** | ✅ Auto-polyfills browser APIs via `xhr2` | ✅ |
25
+ | **Bundle Size** | ~150–300 KB gzipped (dart2js runtime) | ~2–5 MB (gRPC + proto) |
26
+ | **API Key Auth** | ✅ `withApiKey()` | ✅ |
27
+ | **JWT Auth** | ✅ `withJwt()` | ✅ |
28
+ | **Image API** | ✅ URL, base64, buffers | ✅ |
29
+ | **File API (GCS)** | ✅ Text + Document text detection | ✅ |
30
+ | **Shared Protocol Core** | ✅ Same logic as Dart/CLI packages | Standalone implementation |
28
31
 
29
- ### npm
32
+ ### Key Advantages
33
+
34
+ ✨ **Universal Runtime** — One package works everywhere: Node.js 18+, modern browsers (Chrome, Firefox, Safari, Edge), and all major bundlers (Vite, Webpack, Rollup).
35
+
36
+ 🔒 **Zero Protocol Drift** — Feature parity guaranteed with the [google_vision](https://pub.dev/packages/google_vision) Dart package. Protocol fixes benefit all platforms simultaneously.
37
+
38
+ 📦 **Minimal Dependencies** — Only one runtime dependency: `xhr2` (Node.js only, < 10 KB). No gRPC, no protobuf, no native bindings.
39
+
40
+ ---
41
+
42
+ ## Quick Start
43
+
44
+ ### Installation
30
45
 
31
46
  ```bash
32
47
  npm install @unngh/google-vision
33
48
  ```
34
49
 
35
- ### pub.dev
50
+ ### Basic Usage (Node.js / TypeScript)
36
51
 
37
- ```bash
38
- dart pub add google_vision_js
39
- ```
52
+ ```typescript
53
+ import { GoogleVision } from '@unngh/google-vision';
54
+
55
+ // Create and authenticate
56
+ const vision = await GoogleVision.create();
57
+ vision.withApiKey(process.env.GOOGLE_VISION_API_KEY!);
40
58
 
41
- ## Prerequisites
59
+ // Detect labels in an image
60
+ const labels = await vision.image.labelDetection('https://example.com/photo.jpg');
61
+ labels.forEach((label) => {
62
+ console.log(`${label.description}: ${(label.score * 100).toFixed(1)}%`);
63
+ });
42
64
 
43
- ### 1. Enable the Vision API
65
+ // OCR text detection
66
+ const texts = await vision.image.textDetection('https://example.com/document.png');
67
+ console.log('Extracted text:', texts[0]?.description);
44
68
 
45
- Go to the [Google Cloud Console](https://console.cloud.google.com/), select or create a project, and enable the [Vision API](https://console.cloud.google.com/apis/library/vision.googleapis.com).
69
+ // Safe search
70
+ const safeSearch = await vision.image.safeSearchDetection('https://example.com/photo.jpg');
71
+ console.log('Adult content:', safeSearch?.adult);
46
72
 
47
- ### 2. Get an API key
73
+ // Face detection
74
+ const faces = await vision.image.faceDetection('https://example.com/portrait.jpg');
75
+ console.log(`Found ${faces.length} face(s)`);
76
+ ```
48
77
 
49
- 1. In the Cloud Console, go to **APIs & Services > Credentials**.
50
- 2. Click **Create Credentials > API Key**.
51
- 3. Copy the generated API key.
52
- 4. (Optional but recommended) Restrict the key to the Vision API to prevent unauthorized usage.
78
+ ### Browser Usage
53
79
 
54
- > **Tip:** For production use, consider [service account authentication](#authentication) with JWT instead of an API key. Service accounts provide more granular permissions and are the recommended approach for server-side applications.
80
+ ```typescript
81
+ import { GoogleVision } from '@unngh/google-vision/browser';
55
82
 
56
- ### 3. Set the API key
83
+ const vision = await GoogleVision.create();
84
+ vision.withApiKey('YOUR_API_KEY');
57
85
 
58
- ```sh
59
- export GOOGLE_VISION_API_KEY="your-api-key-here"
86
+ // Analyze images directly from the browser
87
+ const logos = await vision.image.logoDetection('https://example.com/brand.png');
88
+ console.log('Detected logos:', logos.map((l) => l.description));
60
89
  ```
61
90
 
62
- ## Usage
91
+ ### Choosing an Import Path
63
92
 
64
- ```javascript
65
- import { GoogleVisionJs } from '@unngh/google-vision';
93
+ This package supports both Node.js and browser environments. Use the appropriate import for your target:
66
94
 
67
- const vision = new GoogleVisionJs();
95
+ | Import Path | Environment | API Transport | Typical Use Case |
96
+ |---|---|---|---|
97
+ | `@unngh/google-vision` | **Auto-detected** | `fetch` / polyfilled `XMLHttpRequest` | Universal code, libraries |
98
+ | `@unngh/google-vision/node` | **Node.js** (explicit) | Polyfilled `XMLHttpRequest` via `xhr2` | Server-side apps, CLI tools, automation scripts |
99
+ | `@unngh/google-vision/browser` | **Browser** (explicit) | Native `fetch` | Web dashboards, browser extensions, client-side analysis |
68
100
 
69
- // Authenticate with API key
70
- vision.withApiKey(process.env.GOOGLE_VISION_API_KEY);
101
+ ---
71
102
 
72
- // Detect labels in an image
73
- const labels = await vision.image.labelDetection({
74
- imageUri: 'https://example.com/image.jpg'
75
- });
103
+ ## API Reference
104
+
105
+ ### Creating a Client
106
+
107
+ ```typescript
108
+ import { GoogleVision } from '@unngh/google-vision';
76
109
 
77
- console.log(labels);
110
+ const vision = await GoogleVision.create();
78
111
  ```
79
112
 
80
- ## Authentication
113
+ The factory loads the dart2js runtime and returns a configured client. No arguments needed when using the `node` or `browser` entrypoints — the loader is pre-configured.
81
114
 
82
- ### API Key
115
+ ### Authentication
83
116
 
84
- The simplest way to authenticate. Best for quick starts and testing.
117
+ #### API Key (Synchronous)
85
118
 
86
- ```javascript
119
+ ```typescript
87
120
  vision.withApiKey('YOUR_API_KEY');
88
121
  ```
89
122
 
90
- ### JWT (Service Account)
123
+ Chainable returns `this` for fluent usage.
91
124
 
92
- The recommended approach for production. Uses a Google Cloud service account JSON key file.
125
+ #### JWT / Service Account (Asynchronous)
93
126
 
94
- ```javascript
127
+ ```typescript
95
128
  const credentials = fs.readFileSync('./service-account.json', 'utf-8');
96
129
  await vision.withJwt(credentials);
130
+ // Optional scope:
131
+ await vision.withJwt(credentials, 'https://www.googleapis.com/auth/cloud-vision');
132
+ ```
133
+
134
+ ### Image API (`vision.image`)
135
+
136
+ All image methods accept an image source (URL string or `{ imageUri, content }` object) and an optional `maxResults` cap.
137
+
138
+ | Method | Returns | Description |
139
+ |--------|---------|-------------|
140
+ | `labelDetection(source, maxResults?)` | `Label[]` | General object and scene labels |
141
+ | `textDetection(source, maxResults?)` | `TextAnnotation[]` | OCR text detection |
142
+ | `documentTextDetection(source, maxResults?)` | `TextAnnotation \| null` | Dense document OCR |
143
+ | `faceDetection(source, maxResults?)` | `FaceAnnotation[]` | Face landmark and emotion detection |
144
+ | `safeSearchDetection(source, maxResults?)` | `SafeSearch \| null` | Explicit content detection (adult, violence, etc.) |
145
+ | `landmarkDetection(source, maxResults?)` | `EntityAnnotation[]` | Geographic landmark recognition |
146
+ | `logoDetection(source, maxResults?)` | `EntityAnnotation[]` | Brand logo recognition |
147
+ | `cropHints(source, maxResults?)` | `CropHint \| null` | Suggested image crop regions |
148
+ | `imageProperties(source, maxResults?)` | `ImageProperties \| null` | Dominant colors and image stats |
149
+ | `objectLocalization(source, maxResults?)` | `LocalizedObject[]` | Bounding boxes for objects |
150
+ | `webDetection(source, maxResults?)` | `WebDetection \| null` | Web entities, similar images, pages |
151
+
152
+ ```typescript
153
+ // URL string
154
+ const labels = await vision.image.labelDetection('https://example.com/photo.jpg');
155
+
156
+ // Limit results
157
+ const top3 = await vision.image.labelDetection('https://example.com/photo.jpg', 3);
158
+
159
+ // Base64 content
160
+ const results = await vision.image.textDetection({
161
+ content: fs.readFileSync('./document.jpg').toString('base64'),
162
+ });
163
+ ```
164
+
165
+ ### File API (`vision.file`)
166
+
167
+ Process PDFs and images stored in Google Cloud Storage. Accepts a GCS URI and optional `maxResults` cap.
168
+
169
+ | Method | Returns | Description |
170
+ |--------|---------|-------------|
171
+ | `labelDetection(gcsUri, maxResults?)` | `Label[]` | Labels for GCS objects |
172
+ | `textDetection(gcsUri, maxResults?)` | `TextAnnotation[]` | OCR from GCS files/PDFs |
173
+ | `documentTextDetection(gcsUri, maxResults?)` | `TextAnnotation[]` | Dense OCR from GCS files/PDFs |
174
+ | `faceDetection(gcsUri, maxResults?)` | `FaceAnnotation[]` | Face detection from GCS images |
175
+
176
+ ```typescript
177
+ const labels = await vision.file.labelDetection('gs://my-bucket/document.pdf');
178
+ const texts = await vision.file.textDetection('gs://my-bucket/scanned-form.pdf');
179
+ ```
180
+
181
+ ---
182
+
183
+ ## Builds
184
+
185
+ | Build | Import Path | API Transport | Use Case |
186
+ |-------|-------------|---------------|----------|
187
+ | **Node.js (ESM)** | `@unngh/google-vision` or `@unngh/google-vision/node` | Polyfilled `XMLHttpRequest` | Server-side apps, CLI tools |
188
+ | **Node.js (CommonJS)** | `require('@unngh/google-vision')` | Same as above | Legacy Node.js projects |
189
+ | **Browser** | `@unngh/google-vision/browser` | Native `fetch` | Web apps, browser extensions |
190
+
191
+ ### Automatic Polyfills (Node.js)
192
+
193
+ On Node.js, the package automatically polyfills browser globals that dart2js expects:
194
+
195
+ - `self` → `globalThis`
196
+ - `window` → `globalThis`
197
+ - `XMLHttpRequest` → dynamically loaded `xhr2` package
198
+
199
+ No configuration needed — just import and use.
200
+
201
+ ---
202
+
203
+ ## Configuration Options
204
+
205
+ ### Authentication Methods
206
+
207
+ | Method | Signature | Use Case |
208
+ |--------|-----------|----------|
209
+ | `withApiKey(key)` | `(apiKey: string) => this` | Quick starts, testing, client-side apps |
210
+ | `withJwt(credentials, scope?)` | `(json: string, scope?: string) => Promise<this>` | Production server-side apps |
211
+
212
+ ### Prerequisites
213
+
214
+ 1. **Enable the Vision API** in the [Google Cloud Console](https://console.cloud.google.com/apis/library/vision.googleapis.com).
215
+ 2. **Create credentials** — API key (simplest) or service account (recommended for production).
216
+ 3. **Set your API key** as `GOOGLE_VISION_API_KEY` environment variable, or pass it directly to `withApiKey()`.
217
+
218
+ ---
219
+
220
+ ## Examples
221
+
222
+ ### Complete Workflow: Label + Safe Search + OCR
223
+
224
+ ```typescript
225
+ import { GoogleVision } from '@unngh/google-vision';
226
+
227
+ const vision = await GoogleVision.create();
228
+ vision.withApiKey(process.env.GOOGLE_VISION_API_KEY!);
229
+
230
+ const imageUrl = 'https://example.com/analysis-target.jpg';
231
+
232
+ // Run multiple detections
233
+ const [labels, safeSearch, texts] = await Promise.all([
234
+ vision.image.labelDetection(imageUrl),
235
+ vision.image.safeSearchDetection(imageUrl),
236
+ vision.image.textDetection(imageUrl),
237
+ ]);
238
+
239
+ console.log('Labels:', labels.map((l) => l.description));
240
+ console.log('Safe:', safeSearch);
241
+ console.log('OCR:', texts[0]?.description?.slice(0, 200));
242
+ ```
243
+
244
+ ### Face Detection with Emotion Analysis
245
+
246
+ ```typescript
247
+ const faces = await vision.image.faceDetection('https://example.com/group-photo.jpg');
248
+ console.log(`Found ${faces.length} face(s)`);
249
+
250
+ faces.forEach((face, i) => {
251
+ console.log(`Face ${i + 1}:`);
252
+ console.log(` Joy: ${face.joyLikelihood}`);
253
+ console.log(` Sorrow: ${face.sorrowLikelihood}`);
254
+ console.log(` Anger: ${face.angerLikelihood}`);
255
+ console.log(` Surprise: ${face.surpriseLikelihood}`);
256
+ });
97
257
  ```
98
258
 
99
- ## Building from source
259
+ ### File Processing from Google Cloud Storage
260
+
261
+ ```typescript
262
+ // Analyze a PDF stored in GCS
263
+ const labels = await vision.file.labelDetection('gs://my-bucket/reports/annual.pdf');
264
+ console.log('Document labels:', labels.map((l) => l.description));
265
+
266
+ // Full document OCR
267
+ const ocr = await vision.file.documentTextDetection('gs://my-bucket/scans/form.pdf');
268
+ console.log('Document text:', ocr[0]?.description);
269
+ ```
270
+
271
+ ---
272
+
273
+ ## Caveats & Limitations
274
+
275
+ 1. **Bundle Size**: The dart2js runtime ships as a self-contained ES module. Expect ~150–300 KB gzipped. Tree-shaking is limited since dart2js emits a monolithic runtime.
276
+
277
+ 2. **Exception Handling**: dart2js boxes Dart exceptions. In catch blocks, unwrap with:
278
+ ```typescript
279
+ catch (error) {
280
+ const boxSym = error?.error
281
+ ? Object.getOwnPropertySymbols(error.error)
282
+ .find(s => s.toString().includes('jsBoxed'))
283
+ : null;
284
+ const realError = boxSym ? error.error[boxSym] : error;
285
+ console.error(realError?.message ?? String(error));
286
+ }
287
+ ```
288
+
289
+ 3. **Browser CORS**: The Vision API does not send CORS headers. Browser usage requires a proxy or is best suited for local development. For production browser apps, proxy requests through your own backend.
290
+
291
+ 4. **Node Version Support**: Requires Node 18+. On older versions, the `xhr2` polyfill handles API transport transparently.
292
+
293
+ ---
294
+
295
+ ## Building from Source
296
+
297
+ ```bash
298
+ # Install dependencies
299
+ npm install
300
+
301
+ # Build everything (dart2js + tsup)
302
+ npm run build
303
+
304
+ # Or with the build script directly
305
+ bash scripts/build.sh
306
+ ```
307
+
308
+ ### Build Outputs
309
+
310
+ - `build/dart/google_vision.js` — Raw dart2js runtime (~1–2 MB uncompressed)
311
+ - `dist/browser.js` — Browser ESM bundle
312
+ - `dist/node.js` — Node.js ESM bundle
313
+ - `dist/node.cjs` — Node.js CommonJS bundle
314
+ - `dist/*.d.ts` — TypeScript declaration files
315
+ - `dist/google_vision.runtime.js` — dart2js runtime (copied for dynamic import)
316
+
317
+ **Requirements**: Dart SDK ^3.8.0 for `dart2js` compilation.
318
+
319
+ ---
320
+
321
+ ## Relation to the Dart Package
322
+
323
+ All protocol logic lives in [`packages/google_vision/`](../google_vision/). This package is a thin JS/TS shim over the exact same code:
324
+
325
+ ```
326
+ google_vision (Dart SDK)
327
+ ↓ shared protocol logic
328
+ google_vision_js (JS/TS bindings)
329
+ ↓ compiled via dart2js
330
+ google_vision_cli (CLI tool)
331
+ ↓ uses same Dart SDK
332
+ ```
333
+
334
+ **Feature parity is guaranteed** — if a Google Vision API feature exists in the Dart package, it works here.
335
+
336
+ ---
337
+
338
+ ## Contributing
339
+
340
+ Contributions welcome! This package is part of the [`google_vision_workspace`](https://github.com/cdavis-code/google_vision_workspace) monorepo.
341
+
342
+ 1. Fork the repository
343
+ 2. Create a feature branch: `git checkout -b feature/my-feature`
344
+ 3. Make your changes
345
+ 4. Build and test: `npm run build`
346
+ 5. Submit a pull request
347
+
348
+ ### Publishing to npm
349
+
350
+ This package uses GitHub Actions for automated npm publishing. When you push a tag with the format `google_vision_js-v*`, it will automatically build and publish to npm.
351
+
352
+ #### Using the Publish Script (Recommended)
353
+
354
+ ```bash
355
+ # Inside packages/google_vision_js/
356
+
357
+ # Bump patch version (1.0.0 -> 1.0.1)
358
+ ./scripts/publish-npm.sh patch
359
+
360
+ # Bump minor version (1.0.0 -> 1.1.0)
361
+ ./scripts/publish-npm.sh minor
362
+
363
+ # Bump major version (1.0.0 -> 2.0.0)
364
+ ./scripts/publish-npm.sh major
365
+
366
+ # Set specific version
367
+ ./scripts/publish-npm.sh 1.2.0
368
+ ```
369
+
370
+ The script will:
371
+ 1. Update the version in package.json
372
+ 2. Build the package (dart2js + tsup)
373
+ 3. Create a git commit and tag
374
+ 4. Provide instructions to push and trigger the GitHub Action
375
+
376
+ #### Manual Publishing
100
377
 
101
378
  ```bash
102
- # Install Dart dependencies
103
- dart pub get
379
+ npm run build
380
+ npm publish --access=public
381
+ ```
104
382
 
105
- # Compile to JavaScript
106
- dart run tool/compile_js.dart
383
+ #### GitHub Action Trigger
384
+
385
+ Push the tag to trigger automated publishing:
386
+
387
+ ```bash
388
+ git push origin main --tags
107
389
  ```
108
390
 
391
+ The workflow will:
392
+ - ✅ Build the package
393
+ - ✅ Publish to npm with public access
394
+ - ✅ Available at `https://www.npmjs.com/package/@unngh/google-vision`
395
+
396
+ **Note**: You need to set `NPM_TOKEN` as a GitHub secret for the workflow to authenticate with npm.
397
+
398
+ ---
399
+
109
400
  ## License
110
401
 
111
- MIT
402
+ MIT License — see [LICENSE](./LICENSE).
403
+
404
+ ---
405
+
406
+ ## Resources
407
+
408
+ - **[Google Vision API Documentation](https://cloud.google.com/vision/docs)**
409
+ - **[Dart SDK (google_vision)](https://pub.dev/packages/google_vision)**
410
+ - **[CLI Tool (google_vision_cli)](../google_vision_cli)**
411
+ - **[GitHub Repository](https://github.com/cdavis-code/google_vision_workspace)**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unngh/google-vision",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Google Vision API client for Node.js and browsers — image labeling, face detection, OCR, safe search, and more. Compiled from Dart.",
5
5
  "license": "MIT",
6
6
  "author": "Chris Davis",