@crvouga/mockingbird-service-google-maps 1.0.0 → 1.1.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.
package/dist/server.d.ts CHANGED
@@ -170,82 +170,6 @@ type FaultRegistry$1 = {
170
170
  take(candidate: FaultCandidate$1): Promise<FaultHit$1[]>;
171
171
  };
172
172
 
173
- /** One handled request, as the structured log sees it. */
174
- type RequestLog$1 = {
175
- service: string;
176
- namespace: string;
177
- operationId: string | undefined;
178
- method: string;
179
- path: string;
180
- status: number;
181
- durationMs: number;
182
- /** True when the path matched no operation in the contract. */
183
- unmatched: boolean;
184
- /** Set when a fault rule produced the response. */
185
- faultId?: string;
186
- /** Resource ids the handler touched (`userId`, `orderId`, …), when the service reports them. */
187
- ids?: Record<string, string>;
188
- /** Set when the service created a resource the request referred to but that did not exist. */
189
- adopted?: boolean;
190
- };
191
- type MetricsReport$1 = {
192
- requests: number;
193
- /** Counts keyed `<operationId> <status>`. */
194
- byOperation: Record<string, number>;
195
- /**
196
- * Paths that matched no operation, most frequent first.
197
- *
198
- * This is the early-warning signal: a consumer calling something the mock does
199
- * not implement shows up here as a count, before it fails a suite as a 404.
200
- */
201
- unmatched: {
202
- method: string;
203
- path: string;
204
- count: number;
205
- }[];
206
- faults: number;
207
- totalDurationMs: number;
208
- };
209
- type Metrics$1 = {
210
- record(entry: RequestLog$1): void;
211
- report(): MetricsReport$1;
212
- reset(): void;
213
- };
214
-
215
- /** One journal entry: a request log stamped with when (on the mock clock) it was handled. */
216
- type JournalEntry$1 = RequestLog$1 & {
217
- at: string;
218
- };
219
- type JournalQuery$1 = {
220
- /** Only this namespace. Omit for every namespace, oldest first across all of them. */
221
- namespace?: string;
222
- operationId?: string;
223
- status?: number;
224
- /** Only entries at or after this instant (epoch ms). */
225
- since?: number;
226
- /** At most this many, the most recent kept. */
227
- limit?: number;
228
- };
229
- type Journal$1 = {
230
- readonly size: number;
231
- record(entry: JournalEntry$1): void;
232
- list(query?: JournalQuery$1): JournalEntry$1[];
233
- /** Forget one namespace's entries, or every namespace's. */
234
- clear(namespace?: string): void;
235
- };
236
-
237
- /** Credential → namespace mapping behind `PUT /__admin/credentials`. */
238
- type CredentialRegistry$1 = {
239
- set(credential: string, namespace: string): void;
240
- get(credential: string): string | undefined;
241
- remove(credential: string): boolean;
242
- clear(): void;
243
- entries(): {
244
- credential: string;
245
- namespace: string;
246
- }[];
247
- };
248
-
249
173
  /** A stable identifier for a point in a {@link Timeline}. */
250
174
  type CheckpointId$1 = string;
251
175
  /** An immutable node in a timeline's checkpoint DAG. */
@@ -336,6 +260,110 @@ interface FetchAPI$2 {
336
260
  fetch(request: Request): Promise<Response>;
337
261
  }
338
262
 
