@visiolab/effect-firebase 0.0.0-stage → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 +66 -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,156 @@
1
+ import { isDocumentSnapshot } from "./documentSnapshot.mjs";
2
+ import { Equal, Hash, Inspectable, Option, Predicate, Schema } from "effect";
3
+ // src/firestore/internal/collection.ts
4
+ /** @internal */
5
+ const CollectionTypeId = "~@visiolab/effect-firebase/firestore/Collection";
6
+ /** @internal */
7
+ const CollectionReferenceTypeId = "~@visiolab/effect-firebase/firestore/CollectionReference";
8
+ /** @internal */
9
+ const DocumentReferenceTypeId = "~@visiolab/effect-firebase/firestore/DocumentReference";
10
+ /** @internal */
11
+ const isCollection = (u) => Predicate.hasProperty(u, CollectionTypeId);
12
+ /** @internal */
13
+ const isCollectionReference = (u) => Predicate.hasProperty(u, CollectionReferenceTypeId);
14
+ /** @internal */
15
+ const isDocumentReference = (u) => Predicate.hasProperty(u, DocumentReferenceTypeId);
16
+ const encoder = new TextEncoder();
17
+ /** Rejects the segments Firestore cannot address, before they reach a path. */
18
+ const validateSegment = (segment) => {
19
+ const reject = (reason) => {
20
+ throw new Error(`Invalid Firestore collection id "${segment}": ${reason}`);
21
+ };
22
+ if (segment.length === 0) reject("it must not be empty");
23
+ if (segment.includes("/")) reject("it must not contain \"/\"");
24
+ if (segment === "." || segment === "..") reject("it must not be \".\" or \"..\"");
25
+ if (/^__.*__$/.test(segment)) reject("it must not match \"__.*__\"");
26
+ if (encoder.encode(segment).length > 1500) reject("it must be at most 1500 bytes");
27
+ };
28
+ /**
29
+ * Brands an id schema with the path addressing it.
30
+ *
31
+ * The path is only known at runtime, and `Schema.brand` rejects an open `string`
32
+ * key. Narrowing the key to a placeholder literal keeps the real path on the
33
+ * annotation: the static brand is erased where `makeCollection` takes the schema
34
+ * as `Schema.Top`, and the public id types are built above it.
35
+ */
36
+ const brandId = (path) => Schema.String.pipe(Schema.brand(path.join("/")));
37
+ /** Interleaves the segments with the ids addressing them: `a/1/b/2/c`. */
38
+ const joinPath = (segments, ancestors) => {
39
+ let path = segments[0];
40
+ for (let i = 0; i < ancestors.length; i++) path += `/${ancestors[i]}/${segments[i + 1]}`;
41
+ return path;
42
+ };
43
+ const CollectionProto = {
44
+ ...Inspectable.BaseProto,
45
+ [CollectionTypeId]: CollectionTypeId,
46
+ toJSON() {
47
+ return {
48
+ _id: "Collection",
49
+ path: this.path.join("/")
50
+ };
51
+ },
52
+ subcollection(segment, schema) {
53
+ validateSegment(segment);
54
+ const path = [...this.path, segment];
55
+ return makeCollection(path, schema, brandId(path));
56
+ },
57
+ collection(...ancestors) {
58
+ return makeCollectionReference(this, ancestors);
59
+ },
60
+ doc(...ids) {
61
+ const parent = makeCollectionReference(this, ids.slice(0, -1));
62
+ return makeDocumentReference(parent, ids[ids.length - 1]);
63
+ }
64
+ };
65
+ /**
66
+ * The target is asserted as the prototype rather than as the finished shape, so
67
+ * that the result type is inferred from the fields below. The return annotation
68
+ * then catches a field that is missing, misspelled or of the wrong type -
69
+ * asserting the finished shape up front would check none of the three.
70
+ */
71
+ const makeCollection = (path, schema, id) => {
72
+ const self = Object.assign(Object.create(CollectionProto), {
73
+ path,
74
+ segment: path[path.length - 1],
75
+ schema,
76
+ id,
77
+ contains: (value) => isDocumentReference(value) ? value.collection === self : isDocumentSnapshot(value) && value.ref.collection === self
78
+ });
79
+ return self;
80
+ };
81
+ const CollectionReferenceProto = {
82
+ ...Inspectable.BaseProto,
83
+ [CollectionReferenceTypeId]: CollectionReferenceTypeId,
84
+ toJSON() {
85
+ return {
86
+ _id: "CollectionReference",
87
+ path: this.path
88
+ };
89
+ },
90
+ [Equal.symbol](that) {
91
+ return isCollectionReference(that) && this.path === that.path && this.collection === that.collection;
92
+ },
93
+ [Hash.symbol]() {
94
+ return Hash.combine(Hash.string(this.path), Hash.random(this.collection));
95
+ },
96
+ doc(id) {
97
+ return makeDocumentReference(this, id);
98
+ }
99
+ };
100
+ const makeCollectionReference = (collection, ancestors) => Object.assign(Object.create(CollectionReferenceProto), {
101
+ collection,
102
+ ancestors,
103
+ id: collection.segment,
104
+ path: joinPath(collection.path, ancestors)
105
+ });
106
+ const DocumentReferenceProto = {
107
+ ...Inspectable.BaseProto,
108
+ [DocumentReferenceTypeId]: DocumentReferenceTypeId,
109
+ toJSON() {
110
+ return {
111
+ _id: "DocumentReference",
112
+ path: this.path
113
+ };
114
+ },
115
+ [Equal.symbol](that) {
116
+ return isDocumentReference(that) && this.path === that.path && this.collection === that.collection;
117
+ },
118
+ [Hash.symbol]() {
119
+ return Hash.combine(Hash.string(this.path), Hash.random(this.collection));
120
+ }
121
+ };
122
+ /** A document is always built from the collection holding it, which it keeps. */
123
+ const makeDocumentReference = (parent, id) => Object.assign(Object.create(DocumentReferenceProto), {
124
+ collection: parent.collection,
125
+ ancestors: parent.ancestors,
126
+ id,
127
+ path: `${parent.path}/${id}`,
128
+ parent
129
+ });
130
+ /** @internal */
131
+ const make = (segment, schema) => {
132
+ validateSegment(segment);
133
+ const path = [segment];
134
+ return makeCollection(path, schema, brandId(path));
135
+ };
136
+ /** @internal */
137
+ const fromPath = (collection, path) => {
138
+ const self = collection;
139
+ const parts = path.split("/");
140
+ if (parts.length !== self.path.length * 2) return Option.none();
141
+ const ids = [];
142
+ for (let i = 0; i < self.path.length; i++) {
143
+ const id = parts[2 * i + 1];
144
+ if (parts[2 * i] !== self.path[i] || id.length === 0) return Option.none();
145
+ ids.push(id);
146
+ }
147
+ return Option.some(self.doc(...ids));
148
+ };
149
+ /**
150
+ * The path a document in `collection` is found at, with `{id}` standing in for
151
+ * each id: `users/{id}/posts/{id}`.
152
+ *
153
+ * @internal
154
+ */
155
+ const pattern = (collection) => collection.path.map((segment) => `${segment}/{id}`).join("/");
156
+ export { CollectionReferenceTypeId, CollectionTypeId, DocumentReferenceTypeId, fromPath, isCollection, isCollectionReference, isDocumentReference, make, pattern };
@@ -0,0 +1,64 @@
1
+ import { UndefinedCollectionError } from "./firestoreError.mjs";
2
+ import { fromPath, pattern } from "./collection.mjs";
3
+ import { Inspectable, Option, Predicate, Result } from "effect";
4
+ // src/firestore/internal/collectionGroup.ts
5
+ /** @internal */
6
+ const TypeId = "~@visiolab/effect-firebase/firestore/CollectionGroup";
7
+ /** @internal */
8
+ const isCollectionGroup = (u) => Predicate.hasProperty(u, TypeId);
9
+ /**
10
+ * Where `member`'s collections sit, with `{id}` standing in for each ancestor
11
+ * id: `users/{id}/posts`.
12
+ */
13
+ const memberPattern = (member) => pattern(member).slice(0, -5);
14
+ const CollectionGroupProto = {
15
+ ...Inspectable.BaseProto,
16
+ [TypeId]: TypeId,
17
+ toJSON() {
18
+ return {
19
+ _id: "CollectionGroup",
20
+ members: this.members.map(memberPattern)
21
+ };
22
+ }
23
+ };
24
+ /**
25
+ * Rejects the groups that would not mean one thing: one with nothing in it,
26
+ * one whose collections Firestore could not read under one collection id, and
27
+ * one with two collections at the same path, which no document could be told
28
+ * apart between.
29
+ */
30
+ const validate = (members) => {
31
+ const reject = (reason) => {
32
+ throw new Error(`Invalid collection group: ${reason}`);
33
+ };
34
+ if (members.length === 0) reject("it must hold at least one collection");
35
+ const [first] = members;
36
+ const seen = /* @__PURE__ */ new Set();
37
+ for (const member of members) {
38
+ if (member.segment !== first.segment) reject(`"${memberPattern(first)}" and "${memberPattern(member)}" do not share their collection id`);
39
+ const pattern = memberPattern(member);
40
+ if (seen.has(pattern)) reject(`it has more than one member at "${pattern}"`);
41
+ seen.add(pattern);
42
+ }
43
+ };
44
+ /** @internal */
45
+ const make = (...valid) => {
46
+ const members = valid;
47
+ validate(members);
48
+ return Object.assign(Object.create(CollectionGroupProto), {
49
+ members,
50
+ id: members[0].segment
51
+ });
52
+ };
53
+ /** @internal */
54
+ const documentAt = (group, path) => {
55
+ for (const member of group.members) {
56
+ const ref = fromPath(member, path);
57
+ if (Option.isSome(ref)) return Result.succeed(ref.value);
58
+ }
59
+ return Result.fail(new UndefinedCollectionError({
60
+ path,
61
+ collectionId: group.id
62
+ }));
63
+ };
64
+ export { TypeId, documentAt, isCollectionGroup, make };
@@ -0,0 +1,4 @@
1
+ import { DocumentPath } from "../DocumentPath.mjs";
2
+ import { Option } from "effect";
3
+ /** @internal */
4
+ export declare const isDocumentPath: (u: unknown) => u is DocumentPath;
@@ -0,0 +1,42 @@
1
+ import { Equal, Hash, Inspectable, Option, Predicate } from "effect";
2
+ // src/firestore/internal/documentPath.ts
3
+ /** @internal */
4
+ const TypeId = "~@visiolab/effect-firebase/firestore/DocumentPath";
5
+ /** A collection id, then a document id, and so on: an even number of non-empty segments. */
6
+ const addressesDocument = (path) => {
7
+ const segments = path.split("/");
8
+ return segments.length % 2 === 0 && segments.every((segment) => segment.length > 0);
9
+ };
10
+ /** @internal */
11
+ const isDocumentPath = (u) => Predicate.hasProperty(u, "~@visiolab/effect-firebase/firestore/DocumentPath") && u["~@visiolab/effect-firebase/firestore/DocumentPath"] === "~@visiolab/effect-firebase/firestore/DocumentPath" && Predicate.hasProperty(u, "path") && Predicate.isString(u.path);
12
+ const Proto = {
13
+ ...Inspectable.BaseProto,
14
+ [TypeId]: TypeId,
15
+ toJSON() {
16
+ return {
17
+ _id: "DocumentPath",
18
+ path: this.path
19
+ };
20
+ },
21
+ [Equal.symbol](that) {
22
+ return isDocumentPath(that) && this.path === that.path;
23
+ },
24
+ [Hash.symbol]() {
25
+ return Hash.string(this.path);
26
+ }
27
+ };
28
+ /**
29
+ * Builds the value without checking `path`, for one that already addresses a
30
+ * document, such as a reference's own.
31
+ *
32
+ * @internal
33
+ */
34
+ const makeUnchecked = (path) => Object.assign(Object.create(Proto), { path });
35
+ /** @internal */
36
+ const make = (path) => addressesDocument(path) ? Option.some(makeUnchecked(path)) : Option.none();
37
+ /** @internal */
38
+ const makeUnsafe = (path) => {
39
+ if (!addressesDocument(path)) throw new Error(`Invalid Firestore document path "${path}": it must address a document`);
40
+ return makeUnchecked(path);
41
+ };
42
+ export { TypeId, isDocumentPath, make, makeUnchecked, makeUnsafe };
@@ -0,0 +1,63 @@
1
+ import { toCodecFirestore } from "./firestoreValue.mjs";
2
+ import { attach, extraToJson } from "./snapshotExtra.mjs";
3
+ import { Effect, Inspectable, Predicate, Schema } from "effect";
4
+ // src/firestore/internal/documentSnapshot.ts
5
+ /** @internal */
6
+ const TypeId = "~@visiolab/effect-firebase/firestore/DocumentSnapshot";
7
+ /** @internal */
8
+ const isDocumentSnapshot = (u) => Predicate.hasProperty(u, TypeId);
9
+ /** @internal */
10
+ const isExisting = (self) => self._tag === "Existing";
11
+ /** @internal */
12
+ const isMissing = (self) => self._tag === "Missing";
13
+ const ExistingProto = {
14
+ ...Inspectable.BaseProto,
15
+ [TypeId]: TypeId,
16
+ _tag: "Existing",
17
+ toJSON() {
18
+ return {
19
+ _id: "DocumentSnapshot",
20
+ _tag: "Existing",
21
+ ref: this.ref.path,
22
+ raw: Inspectable.toJson(this.raw),
23
+ ...extraToJson(this)
24
+ };
25
+ },
26
+ get data() {
27
+ const self = this;
28
+ return Effect.suspend(() => Schema.decodeUnknownEffect(toCodecFirestore(self.ref.collection.schema))(self.raw));
29
+ }
30
+ };
31
+ const MissingProto = {
32
+ ...Inspectable.BaseProto,
33
+ [TypeId]: TypeId,
34
+ _tag: "Missing",
35
+ toJSON() {
36
+ return {
37
+ _id: "DocumentSnapshot",
38
+ _tag: "Missing",
39
+ ref: this.ref.path,
40
+ ...extraToJson(this)
41
+ };
42
+ }
43
+ };
44
+ /** @internal */
45
+ const existing = (ref, data, extra) => {
46
+ const self = Object.assign(Object.create(ExistingProto), {
47
+ ref,
48
+ id: ref.id,
49
+ raw: data
50
+ });
51
+ return attach(self, extra);
52
+ };
53
+ /** @internal */
54
+ const missing = (ref, extra) => {
55
+ const self = Object.assign(Object.create(MissingProto), {
56
+ ref,
57
+ id: ref.id
58
+ });
59
+ return attach(self, extra);
60
+ };
61
+ /** @internal */
62
+ const make = (ref, data, extra) => data === void 0 ? missing(ref, extra) : existing(ref, data, extra);
63
+ export { TypeId, existing, isDocumentSnapshot, isExisting, isMissing, make, missing };
@@ -0,0 +1,11 @@
1
+ import { ArrayRemove, ArrayUnion, DeleteField, Increment, ServerTimestamp } from "../FieldValue.mjs";
2
+ /** @internal */
3
+ export declare const isServerTimestamp: (u: unknown) => u is ServerTimestamp;
4
+ /** @internal */
5
+ export declare const isIncrement: (u: unknown) => u is Increment;
6
+ /** @internal */
7
+ export declare const isDeleteField: (u: unknown) => u is DeleteField;
8
+ /** @internal */
9
+ export declare const isArrayUnion: (u: unknown) => u is ArrayUnion<unknown>;
10
+ /** @internal */
11
+ export declare const isArrayRemove: (u: unknown) => u is ArrayRemove<unknown>;
@@ -0,0 +1,88 @@
1
+ import { Equal, Hash, Inspectable, Predicate } from "effect";
2
+ // src/firestore/internal/fieldValue.ts
3
+ /** @internal */
4
+ const ServerTimestampTypeId = "~@visiolab/effect-firebase/firestore/FieldValue/ServerTimestamp";
5
+ const ServerTimestampProto = {
6
+ ...Inspectable.BaseProto,
7
+ [ServerTimestampTypeId]: ServerTimestampTypeId,
8
+ toJSON() {
9
+ return { _id: "ServerTimestamp" };
10
+ }
11
+ };
12
+ /** @internal */
13
+ const serverTimestamp = Object.create(ServerTimestampProto);
14
+ /** @internal */
15
+ const isServerTimestamp = (u) => Predicate.hasProperty(u, ServerTimestampTypeId);
16
+ /** @internal */
17
+ const IncrementTypeId = "~@visiolab/effect-firebase/firestore/FieldValue/Increment";
18
+ /** @internal */
19
+ const isIncrement = (u) => Predicate.hasProperty(u, IncrementTypeId);
20
+ const IncrementProto = {
21
+ ...Inspectable.BaseProto,
22
+ [IncrementTypeId]: IncrementTypeId,
23
+ toJSON() {
24
+ return {
25
+ _id: "Increment",
26
+ by: this.by
27
+ };
28
+ },
29
+ [Equal.symbol](that) {
30
+ return isIncrement(that) && this.by === that.by;
31
+ },
32
+ [Hash.symbol]() {
33
+ return Hash.combine(Hash.string(IncrementTypeId), Hash.number(this.by));
34
+ }
35
+ };
36
+ /** @internal */
37
+ const increment = (by) => {
38
+ if (!Number.isFinite(by)) throw new Error(`Invalid Firestore increment: by must be a finite number, got ${by}`);
39
+ return Object.assign(Object.create(IncrementProto), { by });
40
+ };
41
+ /** @internal */
42
+ const decrement = (by) => increment(-by);
43
+ /** @internal */
44
+ const DeleteFieldTypeId = "~@visiolab/effect-firebase/firestore/FieldValue/DeleteField";
45
+ const DeleteFieldProto = {
46
+ ...Inspectable.BaseProto,
47
+ [DeleteFieldTypeId]: DeleteFieldTypeId,
48
+ toJSON() {
49
+ return { _id: "DeleteField" };
50
+ }
51
+ };
52
+ /** @internal */
53
+ const deleteField = Object.create(DeleteFieldProto);
54
+ /** @internal */
55
+ const isDeleteField = (u) => Predicate.hasProperty(u, DeleteFieldTypeId);
56
+ /** @internal */
57
+ const ArrayUnionTypeId = "~@visiolab/effect-firebase/firestore/FieldValue/ArrayUnion";
58
+ const ArrayUnionProto = {
59
+ ...Inspectable.BaseProto,
60
+ [ArrayUnionTypeId]: ArrayUnionTypeId,
61
+ toJSON() {
62
+ return {
63
+ _id: "ArrayUnion",
64
+ elements: this.elements.map((element) => Inspectable.toJson(element))
65
+ };
66
+ }
67
+ };
68
+ /** @internal */
69
+ const isArrayUnion = (u) => Predicate.hasProperty(u, ArrayUnionTypeId);
70
+ /** @internal */
71
+ const arrayUnion = (...elements) => Object.assign(Object.create(ArrayUnionProto), { elements });
72
+ /** @internal */
73
+ const ArrayRemoveTypeId = "~@visiolab/effect-firebase/firestore/FieldValue/ArrayRemove";
74
+ const ArrayRemoveProto = {
75
+ ...Inspectable.BaseProto,
76
+ [ArrayRemoveTypeId]: ArrayRemoveTypeId,
77
+ toJSON() {
78
+ return {
79
+ _id: "ArrayRemove",
80
+ elements: this.elements.map((element) => Inspectable.toJson(element))
81
+ };
82
+ }
83
+ };
84
+ /** @internal */
85
+ const isArrayRemove = (u) => Predicate.hasProperty(u, ArrayRemoveTypeId);
86
+ /** @internal */
87
+ const arrayRemove = (...elements) => Object.assign(Object.create(ArrayRemoveProto), { elements });
88
+ export { ArrayRemoveTypeId, ArrayUnionTypeId, DeleteFieldTypeId, IncrementTypeId, ServerTimestampTypeId, arrayRemove, arrayUnion, decrement, deleteField, increment, isArrayRemove, isArrayUnion, isDeleteField, isIncrement, isServerTimestamp, serverTimestamp };
@@ -0,0 +1,145 @@
1
+ import { checkDocument } from "./firestoreValueWalk.mjs";
2
+ import { toCodecFirestore } from "./firestoreValue.mjs";
3
+ import { existing, make as make$1 } from "./documentSnapshot.mjs";
4
+ import { documentAt, isCollectionGroup } from "./collectionGroup.mjs";
5
+ import { make as make$2 } from "./querySnapshot.mjs";
6
+ import { Context, Effect, Result, Schema, Stream } from "effect";
7
+ // src/firestore/internal/firestore.ts
8
+ /** @internal */
9
+ const TypeId = "~@visiolab/effect-firebase/firestore/Firestore";
10
+ /** @internal */
11
+ const TransactionTypeId = "~@visiolab/effect-firebase/firestore/Firestore/Transaction";
12
+ /** The last segment of a document's path: its id. */
13
+ const idOf = (path) => path.slice(path.lastIndexOf("/") + 1);
14
+ /**
15
+ * What a hook is handed for `source`, and how each document it read is
16
+ * addressed: in the collection read, by its id, or, for a group, matched by
17
+ * its path to the collection of the group it lives in.
18
+ */
19
+ const resolve = (source) => isCollectionGroup(source) ? {
20
+ selector: {
21
+ _tag: "CollectionGroup",
22
+ id: source.id
23
+ },
24
+ refOf: (path) => documentAt(source, path)
25
+ } : {
26
+ selector: {
27
+ _tag: "Collection",
28
+ path: source.path
29
+ },
30
+ refOf: (path) => Result.succeed(source.doc(source.collection.id.make(idOf(path))))
31
+ };
32
+ /** A snapshot of the document at `ref`, as a hook read it, with its extra. */
33
+ const documentSnapshotOf = (ref, read) => Result.map(checkDocument(ref.path, read.data), (data) => make$1(ref, data, read.extra));
34
+ /**
35
+ * A snapshot of the documents a hook read, each addressed by `refOf`, with the
36
+ * extra of each and of the read as a whole.
37
+ */
38
+ const querySnapshotOf = (refOf, read) => Result.map(Result.all(read.docs.map((doc) => Result.flatMap(refOf(doc.path), (ref) => Result.map(checkDocument(ref.path, doc.data), (data) => existing(ref, data, doc.extra))))), (docs) => make$2(docs, read.extra));
39
+ /**
40
+ * Turns what is written, in the shape `schema` takes, into Firestore Values,
41
+ * through the codec derived from `schema`. Whatever services the schema
42
+ * encodes through are left for the caller to provide.
43
+ */
44
+ const encode = (schema, data) => Schema.encodeEffect(toCodecFirestore(schema))(data);
45
+ /** A document, in its model's `create` shape. */
46
+ const encodeCreate = (collection, data) => {
47
+ const schema = collection.schema.create;
48
+ return encode(schema, data);
49
+ };
50
+ /**
51
+ * A patch, in its model's update shape for `mode`, which encodes to a map from
52
+ * the field paths it writes at to what it writes there.
53
+ */
54
+ const encodeUpdate = (collection, data, mode) => {
55
+ const schema = mode === "unchecked" ? collection.schema.updateUnchecked : collection.schema.update;
56
+ return encode(schema, data);
57
+ };
58
+ /**
59
+ * The reads and writes a {@link Firestore.Firestore} and a transaction share,
60
+ * made against `target`. Each runs in a span named after it, so traces read
61
+ * the same whichever backend answered.
62
+ */
63
+ const operations = (target) => {
64
+ const span = (name) => Effect.withSpan(`${target.name}.${name}`);
65
+ const getDocs = target.getDocs;
66
+ return {
67
+ getDoc: (ref, options) => Effect.flatMap(target.getDoc(ref.path, options), (read) => Effect.fromResult(documentSnapshotOf(ref, read))).pipe(span("getDoc")),
68
+ getDocs: getDocs === void 0 ? void 0 : (source, options) => {
69
+ const { selector, refOf } = resolve(source);
70
+ return Effect.flatMap(getDocs(selector, options), (read) => Effect.fromResult(querySnapshotOf(refOf, read))).pipe(span("getDocs"));
71
+ },
72
+ setDoc: (ref, data) => Effect.flatMap(encodeCreate(ref.collection, data), (encoded) => target.setDoc(ref.path, encoded)).pipe(span("setDoc")),
73
+ updateDoc: (ref, data, options) => Effect.flatMap(encodeUpdate(ref.collection, data, options?.mode), (encoded) => target.updateDoc(ref.path, encoded)).pipe(span("updateDoc"))
74
+ };
75
+ };
76
+ /**
77
+ * The transaction a body reads and writes through, over the operations of one
78
+ * attempt.
79
+ */
80
+ const transactionOf = (transaction) => {
81
+ const readMany = transaction.getDocs;
82
+ const { getDoc, getDocs, setDoc, updateDoc } = operations({
83
+ name: "Firestore.Transaction",
84
+ getDoc: (path) => transaction.getDoc(path),
85
+ getDocs: readMany === void 0 ? void 0 : (selector) => readMany(selector),
86
+ setDoc: transaction.setDoc,
87
+ updateDoc: transaction.updateDoc
88
+ });
89
+ const portable = {
90
+ [TransactionTypeId]: TransactionTypeId,
91
+ getDoc,
92
+ setDoc,
93
+ updateDoc
94
+ };
95
+ return getDocs === void 0 ? portable : {
96
+ ...portable,
97
+ getDocs
98
+ };
99
+ };
100
+ /**
101
+ * The key a transaction body finds its {@link Firestore.Transaction} under is
102
+ * handed in by the public module that defines it, so that this one only
103
+ * imports it as a type and the two do not import each other.
104
+ *
105
+ * @internal
106
+ */
107
+ const make = (options, Transaction) => {
108
+ const { getDoc, getDocs, setDoc, updateDoc } = operations({
109
+ name: "Firestore",
110
+ getDoc: options.getDoc,
111
+ getDocs: options.getDocs,
112
+ setDoc: options.setDoc,
113
+ updateDoc: options.updateDoc
114
+ });
115
+ const addDoc = (ref, data) => Effect.flatMap(encodeCreate(ref.collection, data), (encoded) => Effect.map(options.addDoc(ref.path, encoded), (id) => ref.doc(ref.collection.id.make(id)))).pipe(Effect.withSpan("Firestore.addDoc"));
116
+ const streamDoc = (ref, listenOptions) => Stream.mapEffect(options.streamDoc(ref.path, listenOptions), (read) => Effect.fromResult(documentSnapshotOf(ref, read))).pipe(Stream.withSpan("Firestore.streamDoc"));
117
+ const streamDocs = (source, listenOptions) => {
118
+ const { selector, refOf } = resolve(source);
119
+ return Stream.mapEffect(options.streamDocs(selector, listenOptions), (read) => Effect.fromResult(querySnapshotOf(refOf, read))).pipe(Stream.withSpan("Firestore.streamDocs"));
120
+ };
121
+ const service = {
122
+ getDoc,
123
+ getDocs,
124
+ setDoc,
125
+ updateDoc,
126
+ addDoc,
127
+ streamDoc,
128
+ streamDocs,
129
+ runTransaction: Effect.fn("Firestore.runTransaction")(function* (effect, transactionOptions) {
130
+ const context = yield* Effect.context();
131
+ const offer = options.runTransaction.context;
132
+ const maxAttempts = transactionOptions?.maxAttempts;
133
+ return yield* options.runTransaction.run((operations) => {
134
+ const transaction = transactionOf(operations);
135
+ const withTransaction = Context.add(context, Transaction, transaction);
136
+ return Effect.provideContext(effect, offer === void 0 ? withTransaction : Context.merge(withTransaction, offer(transaction)));
137
+ }, maxAttempts === void 0 ? {} : { maxAttempts });
138
+ })
139
+ };
140
+ return {
141
+ [TypeId]: TypeId,
142
+ ...service
143
+ };
144
+ };
145
+ export { TransactionTypeId, TypeId, make };
@@ -0,0 +1,29 @@
1
+ import { Schema } from "effect";
2
+ declare const FirestoreError_base: Schema.Class<FirestoreError, Schema.TaggedStruct<"FirestoreError", {
3
+ readonly message: Schema.String;
4
+ /**
5
+ * The code the SDK reported, where it reported one it can be named by.
6
+ *
7
+ * The client SDK names every failure, such as `permission-denied`. The Admin
8
+ * SDK answers for Firestore over gRPC, which numbers its statuses rather than
9
+ * naming them, so there this is often absent even where something went wrong
10
+ * at the database.
11
+ */
12
+ readonly code: Schema.optional<Schema.String>;
13
+ readonly cause: Schema.optional<Schema.Defect>;
14
+ }>, import("effect/Cause").YieldableError & {
15
+ readonly brand: unique symbol;
16
+ }>;
17
+ /** @internal */
18
+ export declare class FirestoreError extends FirestoreError_base {}
19
+ declare const UndefinedCollectionError_base: Schema.Class<UndefinedCollectionError, Schema.TaggedStruct<"UndefinedCollectionError", {
20
+ /** The full path of the document no collection of the group defines. */
21
+ readonly path: Schema.String;
22
+ /** The collection id of the group that was read. */
23
+ readonly collectionId: Schema.String;
24
+ }>, import("effect/Cause").YieldableError & {
25
+ readonly brand: unique symbol;
26
+ }>;
27
+ /** @internal */
28
+ export declare class UndefinedCollectionError extends UndefinedCollectionError_base {}
29
+ export {};
@@ -0,0 +1,30 @@
1
+ import { Schema } from "effect";
2
+ // src/firestore/internal/firestoreError.ts
3
+ /**
4
+ * The errors of the Firestore module, defined apart from it so that the
5
+ * modules it builds on can raise them without importing it back: the public
6
+ * module imports its implementation, and an import cycle through it would
7
+ * leave these unset while a bundle evaluates.
8
+ */
9
+ /** @internal */
10
+ var FirestoreError = class extends Schema.TaggedError()("FirestoreError", {
11
+ message: Schema.String,
12
+ /**
13
+ * The code the SDK reported, where it reported one it can be named by.
14
+ *
15
+ * The client SDK names every failure, such as `permission-denied`. The Admin
16
+ * SDK answers for Firestore over gRPC, which numbers its statuses rather than
17
+ * naming them, so there this is often absent even where something went wrong
18
+ * at the database.
19
+ */
20
+ code: Schema.optional(Schema.String),
21
+ cause: Schema.optional(Schema.Defect())
22
+ }) {};
23
+ /** @internal */
24
+ var UndefinedCollectionError = class extends Schema.TaggedError()("UndefinedCollectionError", {
25
+ /** The full path of the document no collection of the group defines. */
26
+ path: Schema.String,
27
+ /** The collection id of the group that was read. */
28
+ collectionId: Schema.String
29
+ }) {};
30
+ export { FirestoreError, UndefinedCollectionError };