@visiolab/effect-firebase 0.0.0-stage → 0.3.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 (140) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +81 -2
  3. package/dist/_virtual/_rolldown/runtime.mjs +12 -0
  4. package/dist/app/App.d.mts +27 -0
  5. package/dist/app/App.mjs +27 -0
  6. package/dist/app/index.d.mts +2 -0
  7. package/dist/app/index.mjs +1 -0
  8. package/dist/firestore/Collection.d.mts +423 -0
  9. package/dist/firestore/Collection.mjs +133 -0
  10. package/dist/firestore/CollectionGroup.d.mts +164 -0
  11. package/dist/firestore/CollectionGroup.mjs +84 -0
  12. package/dist/firestore/DocumentPath.d.mts +95 -0
  13. package/dist/firestore/DocumentPath.mjs +71 -0
  14. package/dist/firestore/DocumentSnapshot.d.mts +239 -0
  15. package/dist/firestore/DocumentSnapshot.mjs +134 -0
  16. package/dist/firestore/FieldValue.d.mts +332 -0
  17. package/dist/firestore/FieldValue.mjs +256 -0
  18. package/dist/firestore/Firestore.d.mts +1165 -0
  19. package/dist/firestore/Firestore.mjs +274 -0
  20. package/dist/firestore/FirestoreModel.d.mts +1232 -0
  21. package/dist/firestore/FirestoreModel.mjs +586 -0
  22. package/dist/firestore/FirestoreSchema.d.mts +567 -0
  23. package/dist/firestore/FirestoreSchema.mjs +419 -0
  24. package/dist/firestore/FirestoreValue.d.mts +145 -0
  25. package/dist/firestore/FirestoreValue.mjs +87 -0
  26. package/dist/firestore/GeoPoint.d.mts +90 -0
  27. package/dist/firestore/GeoPoint.mjs +75 -0
  28. package/dist/firestore/Patch.d.mts +73 -0
  29. package/dist/firestore/Patch.mjs +57 -0
  30. package/dist/firestore/QuerySnapshot.d.mts +107 -0
  31. package/dist/firestore/QuerySnapshot.mjs +43 -0
  32. package/dist/firestore/Timestamp.d.mts +273 -0
  33. package/dist/firestore/Timestamp.mjs +255 -0
  34. package/dist/firestore/index.d.mts +14 -0
  35. package/dist/firestore/index.mjs +14 -0
  36. package/dist/firestore/internal/collection.mjs +156 -0
  37. package/dist/firestore/internal/collectionGroup.mjs +64 -0
  38. package/dist/firestore/internal/documentPath.d.mts +4 -0
  39. package/dist/firestore/internal/documentPath.mjs +42 -0
  40. package/dist/firestore/internal/documentSnapshot.mjs +63 -0
  41. package/dist/firestore/internal/fieldValue.d.mts +11 -0
  42. package/dist/firestore/internal/fieldValue.mjs +88 -0
  43. package/dist/firestore/internal/firestore.mjs +145 -0
  44. package/dist/firestore/internal/firestoreError.d.mts +29 -0
  45. package/dist/firestore/internal/firestoreError.mjs +30 -0
  46. package/dist/firestore/internal/firestoreModel.mjs +719 -0
  47. package/dist/firestore/internal/firestoreSchema.mjs +123 -0
  48. package/dist/firestore/internal/firestoreValue.mjs +108 -0
  49. package/dist/firestore/internal/firestoreValueWalk.d.mts +58 -0
  50. package/dist/firestore/internal/firestoreValueWalk.mjs +223 -0
  51. package/dist/firestore/internal/geoPoint.d.mts +4 -0
  52. package/dist/firestore/internal/geoPoint.mjs +39 -0
  53. package/dist/firestore/internal/patch.mjs +19 -0
  54. package/dist/firestore/internal/querySnapshot.mjs +24 -0
  55. package/dist/firestore/internal/schemaAST.mjs +35 -0
  56. package/dist/firestore/internal/snapshotExtra.mjs +35 -0
  57. package/dist/firestore/internal/streamFromListener.mjs +12 -0
  58. package/dist/firestore/internal/timestamp.d.mts +4 -0
  59. package/dist/firestore/internal/timestamp.mjs +66 -0
  60. package/dist/firestore/internal/timestampSchema.mjs +31 -0
  61. package/dist/firestore/internal/transactionFromCallback.mjs +66 -0
  62. package/dist/functions/Callable.d.mts +654 -0
  63. package/dist/functions/Callable.mjs +450 -0
  64. package/dist/functions/CallableError.d.mts +297 -0
  65. package/dist/functions/CallableError.mjs +256 -0
  66. package/dist/functions/CallableMiddleware.d.mts +213 -0
  67. package/dist/functions/CallableMiddleware.mjs +52 -0
  68. package/dist/functions/CallableRequest.d.mts +166 -0
  69. package/dist/functions/CallableRequest.mjs +18 -0
  70. package/dist/functions/Caller.d.mts +103 -0
  71. package/dist/functions/Caller.mjs +115 -0
  72. package/dist/functions/DocumentTrigger.d.mts +795 -0
  73. package/dist/functions/DocumentTrigger.mjs +566 -0
  74. package/dist/functions/FirebaseFunction.d.mts +79 -0
  75. package/dist/functions/FirebaseFunction.mjs +4 -0
  76. package/dist/functions/FunctionGroup.d.mts +175 -0
  77. package/dist/functions/FunctionGroup.mjs +64 -0
  78. package/dist/functions/index.d.mts +9 -0
  79. package/dist/functions/index.mjs +9 -0
  80. package/dist/functions/internal/callable.mjs +167 -0
  81. package/dist/functions/internal/callableMiddleware.d.mts +19 -0
  82. package/dist/functions/internal/callableMiddleware.mjs +15 -0
  83. package/dist/functions/internal/callableRequest.mjs +6 -0
  84. package/dist/functions/internal/documentTrigger.d.mts +10 -0
  85. package/dist/functions/internal/documentTrigger.mjs +73 -0
  86. package/dist/functions/internal/firebaseFunction.d.mts +5 -0
  87. package/dist/functions/internal/firebaseFunction.mjs +24 -0
  88. package/dist/functions/internal/functionGroup.mjs +36 -0
  89. package/package.json +72 -3
  90. package/src/app/App.ts +24 -0
  91. package/src/app/index.ts +7 -0
  92. package/src/firestore/Collection.ts +520 -0
  93. package/src/firestore/CollectionGroup.ts +245 -0
  94. package/src/firestore/DocumentPath.ts +114 -0
  95. package/src/firestore/DocumentSnapshot.ts +308 -0
  96. package/src/firestore/FieldValue.ts +375 -0
  97. package/src/firestore/Firestore.ts +1432 -0
  98. package/src/firestore/FirestoreModel.ts +1884 -0
  99. package/src/firestore/FirestoreSchema.ts +643 -0
  100. package/src/firestore/FirestoreValue.ts +207 -0
  101. package/src/firestore/GeoPoint.ts +105 -0
  102. package/src/firestore/Patch.ts +117 -0
  103. package/src/firestore/QuerySnapshot.ts +150 -0
  104. package/src/firestore/Timestamp.ts +302 -0
  105. package/src/firestore/index.ts +67 -0
  106. package/src/firestore/internal/collection.ts +246 -0
  107. package/src/firestore/internal/collectionGroup.ts +81 -0
  108. package/src/firestore/internal/documentPath.ts +53 -0
  109. package/src/firestore/internal/documentSnapshot.ts +117 -0
  110. package/src/firestore/internal/fieldValue.ts +119 -0
  111. package/src/firestore/internal/firestore.ts +322 -0
  112. package/src/firestore/internal/firestoreError.ts +36 -0
  113. package/src/firestore/internal/firestoreModel.ts +1242 -0
  114. package/src/firestore/internal/firestoreSchema.ts +218 -0
  115. package/src/firestore/internal/firestoreValue.ts +202 -0
  116. package/src/firestore/internal/firestoreValueWalk.ts +349 -0
  117. package/src/firestore/internal/geoPoint.ts +59 -0
  118. package/src/firestore/internal/patch.ts +21 -0
  119. package/src/firestore/internal/querySnapshot.ts +39 -0
  120. package/src/firestore/internal/schemaAST.ts +83 -0
  121. package/src/firestore/internal/snapshotExtra.ts +47 -0
  122. package/src/firestore/internal/streamFromListener.ts +27 -0
  123. package/src/firestore/internal/timestamp.ts +119 -0
  124. package/src/firestore/internal/timestampSchema.ts +42 -0
  125. package/src/firestore/internal/transactionFromCallback.ts +106 -0
  126. package/src/functions/Callable.ts +855 -0
  127. package/src/functions/CallableError.ts +348 -0
  128. package/src/functions/CallableMiddleware.ts +279 -0
  129. package/src/functions/CallableRequest.ts +207 -0
  130. package/src/functions/Caller.ts +141 -0
  131. package/src/functions/DocumentTrigger.ts +927 -0
  132. package/src/functions/FirebaseFunction.ts +96 -0
  133. package/src/functions/FunctionGroup.ts +225 -0
  134. package/src/functions/index.ts +62 -0
  135. package/src/functions/internal/callable.ts +272 -0
  136. package/src/functions/internal/callableMiddleware.ts +54 -0
  137. package/src/functions/internal/callableRequest.ts +8 -0
  138. package/src/functions/internal/documentTrigger.ts +143 -0
  139. package/src/functions/internal/firebaseFunction.ts +58 -0
  140. package/src/functions/internal/functionGroup.ts +47 -0