263
+ /**
264
+ * One problem with a request body, at a dotted path (`patient.address.city`, `items.0.sku`).
265
+ * `kind` is `media_type` when the body is present but its media type is not one the operation
266
+ * accepts (a `415` on most vendors), `syntax` when it could not be decoded, `required` when
267
+ * the operation needs a body and none arrived, and `schema` for a contract violation.
268
+ */
269
+ type BodyIssue$1 = {
270
+ path: string;
271
+ message: string;
272
+ kind?: "media_type" | "syntax" | "required" | "schema";
273
+ };
274
+
275
+ /**
276
+ * What the mock received from a request it rejected, so a 4xx can be attributed after the
277
+ * fact (an empty body, a missing `content-type`, a schema violation) without logging the body.
278
+ */
279
+ type RejectedRequest$1 = {
280
+ /** The raw `content-type` header, or `null` when the request sent none. */
281
+ contentType: string | null;
282
+ /** Bytes of body received from `content-length` (`0` for none), or `null` when the client sent no length (a streamed / chunked body). */
283
+ bodyBytes: number | null;
284
+ /** The raw `transfer-encoding` header (`chunked`), or `null`. */
285
+ transferEncoding: string | null;
286
+ };
287
+ /** One handled request, as the structured log sees it. */
288
+ type RequestLog$1 = {
289
+ service: string;
290
+ namespace: string;
291
+ operationId: string | undefined;
292
+ method: string;
293
+ path: string;
294
+ status: number;
295
+ durationMs: number;
296
+ /** True when the path matched no operation in the contract. */
297
+ unmatched: boolean;
298
+ /** Set when a fault rule produced the response. */
299
+ faultId?: string;
300
+ /** Resource ids the handler touched (`userId`, `orderId`, …), when the service reports them. */
301
+ ids?: Record<string, string>;
302
+ /** Set when the service created a resource the request referred to but that did not exist. */
303
+ adopted?: boolean;
304
+ /** Set on a rejection (status ≥ 400) the mock produced itself, never on a scripted fault. */
305
+ request?: RejectedRequest$1;
306
+ /** The validation issues behind a rejection, when the service found any. */
307
+ issues?: BodyIssue$1[];
308
+ };
309
+ type MetricsReport$1 = {
310
+ requests: number;
311
+ /** Counts keyed `<operationId> <status>`. */
312
+ byOperation: Record<string, number>;
313
+ /**
314
+ * Paths that matched no operation, most frequent first.
315
+ *
316
+ * This is the early-warning signal: a consumer calling something the mock does
317
+ * not implement shows up here as a count, before it fails a suite as a 404.
318
+ */
319
+ unmatched: {
320
+ method: string;
321
+ path: string;
322
+ count: number;
323
+ }[];
324
+ faults: number;
325
+ totalDurationMs: number;
326
+ };
327
+ type Metrics$1 = {
328
+ record(entry: RequestLog$1): void;
329
+ report(): MetricsReport$1;
330
+ reset(): void;
331
+ };
332
+
333
+ /** One journal entry: a request log stamped with when (on the mock clock) it was handled. */
334
+ type JournalEntry$1 = RequestLog$1 & {
335
+ at: string;
336
+ };
337
+ type JournalQuery$1 = {
338
+ /** Only this namespace. Omit for every namespace, oldest first across all of them. */
339
+ namespace?: string;
340
+ operationId?: string;
341
+ status?: number;
342
+ /** Only entries at or after this instant (epoch ms). */
343
+ since?: number;
344
+ /** At most this many, the most recent kept. */
345
+ limit?: number;
346
+ };
347
+ type Journal$1 = {
348
+ readonly size: number;
349
+ record(entry: JournalEntry$1): void;
350
+ list(query?: JournalQuery$1): JournalEntry$1[];
351
+ /** Forget one namespace's entries, or every namespace's. */
352
+ clear(namespace?: string): void;
353
+ };
354
+
355
+ /** Credential → namespace mapping behind `PUT /__admin/credentials`. */
356
+ type CredentialRegistry$1 = {
357
+ set(credential: string, namespace: string): void;
358
+ get(credential: string): string | undefined;
359
+ remove(credential: string): boolean;
360
+ clear(): void;
361
+ entries(): {
362
+ credential: string;
363
+ namespace: string;
364
+ }[];
365
+ };
366
+
339
367
  /**
340
368
  * A point-in-time copy of everything a service namespace holds.
341
369
  *
@@ -728,82 +756,6 @@ type FaultRegistry = {
728
756
  take(candidate: FaultCandidate): Promise<FaultHit[]>;
729
757
  };
730
758
 
731
- /** One handled request, as the structured log sees it. */
732
- type RequestLog = {
733
- service: string;
734
- namespace: string;
735
- operationId: string | undefined;
736
- method: string;
737
- path: string;
738
- status: number;
739
- durationMs: number;
740
- /** True when the path matched no operation in the contract. */
741
- unmatched: boolean;
742
- /** Set when a fault rule produced the response. */
743
- faultId?: string;
744
- /** Resource ids the handler touched (`userId`, `orderId`, …), when the service reports them. */
745
- ids?: Record<string, string>;
746
- /** Set when the service created a resource the request referred to but that did not exist. */
747
- adopted?: boolean;
748
- };
749
- type MetricsReport = {
750
- requests: number;
751
- /** Counts keyed `<operationId> <status>`. */
752
- byOperation: Record<string, number>;
753
- /**
754
- * Paths that matched no operation, most frequent first.
755
- *
756
- * This is the early-warning signal: a consumer calling something the mock does
757
- * not implement shows up here as a count, before it fails a suite as a 404.
758
- */
759
- unmatched: {
760
- method: string;
761
- path: string;
762
- count: number;
763
- }[];
764
- faults: number;
765
- totalDurationMs: number;
766
- };
767
- type Metrics = {
768
- record(entry: RequestLog): void;
769
- report(): MetricsReport;
770
- reset(): void;
771
- };
772
-
773
- /** One journal entry: a request log stamped with when (on the mock clock) it was handled. */
774
- type JournalEntry = RequestLog & {
775
- at: string;
776
- };
777
- type JournalQuery = {
778
- /** Only this namespace. Omit for every namespace, oldest first across all of them. */
779
- namespace?: string;
780
- operationId?: string;
781
- status?: number;
782
- /** Only entries at or after this instant (epoch ms). */
783
- since?: number;
784
- /** At most this many, the most recent kept. */
785
- limit?: number;
786
- };
787
- type Journal = {
788
- readonly size: number;
789
- record(entry: JournalEntry): void;
790
- list(query?: JournalQuery): JournalEntry[];
791
- /** Forget one namespace's entries, or every namespace's. */
792
- clear(namespace?: string): void;
793
- };
794
-
795
- /** Credential → namespace mapping behind `PUT /__admin/credentials`. */
796
- type CredentialRegistry = {
797
- set(credential: string, namespace: string): void;
798
- get(credential: string): string | undefined;
799
- remove(credential: string): boolean;
800
- clear(): void;
801
- entries(): {
802
- credential: string;
803
- namespace: string;
804
- }[];
805
- };
806
-
807
759
  /** A stable identifier for a point in a {@link Timeline}. */
808
760
  type CheckpointId = string;
809
761
  /** An immutable node in a timeline's checkpoint DAG. */
@@ -894,6 +846,123 @@ interface FetchAPI$1 {
894
846
  fetch(request: Request): Promise<Response>;
895
847
  }
896
848
 
849
+ /** Options every provider constructor accepts. */
850
+ type APIOptions = {
851
+ /** Sync SQLite client. Defaults to `@crvouga/mockingbird-service-sqlite`. */
852
+ sqlite?: SqliteClient$1;
853
+ /** Clock used for `created`-style fields. Default `Date.now`. */
854
+ now?: () => number;
855
+ /**
856
+ * Storage namespace for this instance's records. Instances sharing one SQLite
857
+ * client stay isolated when their namespaces differ. Defaults to the service name.
858
+ */
859
+ namespace?: string;
860
+ };
861
+
862
+ /**
863
+ * One problem with a request body, at a dotted path (`patient.address.city`, `items.0.sku`).
864
+ * `kind` is `media_type` when the body is present but its media type is not one the operation
865
+ * accepts (a `415` on most vendors), `syntax` when it could not be decoded, `required` when
866
+ * the operation needs a body and none arrived, and `schema` for a contract violation.
867
+ */
868
+ type BodyIssue = {
869
+ path: string;
870
+ message: string;
871
+ kind?: "media_type" | "syntax" | "required" | "schema";
872
+ };
873
+
874
+ /**
875
+ * What the mock received from a request it rejected, so a 4xx can be attributed after the
876
+ * fact (an empty body, a missing `content-type`, a schema violation) without logging the body.
877
+ */
878
+ type RejectedRequest = {
879
+ /** The raw `content-type` header, or `null` when the request sent none. */
880
+ contentType: string | null;
881
+ /** Bytes of body received from `content-length` (`0` for none), or `null` when the client sent no length (a streamed / chunked body). */
882
+ bodyBytes: number | null;
883
+ /** The raw `transfer-encoding` header (`chunked`), or `null`. */
884
+ transferEncoding: string | null;
885
+ };
886
+ /** One handled request, as the structured log sees it. */
887
+ type RequestLog = {
888
+ service: string;
889
+ namespace: string;
890
+ operationId: string | undefined;
891
+ method: string;
892
+ path: string;
893
+ status: number;
894
+ durationMs: number;
895
+ /** True when the path matched no operation in the contract. */
896
+ unmatched: boolean;
897
+ /** Set when a fault rule produced the response. */
898
+ faultId?: string;
899
+ /** Resource ids the handler touched (`userId`, `orderId`, …), when the service reports them. */
900
+ ids?: Record<string, string>;
901
+ /** Set when the service created a resource the request referred to but that did not exist. */
902
+ adopted?: boolean;
903
+ /** Set on a rejection (status ≥ 400) the mock produced itself, never on a scripted fault. */
904
+ request?: RejectedRequest;
905
+ /** The validation issues behind a rejection, when the service found any. */
906
+ issues?: BodyIssue[];
907
+ };
908
+ type MetricsReport = {
909
+ requests: number;
910
+ /** Counts keyed `<operationId> <status>`. */
911
+ byOperation: Record<string, number>;
912
+ /**
913
+ * Paths that matched no operation, most frequent first.
914
+ *
915
+ * This is the early-warning signal: a consumer calling something the mock does
916
+ * not implement shows up here as a count, before it fails a suite as a 404.
917
+ */
918
+ unmatched: {
919
+ method: string;
920
+ path: string;
921
+ count: number;
922
+ }[];
923
+ faults: number;
924
+ totalDurationMs: number;
925
+ };
926
+ type Metrics = {
927
+ record(entry: RequestLog): void;
928
+ report(): MetricsReport;
929
+ reset(): void;
930
+ };
931
+
932
+ /** One journal entry: a request log stamped with when (on the mock clock) it was handled. */
933
+ type JournalEntry = RequestLog & {
934
+ at: string;
935
+ };
936
+ type JournalQuery = {
937
+ /** Only this namespace. Omit for every namespace, oldest first across all of them. */
938
+ namespace?: string;
939
+ operationId?: string;
940
+ status?: number;
941
+ /** Only entries at or after this instant (epoch ms). */
942
+ since?: number;
943
+ /** At most this many, the most recent kept. */
944
+ limit?: number;
945
+ };
946
+ type Journal = {
947
+ readonly size: number;
948
+ record(entry: JournalEntry): void;
949
+ list(query?: JournalQuery): JournalEntry[];
950
+ /** Forget one namespace's entries, or every namespace's. */
951
+ clear(namespace?: string): void;
952
+ };
953
+
954
+ /** Credential → namespace mapping behind `PUT /__admin/credentials`. */
955
+ type CredentialRegistry = {
956
+ set(credential: string, namespace: string): void;
957
+ get(credential: string): string | undefined;
958
+ remove(credential: string): boolean;
959
+ clear(): void;
960
+ entries(): {
961
+ credential: string;
962
+ namespace: string;
963
+ }[];
964
+ };
965
+
897
966
  /**
898
967
  * A point-in-time copy of everything a service namespace holds.
899
968
  *
@@ -1044,19 +1113,6 @@ type ServiceRuntime<T extends ServiceInstance> = FetchAPI$1 & {
1044
1113
  timeline(namespace?: string): Timeline<ServiceTimelineState>;
1045
1114
  };
1046
1115
 
1047
- /** Options every provider constructor accepts. */
1048
- type APIOptions = {
1049
- /** Sync SQLite client. Defaults to `@crvouga/mockingbird-service-sqlite`. */
1050
- sqlite?: SqliteClient$1;
1051
- /** Clock used for `created`-style fields. Default `Date.now`. */
1052
- now?: () => number;
1053
- /**
1054
- * Storage namespace for this instance's records. Instances sharing one SQLite
1055
- * client stay isolated when their namespaces differ. Defaults to the service name.
1056
- */
1057
- namespace?: string;
1058
- };
1059
-
1060
1116
  /** Bind values accepted by Mockingbird's SQLite port (matches sqlite-mem / better-sqlite3). */