@@ -0,0 +1,123 @@
1
+ import { isDocumentPath, makeUnchecked } from "./documentPath.mjs";
2
+ import { arrayRemove, arrayUnion, isArrayRemove, isArrayUnion, isDeleteField, isIncrement, isServerTimestamp, serverTimestamp } from "./fieldValue.mjs";
3
+ import { isGeoPoint } from "./geoPoint.mjs";
4
+ import { Timestamp as Timestamp$1 } from "./timestampSchema.mjs";
5
+ import { toCodecFirestore } from "./firestoreValue.mjs";
6
+ import { fromPath, isDocumentReference, pattern } from "./collection.mjs";
7
+ import { Array, Effect, Equal, Schema, SchemaGetter, SchemaIssue, SchemaParser, SchemaTransformation } from "effect";
8
+ // src/firestore/internal/firestoreSchema.ts
9
+ /** @internal */
10
+ const Timestamp = Timestamp$1;
11
+ /** @internal */
12
+ const GeoPoint = Schema.declare(isGeoPoint, {
13
+ representation: {
14
+ id: "@visiolab/effect-firebase/firestore/GeoPoint",
15
+ payload: null
16
+ },
17
+ expected: "a Firestore GeoPoint",
18
+ toEquivalence: () => Equal.equals,
19
+ toCodecFirestore: () => void 0
20
+ });
21
+ /** @internal */
22
+ const DocumentPath = Schema.declare(isDocumentPath, {
23
+ representation: {
24
+ id: "@visiolab/effect-firebase/firestore/DocumentPath",
25
+ payload: null
26
+ },
27
+ expected: "a Firestore document path",
28
+ toEquivalence: () => Equal.equals,
29
+ toCodecFirestore: () => void 0
30
+ });
31
+ /** @internal */
32
+ const DocumentReference = (collection) => {
33
+ const pattern$1 = pattern(collection);
34
+ return Schema.declare((u) => isDocumentReference(u) && u.collection === collection, {
35
+ representation: {
36
+ id: "@visiolab/effect-firebase/firestore/DocumentReference",
37
+ payload: null
38
+ },
39
+ expected: `a reference to a document in ${pattern$1}`,
40
+ toEquivalence: () => Equal.equals,
41
+ toCodecFirestore: () => Schema.link()(DocumentPath.annotate({ expected: `a reference to a document in ${pattern$1}` }), {
42
+ decode: SchemaGetter.transformEffect((value, options) => Effect.fromOption(fromPath(collection, value.path)).pipe(Effect.mapError(() => new SchemaIssue.InvalidValue({ message: `Expected a document in ${pattern$1}, got "${value.path}"` }, value, options)))),
43
+ encode: SchemaGetter.transform((ref) => makeUnchecked(ref.path))
44
+ })
45
+ });
46
+ };
47
+ /** @internal */
48
+ const ServerTimestamp = Schema.declare(isServerTimestamp, {
49
+ representation: {
50
+ id: "@visiolab/effect-firebase/firestore/FieldValue/ServerTimestamp",
51
+ payload: null
52
+ },
53
+ expected: "a server timestamp",
54
+ toCodecFirestore: () => void 0
55
+ }).pipe(Schema.withConstructorDefault(Effect.succeed(serverTimestamp)));
56
+ /** @internal */
57
+ const ServerTimestampOr = (schema) => Schema.Union([schema, ServerTimestamp]);
58
+ /** @internal */
59
+ const Increment = Schema.declare(isIncrement, {
60
+ representation: {
61
+ id: "@visiolab/effect-firebase/firestore/FieldValue/Increment",
62
+ payload: null
63
+ },
64
+ expected: "an increment",
65
+ toEquivalence: () => Equal.equals,
66
+ toCodecFirestore: () => void 0
67
+ });
68
+ /** @internal */
69
+ const IncrementOr = (schema) => Schema.Union([schema, Increment]);
70
+ /** @internal */
71
+ const DeleteField = Schema.declare(isDeleteField, {
72
+ representation: {
73
+ id: "@visiolab/effect-firebase/firestore/FieldValue/DeleteField",
74
+ payload: null
75
+ },
76
+ expected: "a field deletion",
77
+ toCodecFirestore: () => void 0
78
+ });
79
+ /** @internal */
80
+ const DeleteFieldOr = (schema) => Schema.Union([DeleteField, schema]);
81
+ /**
82
+ * Declares a sentinel that carries elements, each of them run through `item` -
83
+ * decoded on the way in and encoded on the way out, as `Schema.Option` does
84
+ * with what it holds.
85
+ *
86
+ * Its Firestore form is the same sentinel, holding the elements in their own
87
+ * Firestore form: `toCodecFirestore` links it to a sentinel of `item`'s derived
88
+ * codec, so a `DateTimeUtc` among them is stored as a timestamp.
89
+ */
90
+ const elementsSentinel = (name, is, make) => {
91
+ const declareSentinel = (item, toCodecFirestore) => Schema.declareConstructor()([item], ([item]) => {
92
+ const elements = Schema.Array(item);
93
+ return (input, ast, options) => is(input) ? SchemaParser.decodeEffect(elements)(input.elements, options).pipe(Effect.mapBothEager({
94
+ onSuccess: (decoded) => make(...decoded),
95
+ onFailure: (issue) => new SchemaIssue.Pointer(["elements"], issue)
96
+ })) : Effect.fail(new SchemaIssue.InvalidType(ast, input, options));
97
+ }, {
98
+ representation: {
99
+ id: `@visiolab/effect-firebase/firestore/FieldValue/${name}`,
100
+ payload: null
101
+ },
102
+ expected: `an ${name}`,
103
+ toCodecFirestore,
104
+ toEquivalence: ([item]) => {
105
+ const elements = Array.makeEquivalence(item);
106
+ return (self, that) => elements(self.elements, that.elements);
107
+ }
108
+ });
109
+ return (item) => declareSentinel(item, ([encoded]) => Schema.link()(declareSentinel(toCodecFirestore(encoded), () => void 0), SchemaTransformation.passthrough()));
110
+ };
111
+ const ArrayUnionSentinel = elementsSentinel("ArrayUnion", isArrayUnion, arrayUnion);
112
+ /** @internal */
113
+ const ArrayUnion = (item) => ArrayUnionSentinel(item);
114
+ const ArrayRemoveSentinel = elementsSentinel("ArrayRemove", isArrayRemove, arrayRemove);
115
+ /** @internal */
116
+ const ArrayRemove = (item) => ArrayRemoveSentinel(item);
117
+ /** @internal */
118
+ const ArrayOr = (item) => Schema.Union([
119
+ Schema.Array(item),
120
+ ArrayUnion(item),
121
+ ArrayRemove(item)
122
+ ]);
123
+ export { ArrayOr, ArrayRemove, ArrayUnion, DeleteField, DeleteFieldOr, DocumentPath, DocumentReference, GeoPoint, Increment, IncrementOr, ServerTimestamp, ServerTimestampOr, Timestamp };
@@ -0,0 +1,108 @@
1
+ import { fromMillis, toDateTime, toMillis } from "./timestamp.mjs";
2
+ import { isFirestoreValue as isFirestoreValue$1 } from "./firestoreValueWalk.mjs";
3
+ import { applyToSelfOrLastLinkEncodingIdempotent, keywordToCodecJson, keywordToCodecStringTree, mapLink, parameterFromString, recur, replaceContextLastLink, replaceEncoding } from "./schemaAST.mjs";
4
+ import { Timestamp } from "./timestampSchema.mjs";
5
+ import { DateTime, Effect, Predicate, Schema, SchemaAST, SchemaGetter, SchemaIssue, SchemaTransformation } from "effect";
6
+ // src/firestore/internal/firestoreValue.ts
7
+ /** @internal */
8
+ const isFirestoreValue = isFirestoreValue$1;
9
+ /**
10
+ * Accepts only Firestore Values. A declaration with no link to any canonical
11
+ * format falls back to this, so a value Firestore cannot store fails at its own
12
+ * path instead of being stored as a map.
13
+ */
14
+ const FirestoreValueSchema = Schema.declare(isFirestoreValue, {
15
+ expected: "a Firestore value",
16
+ toCodecFirestore: () => void 0
17
+ });
18
+ /** @internal */
19
+ const unknownToFirestore = new SchemaAST.Link(FirestoreValueSchema.ast, SchemaTransformation.passthrough());
20
+ const timestampFromMillis = (millis, options) => {
21
+ const out = fromMillis(millis);
22
+ return out._tag === "Some" ? Effect.succeed(out.value) : Effect.fail(new SchemaIssue.InvalidValue({ message: "Expected an instant Firestore can store, between the years 1 and 9999" }, millis, options));
23
+ };
24
+ const dateToTimestamp = Schema.link()(Timestamp, {
25
+ decode: SchemaGetter.transform((ts) => new Date(toMillis(ts))),
26
+ encode: SchemaGetter.transformEffect((date, options) => timestampFromMillis(date.getTime(), options))
27
+ });
28
+ const dateTimeUtcToTimestamp = Schema.link()(Timestamp, {
29
+ decode: SchemaGetter.transform(toDateTime),
30
+ encode: SchemaGetter.transformEffect((dateTime, options) => timestampFromMillis(DateTime.toEpochMillis(dateTime), options))
31
+ });
32
+ const representationId = (ast) => {
33
+ const representation = ast.annotations?.representation;
34
+ return Predicate.hasProperty(representation, "id") ? representation.id : void 0;
35
+ };
36
+ /**
37
+ * Firestore's own types come first, so these win over the declaration's
38
+ * `toCodecJson`, which would write a string.
39
+ */
40
+ const nativeLink = (ast) => {
41
+ switch (representationId(ast)) {
42
+ case "effect/schema/Date": return dateToTimestamp;
43
+ case "effect/schema/DateTimeUtc": return dateTimeUtcToTimestamp;
44
+ case "effect/schema/Uint8Array": return null;
45
+ }
46
+ };
47
+ const declarationStep = (ast, recur) => {
48
+ const native = nativeLink(ast);
49
+ if (native === null) return ast;
50
+ if (native !== void 0) return replaceEncoding(ast, [native]);
51
+ const annotations = ast.annotations;
52
+ const getLink = [
53
+ annotations?.toCodecFirestore,
54
+ annotations?.toCodecJson,
55
+ annotations?.toCodec
56
+ ].find(Predicate.isFunction);
57
+ if (getLink === void 0) return replaceEncoding(ast, [unknownToFirestore]);
58
+ const link = getLink(ast.typeParameters.map((tp) => Schema.make(SchemaAST.toEncoded(tp))));
59
+ return link === void 0 ? ast : replaceEncoding(ast, [mapLink(link, recur)]);
60
+ };
61
+ const reorderUnion = (types) => {
62
+ const priority = (ast) => {
63
+ switch (SchemaAST.toEncoded(ast)._tag) {
64
+ case "BigInt":
65
+ case "Symbol":
66
+ case "UniqueSymbol": return 0;
67
+ default: return 1;
68
+ }
69
+ };
70
+ const sorted = [...types].sort((a, b) => priority(a) - priority(b));
71
+ return sorted.some((ast, i) => ast !== types[i]) ? sorted : types;
72
+ };
73
+ const step = (ast, recur$1) => {
74
+ switch (ast._tag) {
75
+ case "Declaration": return declarationStep(ast, recur$1);
76
+ case "Any":
77
+ case "Unknown":
78
+ case "ObjectKeyword": return replaceEncoding(ast, [unknownToFirestore]);
79
+ case "Undefined":
80
+ case "Void":
81
+ case "Literal": return keywordToCodecJson(ast);
82
+ case "UniqueSymbol":
83
+ case "Symbol":
84
+ case "BigInt": return keywordToCodecStringTree(ast);
85
+ case "Objects":
86
+ if (ast.propertySignatures.some((ps) => typeof ps.name !== "string")) return replaceEncoding(ast, [unknownToFirestore]);
87
+ return recur(ast, recur$1, parameterFromString);
88
+ case "Union": {
89
+ const types = reorderUnion(ast.types);
90
+ const union = types === ast.types ? ast : new SchemaAST.Union(types, ast.options, ast.annotations, ast.checks, ast.encoding, ast.context, ast.encodingChecks);
91
+ return recur(union, recur$1);
92
+ }
93
+ case "Arrays":
94
+ case "Suspend": return recur(ast, recur$1);
95
+ }
96
+ return ast;
97
+ };
98
+ const withoutConstructorDefault = (context) => context.constructorDefault === void 0 ? context : new SchemaAST.Context(context.isOptional, context.isMutable, void 0, context.annotations);
99
+ /** @internal */
100
+ const toCodecFirestoreAST = applyToSelfOrLastLinkEncodingIdempotent((ast) => {
101
+ const out = step(ast, toCodecFirestoreAST);
102
+ const context = ast.context;
103
+ if (out === ast || context === void 0) return out;
104
+ return replaceContextLastLink(out, withoutConstructorDefault(context));
105
+ });
106
+ /** @internal */
107
+ const toCodecFirestore = (schema) => Schema.make(toCodecFirestoreAST(schema.ast), { schema });
108
+ export { isFirestoreValue, toCodecFirestore, toCodecFirestoreAST, unknownToFirestore };
@@ -0,0 +1,58 @@
1
+ import "./firestoreError.mjs";
2
+ import { isDocumentPath } from "./documentPath.mjs";
3
+ import { isArrayRemove, isArrayUnion, isDeleteField, isIncrement, isServerTimestamp } from "./fieldValue.mjs";
4
+ import { isGeoPoint } from "./geoPoint.mjs";
5
+ import { isTimestamp } from "./timestamp.mjs";
6
+ import { FirestoreValue } from "../FirestoreValue.mjs";
7
+ import { Result } from "effect";
8
+ /** @internal */
9
+ export type FieldPath = ReadonlyArray<string | number>;
10
+ /**
11
+ * The kinds of Firestore Value other than scalars, arrays and maps, each with
12
+ * the guard that recognises it. Adding a kind here makes every SDK's
13
+ * {@link Writer} name it too.
14
+ */
15
+ declare const kinds: {
16
+ readonly bytes: (u: unknown) => u is Uint8Array;
17
+ readonly timestamp: typeof isTimestamp;
18
+ readonly geoPoint: typeof isGeoPoint;
19
+ readonly documentPath: typeof isDocumentPath;
20
+ readonly serverTimestamp: typeof isServerTimestamp;
21
+ readonly increment: typeof isIncrement;
22
+ readonly deleteField: typeof isDeleteField;
23
+ readonly arrayUnion: typeof isArrayUnion;
24
+ readonly arrayRemove: typeof isArrayRemove;
25
+ };
26
+ type Guarded<F> = F extends ((u: unknown) => u is infer A) ? A : never;
27
+ /**
28
+ * Each kind of Firestore Value that is not a scalar, array or map, by name.
29
+ *
30
+ * @internal
31
+ */
32
+ export type Kinds = { readonly [K in keyof typeof kinds]: Guarded<(typeof kinds)[K]>; };
33
+ /**
34
+ * Walks the value a kind holds, such as an array union's elements: `key` is
35
+ * where `child` sits within it.
36
+ *
37
+ * @internal
38
+ */
39
+ export type Walk = (child: unknown, key: string | number) => unknown;
40
+ /**
41
+ * How one SDK writes each kind of Firestore Value that is not a scalar, array
42
+ * or map.
43
+ *
44
+ * @internal
45
+ */
46
+ export type Writer = { readonly [K in keyof Kinds]: (value: Kinds[K], walk: Walk) => unknown; };
47
+ /**
48
+ * How one SDK's values read back as Firestore Values. It hands back
49
+ * `undefined` for a value it leaves to the walk - a scalar, array or map -
50
+ * and calls `fail` for one of its own it cannot read.
51
+ *
52
+ * @internal
53
+ */
54
+ export type Reader = (value: object, context: {
55
+ readonly walk: (child: unknown, key: string | number) => FirestoreValue;
56
+ readonly fail: (kind: string) => never;
57
+ }) => FirestoreValue | undefined;
58
+ export {};
@@ -0,0 +1,223 @@
1
+ import { FirestoreError } from "./firestoreError.mjs";
2
+ import { isDocumentPath } from "./documentPath.mjs";
3
+ import { isArrayRemove, isArrayUnion, isDeleteField, isIncrement, isServerTimestamp } from "./fieldValue.mjs";
4
+ import { isGeoPoint } from "./geoPoint.mjs";
5
+ import { isTimestamp } from "./timestamp.mjs";
6
+ import { Equal, Result } from "effect";
7
+ // src/firestore/internal/firestoreValueWalk.ts
8
+ /**
9
+ * The one walk over Firestore Values that the guard and both SDK mappings
10
+ * share: the path to each value, the scalars, arrays and maps, and how a
11
+ * value outside the union is reported. What differs per SDK is only how each
12
+ * kind of value in {@link kinds} becomes the SDK's own, and back.
13
+ */
14
+ /** @internal */
15
+ const formatFieldPath = (path) => path.length === 0 ? "the document" : `"${path.map((key, i) => typeof key === "number" ? `[${key}]` : i === 0 ? key : `.${key}`).join("")}"`;
16
+ /**
17
+ * The kinds of Firestore Value other than scalars, arrays and maps, each with
18
+ * the guard that recognises it. Adding a kind here makes every SDK's
19
+ * {@link Writer} name it too.
20
+ */
21
+ const kinds = {
22
+ bytes: (u) => u instanceof Uint8Array,
23
+ timestamp: isTimestamp,
24
+ geoPoint: isGeoPoint,
25
+ documentPath: isDocumentPath,
26
+ serverTimestamp: isServerTimestamp,
27
+ increment: isIncrement,
28
+ deleteField: isDeleteField,
29
+ arrayUnion: isArrayUnion,
30
+ arrayRemove: isArrayRemove
31
+ };
32
+ const kindNames = Object.keys(kinds);
33
+ const kindOf = (u) => kindNames.find((kind) => kinds[kind](u));
34
+ /** A map: a plain object whose keys are all strings. */
35
+ const isMap = (u) => {
36
+ const proto = Object.getPrototypeOf(u);
37
+ return (proto === Object.prototype || proto === null) && Object.getOwnPropertySymbols(u).length === 0;
38
+ };
39
+ const describe = (u) => {
40
+ const name = u.constructor?.name;
41
+ return name === void 0 ? "an object with no prototype" : `a ${name}`;
42
+ };
43
+ /** A value outside the Firestore Value union, at `path`. */
44
+ var Outside = class {
45
+ kind;
46
+ value;
47
+ path;
48
+ constructor(kind, value, path) {
49
+ this.kind = kind;
50
+ this.value = value;
51
+ this.path = path;
52
+ }
53
+ };
54
+ const unhandled = Symbol("unhandled");
55
+ /**
56
+ * Walks `value`, handing every object to `visit` first and then walking
57
+ * arrays and maps itself. Throws {@link Outside} at the first value that is
58
+ * none of these, or that contains itself.
59
+ */
60
+ const walk = (value, visit) => {
61
+ const path = [];
62
+ const ancestors = /* @__PURE__ */ new Set();
63
+ const within = (child, key) => {
64
+ path.push(key);
65
+ try {
66
+ return go(child);
67
+ } finally {
68
+ path.pop();
69
+ }
70
+ };
71
+ const go = (u) => {
72
+ switch (typeof u) {
73
+ case "boolean":
74
+ case "number":
75
+ case "string": return u;
76
+ case "object": break;
77
+ default: throw new Outside(`a value of type ${typeof u}`, u, [...path]);
78
+ }
79
+ if (u === null) return u;
80
+ const fail = (kind) => {
81
+ throw new Outside(kind, u, [...path]);
82
+ };
83
+ if (ancestors.has(u)) return fail("a value that contains itself");
84
+ ancestors.add(u);
85
+ try {
86
+ const out = visit(u, within, fail);
87
+ if (out !== unhandled) return out;
88
+ if (Array.isArray(u)) return u.map((e, i) => within(e, i));
89
+ if (isMap(u)) {
90
+ const map = {};
91
+ for (const key of Object.keys(u)) map[key] = within(u[key], key);
92
+ return map;
93
+ }
94
+ return fail(describe(u));
95
+ } finally {
96
+ ancestors.delete(u);
97
+ }
98
+ };
99
+ return go(value);
100
+ };
101
+ const write = (value, writer) => walk(value, (u, within) => {
102
+ const kind = kindOf(u);
103
+ return kind === void 0 ? unhandled : writer[kind](u, within);
104
+ });
105
+ const keep = (value) => value;
106
+ /** Builds nothing: walking with it only checks a value is a Firestore Value. */
107
+ const checker = {
108
+ bytes: keep,
109
+ timestamp: keep,
110
+ geoPoint: keep,
111
+ documentPath: keep,
112
+ serverTimestamp: keep,
113
+ increment: keep,
114
+ deleteField: keep,
115
+ arrayUnion: (u, within) => u.elements.forEach(within),
116
+ arrayRemove: (u, within) => u.elements.forEach(within)
117
+ };
118
+ /** @internal */
119
+ const isFirestoreValue = (u) => {
120
+ try {
121
+ write(u, checker);
122
+ return true;
123
+ } catch (e) {
124
+ if (e instanceof Outside) return false;
125
+ throw e;
126
+ }
127
+ };
128
+ /**
129
+ * Whether two Firestore Values are the same, as Firestore would store them:
130
+ * scalars by value, arrays and maps element by element and key by key, bytes
131
+ * byte by byte, and every other kind as that kind tells its values apart.
132
+ *
133
+ * @internal
134
+ */
135
+ const equals = (self, that) => {
136
+ if (Object.is(self, that)) return true;
137
+ if (typeof self !== "object" || typeof that !== "object" || self === null || that === null) return false;
138
+ if (Array.isArray(self) || Array.isArray(that)) return Array.isArray(self) && Array.isArray(that) && self.length === that.length && self.every((element, i) => equals(element, that[i]));
139
+ const kind = kindOf(self);
140
+ if (kind !== kindOf(that)) return false;
141
+ switch (kind) {
142
+ case void 0: return isMap(self) && isMap(that) && Object.keys(self).length === Object.keys(that).length && Object.keys(self).every((key) => Object.hasOwn(that, key) && equals(self[key], that[key]));
143
+ case "bytes": {
144
+ const a = self;
145
+ const b = that;
146
+ return a.length === b.length && a.every((byte, i) => byte === b[i]);
147
+ }
148
+ case "arrayUnion":
149
+ case "arrayRemove": return equals(self.elements, that.elements);
150
+ default: return Equal.equals(self, that);
151
+ }
152
+ };
153
+ /**
154
+ * Turns a Firestore Value into an SDK's own value through `writer`. A value
155
+ * outside the union is a defect, since `toCodecFirestore` only ever encodes
156
+ * Firestore Values.
157
+ *
158
+ * @internal
159
+ */
160
+ const toSdk = (value, writer) => {
161
+ try {
162
+ return write(value, writer);
163
+ } catch (e) {
164
+ if (!(e instanceof Outside)) throw e;
165
+ throw new globalThis.Error(`Cannot write ${e.kind} at ${formatFieldPath(e.path)}, which is not a Firestore Value`, { cause: e.value });
166
+ }
167
+ };
168
+ /**
169
+ * Turns what an SDK read into a Firestore Value through `reader`, failing on
170
+ * a value outside the union, such as a vector, and naming the field that
171
+ * holds it.
172
+ *
173
+ * @internal
174
+ */
175
+ const fromSdk = (value, reader) => {
176
+ try {
177
+ return Result.succeed(walk(value, (u, within, fail) => {
178
+ const out = reader(u, {
179
+ walk: within,
180
+ fail
181
+ });
182
+ return out === void 0 ? unhandled : out;
183
+ }));
184
+ } catch (e) {
185
+ if (!(e instanceof Outside)) throw e;
186
+ return Result.fail(new FirestoreError({
187
+ message: `Firestore holds ${e.kind} at ${formatFieldPath(e.path)}, which is not a Firestore Value`,
188
+ code: void 0,
189
+ cause: e.value
190
+ }));
191
+ }
192
+ };
193
+ /**
194
+ * Reads what an SDK holds for the document at `path` as Firestore Values
195
+ * through `reader`, keeping `undefined` - a document that is not there - as
196
+ * it is.
197
+ *
198
+ * @internal
199
+ */
200
+ const readDocument = (path, data, reader) => data === void 0 ? Result.succeed(void 0) : Result.mapError(fromSdk(data, reader), (error) => new FirestoreError({
201
+ message: `Failed to read the document "${path}": ${error.message}`,
202
+ code: void 0,
203
+ cause: error.cause
204
+ }));
205
+ /**
206
+ * Reads Firestore Values as they are, walking into the values a kind holds,
207
+ * so that only a value outside the union fails.
208
+ */
209
+ const asIs = (u, { walk }) => {
210
+ const kind = kindOf(u);
211
+ if (kind === "arrayUnion" || kind === "arrayRemove") u.elements.forEach((element, i) => walk(element, i));
212
+ return kind === void 0 ? void 0 : u;
213
+ };
214
+ /**
215
+ * Checks that what a backend read for the document at `path` is made of
216
+ * Firestore Values, failing as {@link readDocument} does on one that is not.
217
+ * It hands back `data` itself, so `undefined` - a document that is not there -
218
+ * stays as it is.
219
+ *
220
+ * @internal
221
+ */
222
+ const checkDocument = (path, data) => Result.map(readDocument(path, data, asIs), () => data);
223
+ export { checkDocument, equals, formatFieldPath, fromSdk, isFirestoreValue, readDocument, toSdk };
@@ -0,0 +1,4 @@
1
+ import { GeoPoint } from "../GeoPoint.mjs";
2
+ import { Option } from "effect";
3
+ /** @internal */
4
+ export declare const isGeoPoint: (u: unknown) => u is GeoPoint;
@@ -0,0 +1,39 @@
1
+ import { Equal, Hash, Inspectable, Option, Predicate } from "effect";
2
+ // src/firestore/internal/geoPoint.ts
3
+ /** @internal */
4
+ const TypeId = "~@visiolab/effect-firebase/firestore/GeoPoint";
5
+ /** Kept apart from the guards below, which narrow a `number` to `never`. */
6
+ const inLatitudeRange = (n) => Number.isFinite(n) && n >= -90 && n <= 90;
7
+ const inLongitudeRange = (n) => Number.isFinite(n) && n >= -180 && n <= 180;
8
+ /** @internal */
9
+ const isGeoPoint = (u) => Predicate.hasProperty(u, "~@visiolab/effect-firebase/firestore/GeoPoint") && u["~@visiolab/effect-firebase/firestore/GeoPoint"] === "~@visiolab/effect-firebase/firestore/GeoPoint" && Predicate.hasProperty(u, "latitude") && Predicate.hasProperty(u, "longitude") && Predicate.isNumber(u.latitude) && Predicate.isNumber(u.longitude) && inLatitudeRange(u.latitude) && inLongitudeRange(u.longitude);
10
+ const Proto = {
11
+ ...Inspectable.BaseProto,
12
+ [TypeId]: TypeId,
13
+ toJSON() {
14
+ return {
15
+ _id: "GeoPoint",
16
+ latitude: this.latitude,
17
+ longitude: this.longitude
18
+ };
19
+ },
20
+ [Equal.symbol](that) {
21
+ return isGeoPoint(that) && this.latitude === that.latitude && this.longitude === that.longitude;
22
+ },
23
+ [Hash.symbol]() {
24
+ return Hash.combine(Hash.number(this.latitude), Hash.number(this.longitude));
25
+ }
26
+ };
27
+ const makeUnchecked = (latitude, longitude) => Object.assign(Object.create(Proto), {
28
+ latitude,
29
+ longitude
30
+ });
31
+ /** @internal */
32
+ const makeUnsafe = (latitude, longitude) => {
33
+ if (!inLatitudeRange(latitude)) throw new Error(`Invalid Firestore geopoint: latitude must be a number between -90 and 90, got ${latitude}`);
34
+ if (!inLongitudeRange(longitude)) throw new Error(`Invalid Firestore geopoint: longitude must be a number between -180 and 180, got ${longitude}`);
35
+ return makeUnchecked(latitude, longitude);
36
+ };
37
+ /** @internal */
38
+ const make = (latitude, longitude) => inLatitudeRange(latitude) && inLongitudeRange(longitude) ? Option.some(makeUnchecked(latitude, longitude)) : Option.none();
39
+ export { TypeId, isGeoPoint, make, makeUnsafe };
@@ -0,0 +1,19 @@
1
+ import { Inspectable, Predicate } from "effect";
2
+ // src/firestore/internal/patch.ts
3
+ /** @internal */
4
+ const ReplaceTypeId = "~@visiolab/effect-firebase/firestore/Patch/Replace";
5
+ const ReplaceProto = {
6
+ ...Inspectable.BaseProto,
7
+ [ReplaceTypeId]: ReplaceTypeId,
8
+ toJSON() {
9
+ return {
10
+ _id: "Replace",
11
+ value: Inspectable.toJson(this.value)
12
+ };
13
+ }
14
+ };
15
+ /** @internal */
16
+ const isReplace = (u) => Predicate.hasProperty(u, ReplaceTypeId);
17
+ /** @internal */
18
+ const replace = (value) => Object.assign(Object.create(ReplaceProto), { value });
19
+ export { ReplaceTypeId, isReplace, replace };
@@ -0,0 +1,24 @@
1
+ import { attach, extraToJson } from "./snapshotExtra.mjs";
2
+ import { Inspectable, Predicate } from "effect";
3
+ // src/firestore/internal/querySnapshot.ts
4
+ /** @internal */
5
+ const TypeId = "~@visiolab/effect-firebase/firestore/QuerySnapshot";
6
+ /** @internal */
7
+ const isQuerySnapshot = (u) => Predicate.hasProperty(u, TypeId);
8
+ const QuerySnapshotProto = {
9
+ ...Inspectable.BaseProto,
10
+ [TypeId]: TypeId,
11
+ toJSON() {
12
+ return {
13
+ _id: "QuerySnapshot",
14
+ docs: this.docs.map((doc) => doc.toJSON()),
15
+ ...extraToJson(this)
16
+ };
17
+ }
18
+ };
19
+ /** @internal */
20
+ const make = (docs, extra) => {
21
+ const self = Object.assign(Object.create(QuerySnapshotProto), { docs });
22
+ return attach(self, extra);
23
+ };
24
+ export { TypeId, isQuerySnapshot, make };
@@ -0,0 +1,35 @@
1
+ import { SchemaAST } from "effect";
2
+ // src/firestore/internal/schemaAST.ts
3
+ /**
4
+ * The one place this package reaches into Effect's `@internal` `SchemaAST`
5
+ * helpers.
6
+ *
7
+ * Effect has no public way to define a canonical format, so deriving
8
+ * `toCodecFirestore` the way `toCodecJson` is derived needs the same internal
9
+ * helpers Effect uses for it (ADR 0002). They exist at runtime but are stripped
10
+ * from Effect's type declarations, so each is typed here by hand, against the
11
+ * pinned `effect` version. Keeping them together means a future public API for
12
+ * canonical formats is a swap of this module only.
13
+ */
14
+ const internals = SchemaAST;
15
+ /** Replaces the encoding chain of `ast`, leaving `ast` alone when it is the same. */
16
+ const replaceEncoding = internals.replaceEncoding;
17
+ /** Maps the target of `link`, leaving `link` alone when the target is unchanged. */
18
+ const mapLink = internals.mapLink;
19
+ /** Replaces the context of the last link's target. */
20
+ const replaceContextLastLink = internals.replaceContextLastLink;
21
+ /**
22
+ * Applies `f` to the node itself when it has no encoding, or to the target of
23
+ * its last encoding link otherwise, so user transformations run first. The
24
+ * result is memoized per node.
25
+ */
26
+ const applyToSelfOrLastLinkEncodingIdempotent = internals.applyToSelfOrLastLinkEncodingIdempotent;
27
+ /** How a canonical format reads index-signature and property keys back from strings. */
28
+ const parameterFromString = internals.parameterFromString;
29
+ /** The keyword's own canonical JSON codec, as `toCodecJson` derives it. */
30
+ const keywordToCodecJson = (ast) => ast.toCodecJson();
31
+ /** The keyword's own canonical StringTree codec, as `toCodecStringTree` derives it. */
32
+ const keywordToCodecStringTree = (ast) => ast.toCodecStringTree();
33
+ /** Recurs into the members of a composite node. */
34
+ const recur = (ast, f, onKey) => ast.recur(f, onKey);
35
+ export { applyToSelfOrLastLinkEncodingIdempotent, keywordToCodecJson, keywordToCodecStringTree, mapLink, parameterFromString, recur, replaceContextLastLink, replaceEncoding };
@@ -0,0 +1,35 @@
1
+ import { Inspectable } from "effect";
2
+ // src/firestore/internal/snapshotExtra.ts
3
+ /**
4
+ * Where a snapshot keeps the extra an SDK built it with, so that it can show
5
+ * that extra when it prints without telling the extra's fields from its own.
6
+ */
7
+ const ExtraKey = "~@visiolab/effect-firebase/firestore/SnapshotExtra";
8
+ /**
9
+ * `self` with the fields of `extra` on it, remembered as its extra so that
10
+ * {@link extraToJson} shows them.
11
+ *
12
+ * The extra itself is held on a key that is not enumerable, so that it does
13
+ * not show up twice: its fields are already on the snapshot.
14
+ *
15
+ * @internal
16
+ */
17
+ const attach = (self, extra) => {
18
+ if (extra === void 0) return self;
19
+ Object.defineProperty(self, ExtraKey, { value: extra });
20
+ return Object.assign(self, extra);
21
+ };
22
+ /**
23
+ * How the extra `self` was built with prints: each of its fields as it would
24
+ * print on its own, or nothing when it was built without one.
25
+ *
26
+ * @internal
27
+ */
28
+ const extraToJson = (self) => {
29
+ const extra = self[ExtraKey];
30
+ if (extra === void 0) return {};
31
+ const json = {};
32
+ for (const [key, value] of Object.entries(extra)) json[key] = Inspectable.toJson(value);
33
+ return json;
34
+ };
35
+ export { attach, extraToJson };