1061
1117
  type SqliteValue = null | number | bigint | string | Uint8Array | boolean;
1062
1118
  /** Mutation counters returned by {@link SqliteStatement.run}. */
@@ -1086,6 +1142,40 @@ interface SqliteClient {
1086
1142
  transaction<T>(fn: () => T): T;
1087
1143
  }
1088
1144
 
1145
+ /**
1146
+ * The Address Validation API (`POST https://addressvalidation.googleapis.com/v1:validateAddress`)
1147
+ * over the same corpus as Places and Geocoding.
1148
+ *
1149
+ * Shapes follow Google's reference: `ValidationResult` (`verdict`, `address`, `geocode`,
1150
+ * `uspsData`) and `google.rpc.Status` errors. Google encodes responses as proto3 JSON, so
1151
+ * `false` booleans and empty lists are omitted rather than sent.
1152
+ *
1153
+ * - https://developers.google.com/maps/documentation/address-validation/reference/rest/v1/TopLevel/validateAddress
1154
+ * - https://developers.google.com/maps/documentation/address-validation/reference/rest/v1/ValidationResult
1155
+ * - https://developers.google.com/maps/documentation/address-validation/understand-response
1156
+ *
1157
+ * Every answer is a pure function of the request and the namespace's corpus. A street the corpus
1158
+ * holds (or one synthesized in a corpus city, as for Autocomplete) is a delivery point: DPV `Y`,
1159
+ * with a deterministic ZIP+4. Admin rows can pin any part of the verdict (`validation`).
1160
+ */
1161
+ type Granularity = "SUB_PREMISE" | "PREMISE" | "PREMISE_PROXIMITY" | "BLOCK" | "ROUTE" | "OTHER";
1162
+ type PossibleNextAction = "FIX" | "CONFIRM_ADD_SUBPREMISES" | "CONFIRM" | "ACCEPT";
1163
+ type DpvConfirmation = "Y" | "N" | "S" | "D";
1164
+ /** Per-row controls set through `PUT /__admin/corpus`: pin the verdict a test needs. */
1165
+ type ValidationOverrides = {
1166
+ granularity?: Granularity;
1167
+ addressComplete?: boolean;
1168
+ possibleNextAction?: PossibleNextAction;
1169
+ dpvConfirmation?: DpvConfirmation;
1170
+ unconfirmedComponentTypes?: string[];
1171
+ /** USPS refuses the address: `uspsData.errorMessage`, and no DPV code. */
1172
+ uspsErrorMessage?: string;
1173
+ /** An apartment building: a missing unit is `D`, an unknown one `S`. */
1174
+ multiUnit?: boolean;
1175
+ /** The units USPS knows for a `multiUnit` row. Absent: every unit is known. */
1176
+ units?: string[];
1177
+ };
1178
+
1089
1179
  /**
1090
1180
  * The addresses the mock knows, matching our QA fixtures: every row of `ROUTING_ZIP_CORPUS`
1091
1181
  * (`packages/qa/src/world/gen/addresses.ts`, ≥1 real ZIP per state + DC, ids kept stable), the
@@ -1104,6 +1194,8 @@ type CorpusAddress = {
1104
1194
  county: string;
1105
1195
  lat: number;
1106
1196
  lng: number;
1197
+ /** Address Validation controls for this row (admin rows only): pin a verdict, flag units. */
1198
+ validation?: ValidationOverrides;
1107
1199
  };
1108
1200
 
1109
1201
  /**
@@ -1169,6 +1261,9 @@ declare class GoogleMapsAPI implements FetchAPI {
1169
1261
  private status;
1170
1262
  /** Key check and status-effect faults, before every web-service call. */
1171
1263
  private gate;
1264
+ /** Key check for the Google Cloud-style Address Validation API. */
1265
+ private cloudGate;
1266
+ private validate;
1172
1267
  private noted;
1173
1268
  private autocomplete;
1174
1269
  private details;
package/dist/server.js CHANGED
@@ -2,8 +2,8 @@ import {
2
2
  DEFAULT_PORT,
3
3
  createServer,
4
4
  serveTarget
5
- } from "./chunk-YHBPY6U2.js";
6
- import "./chunk-5HEE5U7V.js";
5
+ } from "./chunk-XR7ESTGM.js";
6
+ import "./chunk-G5DEDMPE.js";
7
7
  export {
8
8
  DEFAULT_PORT,
9
9
  createServer,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@crvouga/mockingbird-service-google-maps",
3
- "version": "1.0.0",
4
- "description": "Mock of Google Places Autocomplete / Details / Find Place, the Geocoding API and a Maps JavaScript (places) shim, over a QA address corpus, with Google's status codes and fault presets.",
3
+ "version": "1.1.1",
4
+ "description": "Mock of Google Places Autocomplete / Details / Find Place, the Geocoding API, a Maps JavaScript (places) shim and the Address Validation API (USPS CASS/DPV verdicts), over a QA address corpus, with Google's status codes and fault presets.",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "sideEffects": false,
@@ -42,7 +42,9 @@
42
42
  "maps",
43
43
  "places",
44
44
  "geocoding",
45
- "autocomplete"
45
+ "autocomplete",
46
+ "address-validation",
47
+ "usps"
46
48
  ],
47
49
  "mockingbird": {
48
50
  "runtime": "portable",