@react-native-firebase/firestore 23.8.8 → 24.0.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.
- package/CHANGELOG.md +27 -0
- package/RNFBFirestore.podspec +2 -1
- package/android/src/reactnative/java/io/invertase/firebase/firestore/ReactNativeFirebaseFirestoreCollectionModule.java +17 -4
- package/android/src/reactnative/java/io/invertase/firebase/firestore/ReactNativeFirebaseFirestoreDocumentModule.java +2 -2
- package/android/src/reactnative/java/io/invertase/firebase/firestore/ReactNativeFirebaseFirestorePipelineExecutor.java +1243 -0
- package/android/src/reactnative/java/io/invertase/firebase/firestore/ReactNativeFirebaseFirestorePipelineNodeBuilder.java +3919 -0
- package/android/src/reactnative/java/io/invertase/firebase/firestore/ReactNativeFirebaseFirestorePipelineParser.java +1735 -0
- package/android/src/reactnative/java/io/invertase/firebase/firestore/ReactNativeFirebaseFirestoreSerialize.java +1 -1
- package/dist/module/FieldPath.js +59 -0
- package/dist/module/FieldPath.js.map +1 -0
- package/dist/module/FieldValue.js +82 -0
- package/dist/module/FieldValue.js.map +1 -0
- package/{lib → dist/module}/FirestoreAggregate.js +31 -43
- package/dist/module/FirestoreAggregate.js.map +1 -0
- package/dist/module/FirestoreBlob.js +56 -0
- package/dist/module/FirestoreBlob.js.map +1 -0
- package/dist/module/FirestoreCollectionReference.js +70 -0
- package/dist/module/FirestoreCollectionReference.js.map +1 -0
- package/{lib → dist/module}/FirestoreDocumentChange.js +12 -15
- package/dist/module/FirestoreDocumentChange.js.map +1 -0
- package/dist/module/FirestoreDocumentReference.js +170 -0
- package/dist/module/FirestoreDocumentReference.js.map +1 -0
- package/dist/module/FirestoreDocumentSnapshot.js +88 -0
- package/dist/module/FirestoreDocumentSnapshot.js.map +1 -0
- package/dist/module/FirestoreFilter.js +146 -0
- package/dist/module/FirestoreFilter.js.map +1 -0
- package/dist/module/FirestoreGeoPoint.js +80 -0
- package/dist/module/FirestoreGeoPoint.js.map +1 -0
- package/{lib → dist/module}/FirestorePath.js +5 -12
- package/dist/module/FirestorePath.js.map +1 -0
- package/{lib → dist/module}/FirestorePersistentCacheIndexManager.js +11 -4
- package/dist/module/FirestorePersistentCacheIndexManager.js.map +1 -0
- package/dist/module/FirestoreQuery.js +298 -0
- package/dist/module/FirestoreQuery.js.map +1 -0
- package/{lib → dist/module}/FirestoreQueryModifiers.js +25 -136
- package/dist/module/FirestoreQueryModifiers.js.map +1 -0
- package/dist/module/FirestoreQuerySnapshot.js +98 -0
- package/dist/module/FirestoreQuerySnapshot.js.map +1 -0
- package/dist/module/FirestoreSnapshotMetadata.js +38 -0
- package/dist/module/FirestoreSnapshotMetadata.js.map +1 -0
- package/dist/module/FirestoreStatics.js +50 -0
- package/dist/module/FirestoreStatics.js.map +1 -0
- package/{lib → dist/module}/FirestoreTimestamp.js +39 -39
- package/dist/module/FirestoreTimestamp.js.map +1 -0
- package/dist/module/FirestoreTransaction.js +113 -0
- package/dist/module/FirestoreTransaction.js.map +1 -0
- package/dist/module/FirestoreTransactionHandler.js +137 -0
- package/dist/module/FirestoreTransactionHandler.js.map +1 -0
- package/dist/module/FirestoreVectorValue.js +75 -0
- package/dist/module/FirestoreVectorValue.js.map +1 -0
- package/dist/module/FirestoreWriteBatch.js +113 -0
- package/dist/module/FirestoreWriteBatch.js.map +1 -0
- package/dist/module/LoadBundleTask.js +70 -0
- package/dist/module/LoadBundleTask.js.map +1 -0
- package/dist/module/index.js +31 -0
- package/dist/module/index.js.map +1 -0
- package/dist/module/modular/Bytes.js +67 -0
- package/dist/module/modular/Bytes.js.map +1 -0
- package/dist/module/modular/FieldPath.js +25 -0
- package/dist/module/modular/FieldPath.js.map +1 -0
- package/dist/module/modular/FieldValue.js +37 -0
- package/dist/module/modular/FieldValue.js.map +1 -0
- package/dist/module/modular/GeoPoint.js +22 -0
- package/dist/module/modular/GeoPoint.js.map +1 -0
- package/dist/module/modular/Timestamp.js +22 -0
- package/dist/module/modular/Timestamp.js.map +1 -0
- package/dist/module/modular/VectorValue.js +25 -0
- package/dist/module/modular/VectorValue.js.map +1 -0
- package/dist/module/modular/query.js +222 -0
- package/dist/module/modular/query.js.map +1 -0
- package/dist/module/modular/snapshot.js +32 -0
- package/dist/module/modular/snapshot.js.map +1 -0
- package/dist/module/modular.js +229 -0
- package/dist/module/modular.js.map +1 -0
- package/dist/module/namespaced.js +298 -0
- package/dist/module/namespaced.js.map +1 -0
- package/dist/module/package.json +1 -0
- package/dist/module/pipelines/expressions.js +1273 -0
- package/dist/module/pipelines/expressions.js.map +1 -0
- package/dist/module/pipelines/index.js +32 -0
- package/dist/module/pipelines/index.js.map +1 -0
- package/dist/module/pipelines/pipeline-result.js +58 -0
- package/dist/module/pipelines/pipeline-result.js.map +1 -0
- package/dist/module/pipelines/pipeline-source.js +4 -0
- package/dist/module/pipelines/pipeline-source.js.map +1 -0
- package/dist/module/pipelines/pipeline.js +4 -0
- package/dist/module/pipelines/pipeline.js.map +1 -0
- package/dist/module/pipelines/pipeline_impl.js +42 -0
- package/dist/module/pipelines/pipeline_impl.js.map +1 -0
- package/dist/module/pipelines/pipeline_options.js +4 -0
- package/dist/module/pipelines/pipeline_options.js.map +1 -0
- package/dist/module/pipelines/pipeline_runtime.js +526 -0
- package/dist/module/pipelines/pipeline_runtime.js.map +1 -0
- package/dist/module/pipelines/pipeline_support.js +71 -0
- package/dist/module/pipelines/pipeline_support.js.map +1 -0
- package/dist/module/pipelines/pipeline_validate.js +183 -0
- package/dist/module/pipelines/pipeline_validate.js.map +1 -0
- package/dist/module/pipelines/stage_options.js +4 -0
- package/dist/module/pipelines/stage_options.js.map +1 -0
- package/dist/module/pipelines/types.js +2 -0
- package/dist/module/pipelines/types.js.map +1 -0
- package/dist/module/types/firestore.js +4 -0
- package/dist/module/types/firestore.js.map +1 -0
- package/dist/module/types/internal.js +4 -0
- package/dist/module/types/internal.js.map +1 -0
- package/dist/module/types/namespaced.js +338 -0
- package/dist/module/types/namespaced.js.map +1 -0
- package/{lib → dist/module}/utils/index.js +59 -114
- package/dist/module/utils/index.js.map +1 -0
- package/{lib → dist/module}/utils/serialize.js +58 -116
- package/dist/module/utils/serialize.js.map +1 -0
- package/{lib → dist/module}/utils/typemap.js +6 -20
- package/dist/module/utils/typemap.js.map +1 -0
- package/dist/module/version.js +5 -0
- package/dist/module/version.js.map +1 -0
- package/dist/module/web/RNFBFirestoreModule.android.js +5 -0
- package/dist/module/web/RNFBFirestoreModule.android.js.map +1 -0
- package/dist/module/web/RNFBFirestoreModule.ios.js +5 -0
- package/dist/module/web/RNFBFirestoreModule.ios.js.map +1 -0
- package/dist/module/web/RNFBFirestoreModule.js +387 -0
- package/dist/module/web/RNFBFirestoreModule.js.map +1 -0
- package/{lib → dist/module}/web/convert.js +60 -94
- package/dist/module/web/convert.js.map +1 -0
- package/dist/module/web/pipelines/pipeline.js +34 -0
- package/dist/module/web/pipelines/pipeline.js.map +1 -0
- package/dist/module/web/pipelines/pipeline_bridge_factory.js +217 -0
- package/dist/module/web/pipelines/pipeline_bridge_factory.js.map +1 -0
- package/dist/module/web/pipelines/pipeline_node_builder.js +294 -0
- package/dist/module/web/pipelines/pipeline_node_builder.js.map +1 -0
- package/dist/module/web/pipelines/pipeline_parser.js +21 -0
- package/dist/module/web/pipelines/pipeline_parser.js.map +1 -0
- package/dist/module/web/pipelines/pipeline_snapshot_serializer.js +89 -0
- package/dist/module/web/pipelines/pipeline_snapshot_serializer.js.map +1 -0
- package/dist/module/web/query.js +95 -0
- package/dist/module/web/query.js.map +1 -0
- package/dist/typescript/lib/FieldPath.d.ts +10 -0
- package/dist/typescript/lib/FieldPath.d.ts.map +1 -0
- package/dist/typescript/lib/FieldValue.d.ts +17 -0
- package/dist/typescript/lib/FieldValue.d.ts.map +1 -0
- package/dist/typescript/lib/FirestoreAggregate.d.ts +40 -0
- package/dist/typescript/lib/FirestoreAggregate.d.ts.map +1 -0
- package/dist/typescript/lib/FirestoreBlob.d.ts +11 -0
- package/dist/typescript/lib/FirestoreBlob.d.ts.map +1 -0
- package/dist/typescript/lib/FirestoreCollectionReference.d.ts +15 -0
- package/dist/typescript/lib/FirestoreCollectionReference.d.ts.map +1 -0
- package/dist/typescript/lib/FirestoreDocumentChange.d.ts +27 -0
- package/dist/typescript/lib/FirestoreDocumentChange.d.ts.map +1 -0
- package/dist/typescript/lib/FirestoreDocumentReference.d.ts +30 -0
- package/dist/typescript/lib/FirestoreDocumentReference.d.ts.map +1 -0
- package/dist/typescript/lib/FirestoreDocumentSnapshot.d.ts +30 -0
- package/dist/typescript/lib/FirestoreDocumentSnapshot.d.ts.map +1 -0
- package/dist/typescript/lib/FirestoreFilter.d.ts +52 -0
- package/dist/typescript/lib/FirestoreFilter.d.ts.map +1 -0
- package/dist/typescript/lib/FirestoreGeoPoint.d.ts +22 -0
- package/dist/typescript/lib/FirestoreGeoPoint.d.ts.map +1 -0
- package/dist/typescript/lib/FirestorePath.d.ts +12 -0
- package/dist/typescript/lib/FirestorePath.d.ts.map +1 -0
- package/dist/typescript/lib/FirestorePersistentCacheIndexManager.d.ts +16 -0
- package/dist/typescript/lib/FirestorePersistentCacheIndexManager.d.ts.map +1 -0
- package/dist/typescript/lib/FirestoreQuery.d.ts +39 -0
- package/dist/typescript/lib/FirestoreQuery.d.ts.map +1 -0
- package/dist/typescript/lib/FirestoreQueryModifiers.d.ts +59 -0
- package/dist/typescript/lib/FirestoreQueryModifiers.d.ts.map +1 -0
- package/dist/typescript/lib/FirestoreQuerySnapshot.d.ts +49 -0
- package/dist/typescript/lib/FirestoreQuerySnapshot.d.ts.map +1 -0
- package/dist/typescript/lib/FirestoreSnapshotMetadata.d.ts +8 -0
- package/dist/typescript/lib/FirestoreSnapshotMetadata.d.ts.map +1 -0
- package/dist/typescript/lib/FirestoreStatics.d.ts +23 -0
- package/dist/typescript/lib/FirestoreStatics.d.ts.map +1 -0
- package/dist/typescript/lib/FirestoreTimestamp.d.ts +33 -0
- package/dist/typescript/lib/FirestoreTimestamp.d.ts.map +1 -0
- package/dist/typescript/lib/FirestoreTransaction.d.ts +42 -0
- package/dist/typescript/lib/FirestoreTransaction.d.ts.map +1 -0
- package/dist/typescript/lib/FirestoreTransactionHandler.d.ts +26 -0
- package/dist/typescript/lib/FirestoreTransactionHandler.d.ts.map +1 -0
- package/dist/typescript/lib/FirestoreVectorValue.d.ts +17 -0
- package/dist/typescript/lib/FirestoreVectorValue.d.ts.map +1 -0
- package/dist/typescript/lib/FirestoreWriteBatch.d.ts +21 -0
- package/dist/typescript/lib/FirestoreWriteBatch.d.ts.map +1 -0
- package/dist/typescript/lib/LoadBundleTask.d.ts +16 -0
- package/dist/typescript/lib/LoadBundleTask.d.ts.map +1 -0
- package/dist/typescript/lib/index.d.ts +6 -0
- package/dist/typescript/lib/index.d.ts.map +1 -0
- package/dist/typescript/lib/modular/Bytes.d.ts +22 -0
- package/dist/typescript/lib/modular/Bytes.d.ts.map +1 -0
- package/dist/typescript/lib/modular/FieldPath.d.ts +4 -0
- package/dist/typescript/lib/modular/FieldPath.d.ts.map +1 -0
- package/dist/typescript/lib/modular/FieldValue.d.ts +8 -0
- package/dist/typescript/lib/modular/FieldValue.d.ts.map +1 -0
- package/dist/typescript/lib/modular/GeoPoint.d.ts +3 -0
- package/dist/typescript/lib/modular/GeoPoint.d.ts.map +1 -0
- package/dist/typescript/lib/modular/Timestamp.d.ts +3 -0
- package/dist/typescript/lib/modular/Timestamp.d.ts.map +1 -0
- package/dist/typescript/lib/modular/VectorValue.d.ts +4 -0
- package/dist/typescript/lib/modular/VectorValue.d.ts.map +1 -0
- package/dist/typescript/lib/modular/query.d.ts +93 -0
- package/dist/typescript/lib/modular/query.d.ts.map +1 -0
- package/dist/typescript/lib/modular/snapshot.d.ts +30 -0
- package/dist/typescript/lib/modular/snapshot.d.ts.map +1 -0
- package/dist/typescript/lib/modular.d.ts +69 -0
- package/dist/typescript/lib/modular.d.ts.map +1 -0
- package/dist/typescript/lib/namespaced.d.ts +13 -0
- package/dist/typescript/lib/namespaced.d.ts.map +1 -0
- package/dist/typescript/lib/pipelines/expressions.d.ts +723 -0
- package/dist/typescript/lib/pipelines/expressions.d.ts.map +1 -0
- package/dist/typescript/lib/pipelines/index.d.ts +31 -0
- package/dist/typescript/lib/pipelines/index.d.ts.map +1 -0
- package/dist/typescript/lib/pipelines/pipeline-result.d.ts +30 -0
- package/dist/typescript/lib/pipelines/pipeline-result.d.ts.map +1 -0
- package/dist/typescript/lib/pipelines/pipeline-source.d.ts +64 -0
- package/dist/typescript/lib/pipelines/pipeline-source.d.ts.map +1 -0
- package/dist/typescript/lib/pipelines/pipeline.d.ts +61 -0
- package/dist/typescript/lib/pipelines/pipeline.d.ts.map +1 -0
- package/dist/typescript/lib/pipelines/pipeline_impl.d.ts +21 -0
- package/dist/typescript/lib/pipelines/pipeline_impl.d.ts.map +1 -0
- package/dist/typescript/lib/pipelines/pipeline_options.d.ts +17 -0
- package/dist/typescript/lib/pipelines/pipeline_options.d.ts.map +1 -0
- package/dist/typescript/lib/pipelines/pipeline_runtime.d.ts +10 -0
- package/dist/typescript/lib/pipelines/pipeline_runtime.d.ts.map +1 -0
- package/dist/typescript/lib/pipelines/pipeline_support.d.ts +7 -0
- package/dist/typescript/lib/pipelines/pipeline_support.d.ts.map +1 -0
- package/dist/typescript/lib/pipelines/pipeline_validate.d.ts +9 -0
- package/dist/typescript/lib/pipelines/pipeline_validate.d.ts.map +1 -0
- package/dist/typescript/lib/pipelines/stage_options.d.ts +326 -0
- package/dist/typescript/lib/pipelines/stage_options.d.ts.map +1 -0
- package/dist/typescript/lib/pipelines/types.d.ts +10 -0
- package/dist/typescript/lib/pipelines/types.d.ts.map +1 -0
- package/dist/typescript/lib/types/firestore.d.ts +263 -0
- package/dist/typescript/lib/types/firestore.d.ts.map +1 -0
- package/dist/typescript/lib/types/internal.d.ts +483 -0
- package/dist/typescript/lib/types/internal.d.ts.map +1 -0
- package/dist/typescript/lib/types/namespaced.d.ts +2285 -0
- package/dist/typescript/lib/types/namespaced.d.ts.map +1 -0
- package/dist/typescript/lib/utils/index.d.ts +15 -0
- package/dist/typescript/lib/utils/index.d.ts.map +1 -0
- package/dist/typescript/lib/utils/serialize.d.ts +17 -0
- package/dist/typescript/lib/utils/serialize.d.ts.map +1 -0
- package/dist/typescript/lib/utils/typemap.d.ts +3 -0
- package/dist/typescript/lib/utils/typemap.d.ts.map +1 -0
- package/dist/typescript/lib/version.d.ts +2 -0
- package/dist/typescript/lib/version.d.ts.map +1 -0
- package/dist/typescript/lib/web/RNFBFirestoreModule.android.d.ts +3 -0
- package/dist/typescript/lib/web/RNFBFirestoreModule.android.d.ts.map +1 -0
- package/dist/typescript/lib/web/RNFBFirestoreModule.d.ts +75 -0
- package/dist/typescript/lib/web/RNFBFirestoreModule.d.ts.map +1 -0
- package/dist/typescript/lib/web/RNFBFirestoreModule.ios.d.ts +3 -0
- package/dist/typescript/lib/web/RNFBFirestoreModule.ios.d.ts.map +1 -0
- package/dist/typescript/lib/web/convert.d.ts +14 -0
- package/dist/typescript/lib/web/convert.d.ts.map +1 -0
- package/dist/typescript/lib/web/pipelines/pipeline.d.ts +4 -0
- package/dist/typescript/lib/web/pipelines/pipeline.d.ts.map +1 -0
- package/dist/typescript/lib/web/pipelines/pipeline_bridge_factory.d.ts +5 -0
- package/dist/typescript/lib/web/pipelines/pipeline_bridge_factory.d.ts.map +1 -0
- package/dist/typescript/lib/web/pipelines/pipeline_node_builder.d.ts +5 -0
- package/dist/typescript/lib/web/pipelines/pipeline_node_builder.d.ts.map +1 -0
- package/dist/typescript/lib/web/pipelines/pipeline_parser.d.ts +3 -0
- package/dist/typescript/lib/web/pipelines/pipeline_parser.d.ts.map +1 -0
- package/dist/typescript/lib/web/pipelines/pipeline_snapshot_serializer.d.ts +4 -0
- package/dist/typescript/lib/web/pipelines/pipeline_snapshot_serializer.d.ts.map +1 -0
- package/dist/typescript/lib/web/query.d.ts +23 -0
- package/dist/typescript/lib/web/query.d.ts.map +1 -0
- package/dist/typescript/package.json +1 -0
- package/ios/RNFBFirestore/RNFBFirestoreCollectionModule.m +52 -2
- package/ios/RNFBFirestore/RNFBFirestorePipelineBridgeFactory.swift +384 -0
- package/ios/RNFBFirestore/RNFBFirestorePipelineCallHandler.swift +86 -0
- package/ios/RNFBFirestore/RNFBFirestorePipelineNodeBuilder.swift +1500 -0
- package/ios/RNFBFirestore/RNFBFirestorePipelineParser.swift +1352 -0
- package/ios/RNFBFirestore/RNFBFirestorePipelineSnapshotSerializer.swift +98 -0
- package/lib/{FirestoreFieldPath.js → FieldPath.ts} +10 -12
- package/lib/{FirestoreFieldValue.js → FieldValue.ts} +22 -19
- package/lib/FirestoreAggregate.ts +124 -0
- package/lib/FirestoreBlob.ts +73 -0
- package/lib/FirestoreCollectionReference.ts +99 -0
- package/lib/FirestoreDocumentChange.ts +71 -0
- package/lib/FirestoreDocumentReference.ts +310 -0
- package/lib/FirestoreDocumentSnapshot.ts +149 -0
- package/lib/FirestoreFilter.ts +232 -0
- package/lib/{FirestoreGeoPoint.js → FirestoreGeoPoint.ts} +48 -8
- package/lib/FirestorePath.ts +54 -0
- package/lib/FirestorePersistentCacheIndexManager.ts +46 -0
- package/lib/{FirestoreQuery.js → FirestoreQuery.ts} +208 -100
- package/lib/FirestoreQueryModifiers.ts +411 -0
- package/lib/{FirestoreQuerySnapshot.js → FirestoreQuerySnapshot.ts} +61 -32
- package/lib/{FirestoreSnapshotMetadata.js → FirestoreSnapshotMetadata.ts} +8 -6
- package/lib/{FirestoreStatics.js → FirestoreStatics.ts} +18 -11
- package/lib/FirestoreTimestamp.ts +161 -0
- package/lib/{FirestoreTransaction.js → FirestoreTransaction.ts} +64 -27
- package/lib/{FirestoreTransactionHandler.js → FirestoreTransactionHandler.ts} +54 -75
- package/lib/{FirestoreVectorValue.js → FirestoreVectorValue.ts} +36 -15
- package/lib/{FirestoreWriteBatch.js → FirestoreWriteBatch.ts} +45 -21
- package/lib/LoadBundleTask.ts +85 -0
- package/lib/index.ts +71 -0
- package/lib/modular/Bytes.ts +81 -0
- package/lib/modular/FieldPath.ts +24 -0
- package/lib/modular/FieldValue.ts +40 -0
- package/lib/modular/GeoPoint.ts +20 -0
- package/lib/modular/Timestamp.ts +20 -0
- package/lib/modular/VectorValue.ts +24 -0
- package/lib/modular/query.ts +368 -0
- package/lib/modular/snapshot.ts +137 -0
- package/lib/modular.ts +552 -0
- package/lib/{index.js → namespaced.ts} +170 -80
- package/lib/pipelines/expressions.ts +2321 -0
- package/lib/pipelines/index.ts +203 -0
- package/lib/pipelines/pipeline-result.ts +78 -0
- package/lib/pipelines/pipeline-source.ts +83 -0
- package/lib/pipelines/pipeline.ts +99 -0
- package/lib/pipelines/pipeline_impl.ts +46 -0
- package/lib/pipelines/pipeline_options.ts +32 -0
- package/lib/pipelines/pipeline_runtime.ts +863 -0
- package/lib/pipelines/pipeline_support.ts +134 -0
- package/lib/pipelines/pipeline_validate.ts +242 -0
- package/lib/pipelines/stage_options.ts +376 -0
- package/lib/pipelines/types.ts +26 -0
- package/lib/types/firestore.ts +477 -0
- package/lib/types/internal.ts +747 -0
- package/lib/{index.d.ts → types/namespaced.ts} +280 -79
- package/lib/utils/index.ts +244 -0
- package/lib/utils/serialize.ts +314 -0
- package/lib/utils/typemap.ts +65 -0
- package/lib/version.ts +2 -0
- package/lib/web/{RNFBFirestoreModule.js → RNFBFirestoreModule.ts} +222 -234
- package/lib/web/convert.ts +287 -0
- package/lib/web/pipelines/pipeline.ts +47 -0
- package/lib/web/pipelines/pipeline_bridge_factory.ts +377 -0
- package/lib/web/pipelines/pipeline_node_builder.ts +413 -0
- package/lib/web/pipelines/pipeline_parser.ts +23 -0
- package/lib/web/pipelines/pipeline_snapshot_serializer.ts +133 -0
- package/lib/web/query.ts +150 -0
- package/package.json +46 -7
- package/tsconfig.json +35 -0
- package/lib/FirestoreBlob.js +0 -107
- package/lib/FirestoreCollectionReference.js +0 -70
- package/lib/FirestoreDocumentReference.js +0 -222
- package/lib/FirestoreDocumentSnapshot.js +0 -132
- package/lib/FirestoreFilter.js +0 -156
- package/lib/modular/Bytes.d.ts +0 -11
- package/lib/modular/Bytes.js +0 -62
- package/lib/modular/FieldPath.d.ts +0 -20
- package/lib/modular/FieldPath.js +0 -7
- package/lib/modular/FieldValue.d.ts +0 -67
- package/lib/modular/FieldValue.js +0 -41
- package/lib/modular/GeoPoint.d.ts +0 -17
- package/lib/modular/GeoPoint.js +0 -3
- package/lib/modular/Timestamp.d.ts +0 -85
- package/lib/modular/Timestamp.js +0 -3
- package/lib/modular/VectorValue.d.ts +0 -30
- package/lib/modular/VectorValue.js +0 -11
- package/lib/modular/index.d.ts +0 -788
- package/lib/modular/index.js +0 -410
- package/lib/modular/query.d.ts +0 -370
- package/lib/modular/query.js +0 -233
- package/lib/modular/snapshot.d.ts +0 -256
- package/lib/modular/snapshot.js +0 -33
- package/lib/modular/utils/observer.js +0 -16
- package/lib/version.js +0 -2
- package/lib/web/query.js +0 -112
- /package/lib/web/{RNFBFirestoreModule.android.js → RNFBFirestoreModule.android.ts} +0 -0
- /package/lib/web/{RNFBFirestoreModule.ios.js → RNFBFirestoreModule.ios.ts} +0 -0
|
@@ -0,0 +1,1500 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2016-present Invertase Limited & Contributors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import Foundation
|
|
18
|
+
import FirebaseFirestore
|
|
19
|
+
|
|
20
|
+
final class RNFBFirestorePipelineNodeBuilder {
|
|
21
|
+
private final class SerializedValueBox {
|
|
22
|
+
var value: Any?
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
private final class SerializedExpressionBox {
|
|
26
|
+
var value: Any?
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
private final class ExprBridgeBox {
|
|
30
|
+
var value: ExprBridge?
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
private final class RawParamBox {
|
|
34
|
+
var value: Any?
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private enum ExpressionCoercionMode {
|
|
38
|
+
case expression
|
|
39
|
+
case booleanExpression
|
|
40
|
+
case expressionValue
|
|
41
|
+
case comparisonOperand
|
|
42
|
+
case vectorExpressionValue
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private enum ExpressionCoercionFrame {
|
|
46
|
+
case enter(
|
|
47
|
+
Any,
|
|
48
|
+
String,
|
|
49
|
+
ExpressionCoercionMode,
|
|
50
|
+
ExprBridgeBox
|
|
51
|
+
)
|
|
52
|
+
case functionExit(
|
|
53
|
+
ExprBridgeBox,
|
|
54
|
+
String,
|
|
55
|
+
[ExprBridgeBox],
|
|
56
|
+
String
|
|
57
|
+
)
|
|
58
|
+
case conditionalExit(
|
|
59
|
+
ExprBridgeBox,
|
|
60
|
+
ExprBridgeBox,
|
|
61
|
+
ExprBridgeBox,
|
|
62
|
+
ExprBridgeBox,
|
|
63
|
+
String
|
|
64
|
+
)
|
|
65
|
+
case arrayExit(
|
|
66
|
+
ExprBridgeBox,
|
|
67
|
+
[ExprBridgeBox],
|
|
68
|
+
String
|
|
69
|
+
)
|
|
70
|
+
case mapLiteralExit(
|
|
71
|
+
ExprBridgeBox,
|
|
72
|
+
[(String, ExprBridgeBox)],
|
|
73
|
+
String
|
|
74
|
+
)
|
|
75
|
+
case mapPassthroughExit(
|
|
76
|
+
ExprBridgeBox,
|
|
77
|
+
[ExprBridgeBox],
|
|
78
|
+
String
|
|
79
|
+
)
|
|
80
|
+
case logicalOperatorExit(
|
|
81
|
+
ExprBridgeBox,
|
|
82
|
+
String,
|
|
83
|
+
[ExprBridgeBox],
|
|
84
|
+
String
|
|
85
|
+
)
|
|
86
|
+
case binaryOperatorExit(
|
|
87
|
+
ExprBridgeBox,
|
|
88
|
+
String,
|
|
89
|
+
String,
|
|
90
|
+
ExprBridgeBox,
|
|
91
|
+
String
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private enum RawParamCoercionFrame {
|
|
96
|
+
case enter(
|
|
97
|
+
Any,
|
|
98
|
+
String,
|
|
99
|
+
RawParamBox
|
|
100
|
+
)
|
|
101
|
+
case listExit(
|
|
102
|
+
RawParamBox,
|
|
103
|
+
[RawParamBox]
|
|
104
|
+
)
|
|
105
|
+
case mapExit(
|
|
106
|
+
RawParamBox,
|
|
107
|
+
[(String, RawParamBox)]
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private enum SerializationFrame {
|
|
112
|
+
case expressionEnter(
|
|
113
|
+
RNFBFirestoreParsedExpressionNode,
|
|
114
|
+
SerializedExpressionBox
|
|
115
|
+
)
|
|
116
|
+
case expressionFunctionExit(
|
|
117
|
+
SerializedExpressionBox,
|
|
118
|
+
String,
|
|
119
|
+
[SerializedValueBox]
|
|
120
|
+
)
|
|
121
|
+
case valueEnter(
|
|
122
|
+
RNFBFirestoreParsedValueNode,
|
|
123
|
+
SerializedValueBox
|
|
124
|
+
)
|
|
125
|
+
case valueListExit(
|
|
126
|
+
SerializedValueBox,
|
|
127
|
+
[SerializedValueBox]
|
|
128
|
+
)
|
|
129
|
+
case valueMapExit(
|
|
130
|
+
SerializedValueBox,
|
|
131
|
+
[(String, SerializedValueBox)]
|
|
132
|
+
)
|
|
133
|
+
case expressionConstantExit(
|
|
134
|
+
SerializedExpressionBox,
|
|
135
|
+
SerializedValueBox
|
|
136
|
+
)
|
|
137
|
+
case valueExpressionExit(
|
|
138
|
+
SerializedValueBox,
|
|
139
|
+
SerializedExpressionBox
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private enum ConstantResolutionFrame {
|
|
144
|
+
case enter(
|
|
145
|
+
Any,
|
|
146
|
+
String,
|
|
147
|
+
SerializedValueBox
|
|
148
|
+
)
|
|
149
|
+
case exitList(
|
|
150
|
+
SerializedValueBox,
|
|
151
|
+
[SerializedValueBox]
|
|
152
|
+
)
|
|
153
|
+
case exitMap(
|
|
154
|
+
SerializedValueBox,
|
|
155
|
+
[(String, SerializedValueBox)]
|
|
156
|
+
)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
func coerceExpression(
|
|
160
|
+
_ value: RNFBFirestoreParsedExpressionNode,
|
|
161
|
+
fieldName: String
|
|
162
|
+
) throws -> ExprBridge {
|
|
163
|
+
try coerceExpression(serializeExpressionNode(value), fieldName: fieldName)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
func coerceBooleanExpression(
|
|
167
|
+
_ value: RNFBFirestoreParsedExpressionNode,
|
|
168
|
+
fieldName: String
|
|
169
|
+
) throws -> ExprBridge {
|
|
170
|
+
try coerceBooleanExpression(serializeExpressionNode(value), fieldName: fieldName)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
func coerceNamedSelectables(
|
|
174
|
+
_ values: [RNFBFirestoreParsedSelectableNode],
|
|
175
|
+
fieldName: String
|
|
176
|
+
) throws -> [String: ExprBridge] {
|
|
177
|
+
guard !values.isEmpty else {
|
|
178
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) to contain at least one value.")
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
var output: [String: ExprBridge] = [:]
|
|
182
|
+
for (index, value) in values.enumerated() {
|
|
183
|
+
let expression = try coerceExpression(value.expression, fieldName: "\(fieldName)[\(index)].expr")
|
|
184
|
+
let alias = coerceAlias(from: value) ?? expressionAlias(expression) ?? "field_\(index)"
|
|
185
|
+
output[alias] = expression
|
|
186
|
+
}
|
|
187
|
+
return output
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
func coerceOrderings(
|
|
191
|
+
_ values: [RNFBFirestoreParsedOrderingNode],
|
|
192
|
+
fieldName: String
|
|
193
|
+
) throws -> [Any] {
|
|
194
|
+
guard !values.isEmpty else {
|
|
195
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) to contain at least one value.")
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return try values.enumerated().map { index, value in
|
|
199
|
+
OrderingBridge(
|
|
200
|
+
expr: try coerceExpression(value.expression, fieldName: "\(fieldName)[\(index)].expr"),
|
|
201
|
+
direction: value.descending ? "descending" : "ascending"
|
|
202
|
+
)
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
func coerceAliasedAggregate(
|
|
207
|
+
_ value: RNFBFirestoreParsedAggregateNode,
|
|
208
|
+
fieldName: String
|
|
209
|
+
) throws -> (alias: String, function: AggregateFunctionBridge) {
|
|
210
|
+
var aggregate: [String: Any] = ["kind": value.kind]
|
|
211
|
+
if let primaryValue = value.primaryValue {
|
|
212
|
+
aggregate["expr"] = serializeValueNode(primaryValue)
|
|
213
|
+
}
|
|
214
|
+
if !value.args.isEmpty {
|
|
215
|
+
aggregate["args"] = value.args.map(serializeValueNode)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
let serializedAccumulator: [String: Any] = [
|
|
219
|
+
"alias": value.alias,
|
|
220
|
+
"aggregate": aggregate,
|
|
221
|
+
]
|
|
222
|
+
|
|
223
|
+
return (
|
|
224
|
+
alias: value.alias,
|
|
225
|
+
function: try coerceAggregateFunction(serializedAccumulator, fieldName: fieldName)
|
|
226
|
+
)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
func coerceVector(
|
|
230
|
+
_ value: RNFBFirestoreParsedValueNode,
|
|
231
|
+
fieldName: String
|
|
232
|
+
) throws -> [Double] {
|
|
233
|
+
try coerceVector(serializeValueNode(value), fieldName: fieldName)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
func coerceFieldPath(
|
|
237
|
+
_ value: RNFBFirestoreParsedExpressionNode,
|
|
238
|
+
fieldName: String
|
|
239
|
+
) throws -> String {
|
|
240
|
+
try coerceFieldPath(serializeExpressionNode(value), fieldName: fieldName)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
func coerceExpression(_ value: Any, fieldName: String) throws -> ExprBridge {
|
|
244
|
+
try coerceExpressionTree(value, fieldName: fieldName, mode: .expression)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
func coerceBooleanExpression(_ value: Any, fieldName: String) throws -> ExprBridge {
|
|
248
|
+
try coerceExpressionTree(value, fieldName: fieldName, mode: .booleanExpression)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
func coerceAggregateFunction(
|
|
252
|
+
_ accumulator: [String: Any],
|
|
253
|
+
fieldName: String
|
|
254
|
+
) throws -> AggregateFunctionBridge {
|
|
255
|
+
let aggregate = (accumulator["aggregate"] as? [String: Any]) ?? accumulator
|
|
256
|
+
let kind = (aggregate["kind"] as? String)
|
|
257
|
+
?? (aggregate["name"] as? String)
|
|
258
|
+
?? (aggregate["function"] as? String)
|
|
259
|
+
?? (aggregate["op"] as? String)
|
|
260
|
+
|
|
261
|
+
guard let kind, !kind.isEmpty else {
|
|
262
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) to include an aggregate kind.")
|
|
263
|
+
}
|
|
264
|
+
let normalizedKind = normalizeAggregateKind(kind)
|
|
265
|
+
|
|
266
|
+
var args: [ExprBridge] = []
|
|
267
|
+
if let expr = aggregate["expr"] ?? aggregate["field"] ?? aggregate["value"] {
|
|
268
|
+
args.append(try coerceExpression(expr, fieldName: "\(fieldName).expr"))
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if let extraArgs = aggregate["args"] as? [Any] {
|
|
272
|
+
args.append(contentsOf: try extraArgs.map {
|
|
273
|
+
try coerceExpression($0, fieldName: "\(fieldName).args")
|
|
274
|
+
})
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return AggregateFunctionBridge(name: normalizedKind, args: args)
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
func coerceNamedSelectables(
|
|
281
|
+
_ values: [Any],
|
|
282
|
+
fieldName: String
|
|
283
|
+
) throws -> [String: ExprBridge] {
|
|
284
|
+
guard !values.isEmpty else {
|
|
285
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) to contain at least one value.")
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
var output: [String: ExprBridge] = [:]
|
|
289
|
+
for (index, value) in values.enumerated() {
|
|
290
|
+
let expression = try coerceExpression(value, fieldName: "\(fieldName)[\(index)]")
|
|
291
|
+
let alias = coerceAlias(from: value) ?? expressionAlias(expression) ?? "field_\(index)"
|
|
292
|
+
output[alias] = expression
|
|
293
|
+
}
|
|
294
|
+
return output
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
func coerceOrderings(_ values: [Any], fieldName: String) throws -> [Any] {
|
|
298
|
+
guard !values.isEmpty else {
|
|
299
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) to contain at least one value.")
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return try values.enumerated().map { index, value in
|
|
303
|
+
if let path = value as? String {
|
|
304
|
+
return OrderingBridge(expr: FieldBridge(name: path), direction: "asc")
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
guard let map = value as? [String: Any] else {
|
|
308
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName)[\(index)] to be a string or object.")
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
let direction = (map["direction"] as? String) ?? "asc"
|
|
312
|
+
let expressionValue = map["expression"] ?? map["expr"] ?? map["field"] ?? map["fieldPath"] ?? map["path"] ?? map
|
|
313
|
+
return OrderingBridge(
|
|
314
|
+
expr: try coerceExpression(expressionValue, fieldName: "\(fieldName)[\(index)]"),
|
|
315
|
+
direction: direction
|
|
316
|
+
)
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
func coerceRawParams(_ value: Any?, fieldName: String) throws -> [Any] {
|
|
321
|
+
guard let value else {
|
|
322
|
+
return []
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if let values = value as? [Any] {
|
|
326
|
+
return try values.enumerated().map { index, nested in
|
|
327
|
+
try coerceRawParamValue(nested, fieldName: "\(fieldName)[\(index)]")
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if let values = value as? [String: Any] {
|
|
332
|
+
return [try coerceRawParamDictionary(values, fieldName: fieldName)]
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return [try coerceRawParamValue(value, fieldName: fieldName)]
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
func coerceRawOptions(_ options: [String: Any]?, fieldName: String) throws -> [String: ExprBridge]? {
|
|
339
|
+
guard let options else {
|
|
340
|
+
return nil
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
var output: [String: ExprBridge] = [:]
|
|
344
|
+
for (key, value) in options {
|
|
345
|
+
output[key] = try coerceExpression(value, fieldName: "\(fieldName).\(key)")
|
|
346
|
+
}
|
|
347
|
+
return output
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
func coerceVector(_ value: Any?, fieldName: String) throws -> [Double] {
|
|
351
|
+
if let map = value as? [String: Any] {
|
|
352
|
+
return try coerceVector(map["values"], fieldName: fieldName)
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
guard let values = value as? [Any] else {
|
|
356
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) to be an array.")
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return try values.map {
|
|
360
|
+
try coerceNumber($0, fieldName: fieldName)
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
func coerceFieldPath(_ value: Any, fieldName: String) throws -> String {
|
|
365
|
+
var currentValue: Any = value
|
|
366
|
+
|
|
367
|
+
while true {
|
|
368
|
+
if let string = currentValue as? String, !string.isEmpty {
|
|
369
|
+
return string
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if let map = currentValue as? [String: Any] {
|
|
373
|
+
if let path = map["path"] as? String, !path.isEmpty {
|
|
374
|
+
return path
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if let fieldPath = map["fieldPath"], !(fieldPath is [String: Any]) {
|
|
378
|
+
currentValue = fieldPath
|
|
379
|
+
continue
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
let segments = (map["segments"] as? [Any]) ?? (map["_segments"] as? [Any]) ?? []
|
|
383
|
+
if !segments.isEmpty {
|
|
384
|
+
let stringSegments = try segments.map { segment -> String in
|
|
385
|
+
guard let value = segment as? String else {
|
|
386
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) segment values to be strings.")
|
|
387
|
+
}
|
|
388
|
+
return value
|
|
389
|
+
}
|
|
390
|
+
let path = stringSegments.joined(separator: ".")
|
|
391
|
+
if !path.isEmpty {
|
|
392
|
+
return path
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) to resolve to a field path string.")
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
func coerceStringArray(
|
|
402
|
+
_ values: [Any],
|
|
403
|
+
fieldName: String
|
|
404
|
+
) throws -> [String] {
|
|
405
|
+
guard !values.isEmpty else {
|
|
406
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) to contain at least one value.")
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return try values.enumerated().map { index, value in
|
|
410
|
+
guard let string = value as? String else {
|
|
411
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName)[\(index)] to be a string.")
|
|
412
|
+
}
|
|
413
|
+
return string
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
func coerceInt(_ value: Any?, fieldName: String) throws -> Int {
|
|
418
|
+
guard let value else {
|
|
419
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) to be a number.")
|
|
420
|
+
}
|
|
421
|
+
return Int(try coerceNumber(value, fieldName: fieldName))
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
func requireValue(
|
|
425
|
+
_ map: [String: Any],
|
|
426
|
+
key: String,
|
|
427
|
+
fieldName: String
|
|
428
|
+
) throws -> Any {
|
|
429
|
+
guard let value = map[key] else {
|
|
430
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) to be provided.")
|
|
431
|
+
}
|
|
432
|
+
return value
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
func requireNonEmptyString(
|
|
436
|
+
_ map: [String: Any],
|
|
437
|
+
key: String,
|
|
438
|
+
fieldName: String
|
|
439
|
+
) throws -> String {
|
|
440
|
+
guard let value = map[key] as? String else {
|
|
441
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) to be a string.")
|
|
442
|
+
}
|
|
443
|
+
guard !value.isEmpty else {
|
|
444
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) to be a non-empty string.")
|
|
445
|
+
}
|
|
446
|
+
return value
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
private func coerceBooleanFunctionExpression(
|
|
450
|
+
name: String,
|
|
451
|
+
args: [Any],
|
|
452
|
+
fieldName: String
|
|
453
|
+
) throws -> ExprBridge {
|
|
454
|
+
return try coerceBooleanExpression([
|
|
455
|
+
"name": name,
|
|
456
|
+
"args": args,
|
|
457
|
+
], fieldName: fieldName)
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// NOTE: iOS pipeline function lowering lives in this builder.
|
|
461
|
+
//
|
|
462
|
+
// If a serialized JS pipeline function is not supported by the currently linked
|
|
463
|
+
// Firebase iOS pipeline runtime, add or document it here first.
|
|
464
|
+
//
|
|
465
|
+
// Some functions are intentionally blocked before reaching native on iOS
|
|
466
|
+
// (see `pipeline_support.ts` / `getIOSUnsupportedPipelineFunctions()`)
|
|
467
|
+
// because the installed iOS SDK/runtime currently rejects them with
|
|
468
|
+
// `invalid-argument` even though newer Firebase snippets may show them.
|
|
469
|
+
// When iOS support becomes available, implement the lowering here and then
|
|
470
|
+
// remove the corresponding JS-side unsupported-function guard.
|
|
471
|
+
private func coerceFunctionExpression(
|
|
472
|
+
name: String,
|
|
473
|
+
args: [Any],
|
|
474
|
+
fieldName: String
|
|
475
|
+
) throws -> ExprBridge {
|
|
476
|
+
return try coerceExpression([
|
|
477
|
+
"name": name,
|
|
478
|
+
"args": args,
|
|
479
|
+
], fieldName: fieldName)
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
private func buildArrayExpression(_ args: [Any], fieldName: String) throws -> ExprBridge {
|
|
483
|
+
let elements: [Any]
|
|
484
|
+
if args.count == 1, let unwrapped = try unwrapConstantArray(args[0], fieldName: "\(fieldName).args[0]") {
|
|
485
|
+
elements = unwrapped
|
|
486
|
+
} else {
|
|
487
|
+
elements = args
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
if !elements.contains(where: containsSerializedExpression) {
|
|
491
|
+
return ConstantBridge(try elements.enumerated().map { index, value in
|
|
492
|
+
try resolveConstantValue(value, fieldName: "\(fieldName).args[\(index)]")
|
|
493
|
+
})
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
return FunctionExprBridge(name: "array", args: try elements.enumerated().map { index, value in
|
|
497
|
+
try coerceExpressionValue(value, fieldName: "\(fieldName).args[\(index)]")
|
|
498
|
+
})
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
private func buildMapExpression(_ args: [Any], fieldName: String) throws -> ExprBridge {
|
|
502
|
+
guard args.count == 1 else {
|
|
503
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName).map to include exactly 1 argument.")
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
guard let entries = try unwrapConstantMap(args[0], fieldName: "\(fieldName).args[0]") else {
|
|
507
|
+
return FunctionExprBridge(name: "map", args: try args.enumerated().map { index, value in
|
|
508
|
+
try coerceExpressionValue(value, fieldName: "\(fieldName).args[\(index)]")
|
|
509
|
+
})
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
if !entries.values.contains(where: containsSerializedExpression) {
|
|
513
|
+
var resolved: [String: Any] = [:]
|
|
514
|
+
for (key, value) in entries {
|
|
515
|
+
resolved[key] = try resolveConstantValue(value, fieldName: "\(fieldName).args[0].\(key)")
|
|
516
|
+
}
|
|
517
|
+
return ConstantBridge(resolved)
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
var expressionArgs: [ExprBridge] = []
|
|
521
|
+
for (key, value) in entries {
|
|
522
|
+
expressionArgs.append(ConstantBridge(key))
|
|
523
|
+
expressionArgs.append(try coerceExpressionValue(value, fieldName: "\(fieldName).args[0].\(key)"))
|
|
524
|
+
}
|
|
525
|
+
return FunctionExprBridge(name: "map", args: expressionArgs)
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
private func coerceBooleanOperatorExpression(
|
|
529
|
+
map: [String: Any],
|
|
530
|
+
operatorName: String,
|
|
531
|
+
fieldName: String
|
|
532
|
+
) throws -> ExprBridge {
|
|
533
|
+
let normalized = operatorName.uppercased()
|
|
534
|
+
if normalized == "AND" || normalized == "OR" {
|
|
535
|
+
guard let queries = map["queries"] as? [Any], !queries.isEmpty else {
|
|
536
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName).queries to contain boolean expressions.")
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
let args = try queries.map { try coerceBooleanExpression($0, fieldName: "\(fieldName).queries") }
|
|
540
|
+
return FunctionExprBridge(name: normalized == "AND" ? "and" : "or", args: args)
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
let fieldValue = map["fieldPath"] ?? map["field"]
|
|
544
|
+
guard let fieldValue else {
|
|
545
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName).fieldPath to be provided.")
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
let left = FieldBridge(name: try coerceFieldPath(fieldValue, fieldName: "\(fieldName).fieldPath"))
|
|
549
|
+
let right = map["value"] ?? map["right"] ?? map["operand"] ?? NSNull()
|
|
550
|
+
let rightExpr = try coerceComparisonOperand(right, fieldName: "\(fieldName).value")
|
|
551
|
+
let fnName = mapOperatorToFunction(normalized)
|
|
552
|
+
return FunctionExprBridge(name: fnName, args: [left, rightExpr])
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
private func coerceComparisonOperand(_ value: Any, fieldName: String) throws -> ExprBridge {
|
|
556
|
+
try coerceExpressionTree(value, fieldName: fieldName, mode: .comparisonOperand)
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
private func coerceExpressionValue(_ value: Any, fieldName: String) throws -> ExprBridge {
|
|
560
|
+
try coerceExpressionTree(value, fieldName: fieldName, mode: .expressionValue)
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
private func coerceVectorExpressionValue(_ value: Any, fieldName: String) throws -> ExprBridge {
|
|
564
|
+
try coerceExpressionTree(value, fieldName: fieldName, mode: .vectorExpressionValue)
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
private func resolveConstantValue(_ value: Any, fieldName: String) throws -> Any {
|
|
568
|
+
let rootBox = SerializedValueBox()
|
|
569
|
+
var stack: [ConstantResolutionFrame] = [
|
|
570
|
+
.enter(value, fieldName, rootBox),
|
|
571
|
+
]
|
|
572
|
+
|
|
573
|
+
while let frame = stack.popLast() {
|
|
574
|
+
switch frame {
|
|
575
|
+
case let .enter(value, currentFieldName, box):
|
|
576
|
+
var currentValue: Any = value
|
|
577
|
+
|
|
578
|
+
while let map = currentValue as? [String: Any],
|
|
579
|
+
let constantValue = try unwrapConstantValue(map, fieldName: currentFieldName) {
|
|
580
|
+
currentValue = constantValue
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
if let map = currentValue as? [String: Any] {
|
|
584
|
+
if isSerializedExpressionLike(map) {
|
|
585
|
+
box.value = try coerceExpression(map, fieldName: currentFieldName)
|
|
586
|
+
continue
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
let entries = map.map { (key: $0.key, box: SerializedValueBox(), value: $0.value) }
|
|
590
|
+
stack.append(.exitMap(box, entries.map { ($0.key, $0.box) }))
|
|
591
|
+
for entry in entries.reversed() {
|
|
592
|
+
stack.append(.enter(entry.value, "\(currentFieldName).\(entry.key)", entry.box))
|
|
593
|
+
}
|
|
594
|
+
continue
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
if let values = currentValue as? [Any] {
|
|
598
|
+
let childBoxes = values.map { _ in SerializedValueBox() }
|
|
599
|
+
stack.append(.exitList(box, childBoxes))
|
|
600
|
+
for index in values.indices.reversed() {
|
|
601
|
+
stack.append(.enter(values[index], "\(currentFieldName)[\(index)]", childBoxes[index]))
|
|
602
|
+
}
|
|
603
|
+
continue
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
box.value = currentValue
|
|
607
|
+
case let .exitList(box, childBoxes):
|
|
608
|
+
box.value = childBoxes.map { $0.value as Any }
|
|
609
|
+
case let .exitMap(box, entries):
|
|
610
|
+
var output: [String: Any] = [:]
|
|
611
|
+
for (key, childBox) in entries {
|
|
612
|
+
output[key] = childBox.value
|
|
613
|
+
}
|
|
614
|
+
box.value = output
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
return rootBox.value as Any
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
private func containsSerializedExpression(_ value: Any) -> Bool {
|
|
622
|
+
var stack: [Any] = [value]
|
|
623
|
+
|
|
624
|
+
while let value = stack.popLast() {
|
|
625
|
+
var currentValue: Any = value
|
|
626
|
+
|
|
627
|
+
while let map = currentValue as? [String: Any],
|
|
628
|
+
let constantValue = try? unwrapConstantValue(map, fieldName: "") {
|
|
629
|
+
currentValue = constantValue
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
if let map = currentValue as? [String: Any] {
|
|
633
|
+
if isSerializedExpressionLike(map) {
|
|
634
|
+
return true
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
for nestedValue in map.values {
|
|
638
|
+
stack.append(nestedValue)
|
|
639
|
+
}
|
|
640
|
+
continue
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
if let values = currentValue as? [Any] {
|
|
644
|
+
for nestedValue in values {
|
|
645
|
+
stack.append(nestedValue)
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
return false
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
private func unwrapConstantArray(_ value: Any, fieldName: String) throws -> [Any]? {
|
|
654
|
+
if let array = value as? [Any] {
|
|
655
|
+
return array
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
guard let map = value as? [String: Any],
|
|
659
|
+
let constantValue = try unwrapConstantValue(map, fieldName: fieldName) else {
|
|
660
|
+
return nil
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
return constantValue as? [Any]
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
private func unwrapConstantMap(_ value: Any, fieldName: String) throws -> [String: Any]? {
|
|
667
|
+
guard let map = value as? [String: Any] else {
|
|
668
|
+
return nil
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
if let constantValue = try unwrapConstantValue(map, fieldName: fieldName) {
|
|
672
|
+
return constantValue as? [String: Any]
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
return isSerializedExpressionLike(map) ? nil : map
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
private func unwrapConstantValue(_ map: [String: Any], fieldName: String) throws -> Any? {
|
|
679
|
+
guard let kind = (map["exprType"] as? String)?.lowercased(), kind == "constant" else {
|
|
680
|
+
return nil
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
guard let value = map["value"] else {
|
|
684
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName).value to be provided.")
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
return value
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
private func isSerializedExpressionLike(_ map: [String: Any]) -> Bool {
|
|
691
|
+
map["exprType"] != nil || map["operator"] != nil || map["name"] != nil || map["expr"] != nil ||
|
|
692
|
+
map["expression"] != nil || map["fieldPath"] != nil || map["path"] != nil ||
|
|
693
|
+
map["segments"] != nil || map["_segments"] != nil
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
private func canonicalComparisonFunctionName(_ normalizedName: String) -> String {
|
|
697
|
+
switch normalizedName {
|
|
698
|
+
case "equal": return "equal"
|
|
699
|
+
case "notequal": return "not_equal"
|
|
700
|
+
case "greaterthan": return "greater_than"
|
|
701
|
+
case "greaterthanorequal": return "greater_than_or_equal"
|
|
702
|
+
case "lessthan": return "less_than"
|
|
703
|
+
case "lessthanorequal": return "less_than_or_equal"
|
|
704
|
+
case "arraycontains": return "array_contains"
|
|
705
|
+
case "arraycontainsany": return "array_contains_any"
|
|
706
|
+
case "arraycontainsall": return "array_contains_all"
|
|
707
|
+
case "equalany": return "equal_any"
|
|
708
|
+
case "notequalany": return "not_equal_any"
|
|
709
|
+
default: return normalizedName
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
private func canonicalizeFunctionName(_ name: String) -> String {
|
|
714
|
+
name.lowercased()
|
|
715
|
+
.replacingOccurrences(of: "_", with: "")
|
|
716
|
+
.replacingOccurrences(of: "-", with: "")
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
private func normalizeExpressionFunctionName(_ name: String) -> String {
|
|
720
|
+
let normalized = canonicalizeFunctionName(name)
|
|
721
|
+
switch normalized {
|
|
722
|
+
case "conditional":
|
|
723
|
+
return "cond"
|
|
724
|
+
case "logicalmaximum", "arraymaximum":
|
|
725
|
+
return "maximum"
|
|
726
|
+
case "logicalminimum", "arrayminimum":
|
|
727
|
+
return "minimum"
|
|
728
|
+
case "arraysum":
|
|
729
|
+
return "sum"
|
|
730
|
+
case "lower", "tolower":
|
|
731
|
+
return "to_lower"
|
|
732
|
+
case "upper", "toupper":
|
|
733
|
+
return "to_upper"
|
|
734
|
+
case "stringconcat":
|
|
735
|
+
return "string_concat"
|
|
736
|
+
case "startswith":
|
|
737
|
+
return "starts_with"
|
|
738
|
+
case "endswith":
|
|
739
|
+
return "ends_with"
|
|
740
|
+
case "timestampsubtract":
|
|
741
|
+
return "timestamp_sub"
|
|
742
|
+
case "timestamptruncate":
|
|
743
|
+
return "timestamp_trunc"
|
|
744
|
+
case "arraycontains":
|
|
745
|
+
return "array_contains"
|
|
746
|
+
case "arraycontainsany":
|
|
747
|
+
return "array_contains_any"
|
|
748
|
+
case "arraycontainsall":
|
|
749
|
+
return "array_contains_all"
|
|
750
|
+
case "charlength", "characterlength":
|
|
751
|
+
return "char_length"
|
|
752
|
+
case "bytelength":
|
|
753
|
+
return "byte_length"
|
|
754
|
+
case "greaterthan":
|
|
755
|
+
return "greater_than"
|
|
756
|
+
case "lessthan":
|
|
757
|
+
return "less_than"
|
|
758
|
+
case "greaterthanorequal":
|
|
759
|
+
return "greater_than_or_equal"
|
|
760
|
+
case "lessthanorequal":
|
|
761
|
+
return "less_than_or_equal"
|
|
762
|
+
case "notequal":
|
|
763
|
+
return "not_equal"
|
|
764
|
+
default:
|
|
765
|
+
return snakeCaseFunctionName(name)
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
private func normalizeAggregateKind(_ kind: String) -> String {
|
|
770
|
+
let normalized = canonicalizeFunctionName(kind)
|
|
771
|
+
switch normalized {
|
|
772
|
+
case "countall", "count_all":
|
|
773
|
+
return "count"
|
|
774
|
+
case "avg":
|
|
775
|
+
return "average"
|
|
776
|
+
case "min":
|
|
777
|
+
return "minimum"
|
|
778
|
+
case "max":
|
|
779
|
+
return "maximum"
|
|
780
|
+
case "countif", "count_if":
|
|
781
|
+
return "count_if"
|
|
782
|
+
case "countdistinct", "count_distinct":
|
|
783
|
+
return "count_distinct"
|
|
784
|
+
case "arrayagg", "array_agg":
|
|
785
|
+
return "array_agg"
|
|
786
|
+
case "arrayaggdistinct", "array_agg_distinct":
|
|
787
|
+
return "array_agg_distinct"
|
|
788
|
+
default:
|
|
789
|
+
return snakeCaseFunctionName(kind)
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
private func snakeCaseFunctionName(_ name: String) -> String {
|
|
794
|
+
guard !name.isEmpty else {
|
|
795
|
+
return name
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
var result = ""
|
|
799
|
+
for scalar in name.unicodeScalars {
|
|
800
|
+
let char = Character(scalar)
|
|
801
|
+
if CharacterSet.uppercaseLetters.contains(scalar) {
|
|
802
|
+
if !result.isEmpty {
|
|
803
|
+
result.append("_")
|
|
804
|
+
}
|
|
805
|
+
result.append(String(char).lowercased())
|
|
806
|
+
} else if char == "-" {
|
|
807
|
+
result.append("_")
|
|
808
|
+
} else {
|
|
809
|
+
result.append(String(char).lowercased())
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
return result
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
private func mapOperatorToFunction(_ operatorName: String) -> String {
|
|
817
|
+
switch operatorName {
|
|
818
|
+
case "==", "=", "EQUAL": return "equal"
|
|
819
|
+
case "!=", "<>", "NOT_EQUAL": return "not_equal"
|
|
820
|
+
case ">", "GREATER_THAN": return "greater_than"
|
|
821
|
+
case ">=", "GREATER_THAN_OR_EQUAL": return "greater_than_or_equal"
|
|
822
|
+
case "<", "LESS_THAN": return "less_than"
|
|
823
|
+
case "<=", "LESS_THAN_OR_EQUAL": return "less_than_or_equal"
|
|
824
|
+
case "ARRAY_CONTAINS", "ARRAY-CONTAINS": return "array_contains"
|
|
825
|
+
case "ARRAY_CONTAINS_ANY", "ARRAY-CONTAINS-ANY": return "array_contains_any"
|
|
826
|
+
case "ARRAY_CONTAINS_ALL", "ARRAY-CONTAINS-ALL": return "array_contains_all"
|
|
827
|
+
case "IN": return "equal_any"
|
|
828
|
+
case "NOT_IN": return "not_equal_any"
|
|
829
|
+
default: return operatorName.lowercased()
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
private func coerceRawParamDictionary(_ values: [String: Any], fieldName: String) throws -> [String: Any] {
|
|
834
|
+
var output: [String: Any] = [:]
|
|
835
|
+
for (key, nested) in values {
|
|
836
|
+
output[key] = try coerceRawParamValue(nested, fieldName: "\(fieldName).\(key)")
|
|
837
|
+
}
|
|
838
|
+
return output
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
private func coerceRawParamValue(_ value: Any, fieldName: String) throws -> Any {
|
|
842
|
+
let rootBox = RawParamBox()
|
|
843
|
+
var stack: [RawParamCoercionFrame] = [
|
|
844
|
+
.enter(value, fieldName, rootBox),
|
|
845
|
+
]
|
|
846
|
+
|
|
847
|
+
while let frame = stack.popLast() {
|
|
848
|
+
switch frame {
|
|
849
|
+
case let .enter(value, currentFieldName, box):
|
|
850
|
+
if value is ExprBridge || value is AggregateFunctionBridge {
|
|
851
|
+
box.value = value
|
|
852
|
+
continue
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
if let map = value as? [String: Any] {
|
|
856
|
+
let entries = map.map { (key: $0.key, box: RawParamBox(), value: $0.value) }
|
|
857
|
+
stack.append(.mapExit(box, entries.map { ($0.key, $0.box) }))
|
|
858
|
+
for entry in entries.reversed() {
|
|
859
|
+
stack.append(.enter(entry.value, "\(currentFieldName).\(entry.key)", entry.box))
|
|
860
|
+
}
|
|
861
|
+
continue
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
if let array = value as? [Any] {
|
|
865
|
+
let childBoxes = array.map { _ in RawParamBox() }
|
|
866
|
+
stack.append(.listExit(box, childBoxes))
|
|
867
|
+
for index in array.indices.reversed() {
|
|
868
|
+
stack.append(.enter(array[index], "\(currentFieldName)[\(index)]", childBoxes[index]))
|
|
869
|
+
}
|
|
870
|
+
continue
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
box.value = try coerceExpression(value, fieldName: currentFieldName)
|
|
874
|
+
case let .listExit(box, childBoxes):
|
|
875
|
+
box.value = childBoxes.map { $0.value as Any }
|
|
876
|
+
case let .mapExit(box, entries):
|
|
877
|
+
var output: [String: Any] = [:]
|
|
878
|
+
for (key, childBox) in entries {
|
|
879
|
+
output[key] = childBox.value
|
|
880
|
+
}
|
|
881
|
+
box.value = output
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
return rootBox.value as Any
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
private func coerceExpressionTree(
|
|
889
|
+
_ value: Any,
|
|
890
|
+
fieldName: String,
|
|
891
|
+
mode: ExpressionCoercionMode
|
|
892
|
+
) throws -> ExprBridge {
|
|
893
|
+
let comparisonFunctions: Set<String> = [
|
|
894
|
+
"equal", "notequal", "greaterthan", "greaterthanorequal", "lessthan", "lessthanorequal",
|
|
895
|
+
"arraycontains", "arraycontainsany", "arraycontainsall", "equalany", "notequalany",
|
|
896
|
+
]
|
|
897
|
+
|
|
898
|
+
let rootBox = ExprBridgeBox()
|
|
899
|
+
var stack: [ExpressionCoercionFrame] = [
|
|
900
|
+
.enter(value, fieldName, mode, rootBox),
|
|
901
|
+
]
|
|
902
|
+
|
|
903
|
+
while let frame = stack.popLast() {
|
|
904
|
+
switch frame {
|
|
905
|
+
case let .enter(value, currentFieldName, currentMode, box):
|
|
906
|
+
switch currentMode {
|
|
907
|
+
case .expressionValue:
|
|
908
|
+
if containsSerializedExpression(value) {
|
|
909
|
+
stack.append(.enter(value, currentFieldName, .expression, box))
|
|
910
|
+
} else {
|
|
911
|
+
box.value = ConstantBridge(try resolveConstantValue(value, fieldName: currentFieldName))
|
|
912
|
+
}
|
|
913
|
+
continue
|
|
914
|
+
case .comparisonOperand:
|
|
915
|
+
if let map = value as? [String: Any] {
|
|
916
|
+
stack.append(.enter(map, currentFieldName, .expression, box))
|
|
917
|
+
continue
|
|
918
|
+
}
|
|
919
|
+
if let values = value as? [Any] {
|
|
920
|
+
box.value = ConstantBridge(values)
|
|
921
|
+
continue
|
|
922
|
+
}
|
|
923
|
+
if let stringValue = value as? String {
|
|
924
|
+
box.value = ConstantBridge(stringValue)
|
|
925
|
+
continue
|
|
926
|
+
}
|
|
927
|
+
if isImmediateExpressionConstant(value) {
|
|
928
|
+
box.value = ConstantBridge(value)
|
|
929
|
+
continue
|
|
930
|
+
}
|
|
931
|
+
stack.append(.enter(value, currentFieldName, .expression, box))
|
|
932
|
+
continue
|
|
933
|
+
case .vectorExpressionValue:
|
|
934
|
+
var currentValue: Any = value
|
|
935
|
+
while let map = currentValue as? [String: Any],
|
|
936
|
+
let constantValue = try unwrapConstantValue(map, fieldName: currentFieldName) {
|
|
937
|
+
currentValue = constantValue
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
if let map = currentValue as? [String: Any], map["values"] != nil {
|
|
941
|
+
let vector = try coerceVector(map["values"], fieldName: currentFieldName)
|
|
942
|
+
box.value = ConstantBridge(VectorValue(__array: vector.map { NSNumber(value: $0) }))
|
|
943
|
+
continue
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
if currentValue is [Any] {
|
|
947
|
+
let vector = try coerceVector(currentValue, fieldName: currentFieldName)
|
|
948
|
+
box.value = ConstantBridge(VectorValue(__array: vector.map { NSNumber(value: $0) }))
|
|
949
|
+
continue
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
if containsSerializedExpression(currentValue) {
|
|
953
|
+
stack.append(.enter(currentValue, currentFieldName, .expression, box))
|
|
954
|
+
} else {
|
|
955
|
+
box.value = ConstantBridge(try resolveConstantValue(currentValue, fieldName: currentFieldName))
|
|
956
|
+
}
|
|
957
|
+
continue
|
|
958
|
+
case .expression, .booleanExpression:
|
|
959
|
+
var currentValue: Any = value
|
|
960
|
+
var currentField = currentFieldName
|
|
961
|
+
|
|
962
|
+
expressionLoop: while true {
|
|
963
|
+
if currentMode == .booleanExpression,
|
|
964
|
+
let conditionMap = currentValue as? [String: Any],
|
|
965
|
+
let nested = conditionMap["condition"] {
|
|
966
|
+
currentValue = nested
|
|
967
|
+
currentField = "\(currentField).condition"
|
|
968
|
+
continue
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
if let stringValue = currentValue as? String {
|
|
972
|
+
box.value = FieldBridge(name: stringValue)
|
|
973
|
+
break expressionLoop
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
if let expression = currentValue as? ExprBridge {
|
|
977
|
+
box.value = expression
|
|
978
|
+
break expressionLoop
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
if isImmediateExpressionConstant(currentValue) {
|
|
982
|
+
box.value = ConstantBridge(currentValue)
|
|
983
|
+
break expressionLoop
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
guard let map = currentValue as? [String: Any] else {
|
|
987
|
+
throw PipelineValidationError(
|
|
988
|
+
"pipelineExecute() could not convert \(currentField) into a pipeline expression.")
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
if let nested = map["expr"] {
|
|
992
|
+
currentValue = nested
|
|
993
|
+
currentField = "\(currentField).expr"
|
|
994
|
+
continue
|
|
995
|
+
}
|
|
996
|
+
if let nested = map["expression"] {
|
|
997
|
+
currentValue = nested
|
|
998
|
+
currentField = "\(currentField).expression"
|
|
999
|
+
continue
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
if let operatorName = map["operator"] as? String {
|
|
1003
|
+
let normalizedOperator = operatorName.uppercased()
|
|
1004
|
+
if normalizedOperator == "AND" || normalizedOperator == "OR" {
|
|
1005
|
+
guard let queries = map["queries"] as? [Any], !queries.isEmpty else {
|
|
1006
|
+
throw PipelineValidationError(
|
|
1007
|
+
"pipelineExecute() expected \(currentField).queries to contain boolean expressions.")
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
let queryBoxes = queries.map { _ in ExprBridgeBox() }
|
|
1011
|
+
stack.append(.logicalOperatorExit(
|
|
1012
|
+
box,
|
|
1013
|
+
normalizedOperator == "AND" ? "and" : "or",
|
|
1014
|
+
queryBoxes,
|
|
1015
|
+
currentField
|
|
1016
|
+
))
|
|
1017
|
+
for index in queries.indices.reversed() {
|
|
1018
|
+
stack.append(.enter(
|
|
1019
|
+
queries[index],
|
|
1020
|
+
"\(currentField).queries[\(index)]",
|
|
1021
|
+
.booleanExpression,
|
|
1022
|
+
queryBoxes[index]
|
|
1023
|
+
))
|
|
1024
|
+
}
|
|
1025
|
+
break expressionLoop
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
let fieldValue = map["fieldPath"] ?? map["field"]
|
|
1029
|
+
guard let fieldValue else {
|
|
1030
|
+
throw PipelineValidationError("pipelineExecute() expected \(currentField).fieldPath to be provided.")
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
let leftFieldPath = try coerceFieldPath(fieldValue, fieldName: "\(currentField).fieldPath")
|
|
1034
|
+
let right = map["value"] ?? map["right"] ?? map["operand"] ?? NSNull()
|
|
1035
|
+
let rightBox = ExprBridgeBox()
|
|
1036
|
+
stack.append(.binaryOperatorExit(
|
|
1037
|
+
box,
|
|
1038
|
+
mapOperatorToFunction(normalizedOperator),
|
|
1039
|
+
leftFieldPath,
|
|
1040
|
+
rightBox,
|
|
1041
|
+
currentField
|
|
1042
|
+
))
|
|
1043
|
+
stack.append(.enter(right, "\(currentField).value", .comparisonOperand, rightBox))
|
|
1044
|
+
break expressionLoop
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
if let name = map["name"] as? String {
|
|
1048
|
+
let rawArgs: [Any]
|
|
1049
|
+
if let args = map["args"] as? [Any] {
|
|
1050
|
+
rawArgs = args
|
|
1051
|
+
} else if let singleArg = map["args"] {
|
|
1052
|
+
rawArgs = [singleArg]
|
|
1053
|
+
} else {
|
|
1054
|
+
rawArgs = []
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
let normalized = canonicalizeFunctionName(name)
|
|
1058
|
+
|
|
1059
|
+
if normalized == "array" {
|
|
1060
|
+
let elements: [Any]
|
|
1061
|
+
if rawArgs.count == 1,
|
|
1062
|
+
let unwrapped = try unwrapConstantArray(rawArgs[0], fieldName: "\(currentField).args[0]") {
|
|
1063
|
+
elements = unwrapped
|
|
1064
|
+
} else {
|
|
1065
|
+
elements = rawArgs
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
if !elements.contains(where: containsSerializedExpression) {
|
|
1069
|
+
box.value = ConstantBridge(try elements.enumerated().map { index, element in
|
|
1070
|
+
try resolveConstantValue(element, fieldName: "\(currentField).args[\(index)]")
|
|
1071
|
+
})
|
|
1072
|
+
break expressionLoop
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
let argBoxes = elements.map { _ in ExprBridgeBox() }
|
|
1076
|
+
stack.append(.arrayExit(box, argBoxes, currentField))
|
|
1077
|
+
for index in elements.indices.reversed() {
|
|
1078
|
+
stack.append(.enter(
|
|
1079
|
+
elements[index],
|
|
1080
|
+
"\(currentField).args[\(index)]",
|
|
1081
|
+
.expressionValue,
|
|
1082
|
+
argBoxes[index]
|
|
1083
|
+
))
|
|
1084
|
+
}
|
|
1085
|
+
break expressionLoop
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
if normalized == "map" {
|
|
1089
|
+
guard rawArgs.count == 1 else {
|
|
1090
|
+
throw PipelineValidationError(
|
|
1091
|
+
"pipelineExecute() expected \(currentField).map to include exactly 1 argument.")
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
if let entries = try unwrapConstantMap(rawArgs[0], fieldName: "\(currentField).args[0]") {
|
|
1095
|
+
if !entries.values.contains(where: containsSerializedExpression) {
|
|
1096
|
+
var resolved: [String: Any] = [:]
|
|
1097
|
+
for (key, entryValue) in entries {
|
|
1098
|
+
resolved[key] = try resolveConstantValue(
|
|
1099
|
+
entryValue,
|
|
1100
|
+
fieldName: "\(currentField).args[0].\(key)"
|
|
1101
|
+
)
|
|
1102
|
+
}
|
|
1103
|
+
box.value = ConstantBridge(resolved)
|
|
1104
|
+
break expressionLoop
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
let entryBoxes = entries.map { (key: $0.key, box: ExprBridgeBox(), value: $0.value) }
|
|
1108
|
+
stack.append(.mapLiteralExit(
|
|
1109
|
+
box,
|
|
1110
|
+
entryBoxes.map { ($0.key, $0.box) },
|
|
1111
|
+
currentField
|
|
1112
|
+
))
|
|
1113
|
+
for entry in entryBoxes.reversed() {
|
|
1114
|
+
stack.append(.enter(
|
|
1115
|
+
entry.value,
|
|
1116
|
+
"\(currentField).args[0].\(entry.key)",
|
|
1117
|
+
.expressionValue,
|
|
1118
|
+
entry.box
|
|
1119
|
+
))
|
|
1120
|
+
}
|
|
1121
|
+
break expressionLoop
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
let argBoxes = rawArgs.map { _ in ExprBridgeBox() }
|
|
1125
|
+
stack.append(.mapPassthroughExit(box, argBoxes, currentField))
|
|
1126
|
+
for index in rawArgs.indices.reversed() {
|
|
1127
|
+
stack.append(.enter(
|
|
1128
|
+
rawArgs[index],
|
|
1129
|
+
"\(currentField).args[\(index)]",
|
|
1130
|
+
.expressionValue,
|
|
1131
|
+
argBoxes[index]
|
|
1132
|
+
))
|
|
1133
|
+
}
|
|
1134
|
+
break expressionLoop
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
if normalized == "conditional" {
|
|
1138
|
+
guard rawArgs.count == 3 else {
|
|
1139
|
+
throw PipelineValidationError(
|
|
1140
|
+
"pipelineExecute() expected \(currentField).conditional to include exactly 3 arguments.")
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
let conditionBox = ExprBridgeBox()
|
|
1144
|
+
let trueBox = ExprBridgeBox()
|
|
1145
|
+
let falseBox = ExprBridgeBox()
|
|
1146
|
+
stack.append(.conditionalExit(box, conditionBox, trueBox, falseBox, currentField))
|
|
1147
|
+
stack.append(.enter(rawArgs[2], "\(currentField).args[2]", .expressionValue, falseBox))
|
|
1148
|
+
stack.append(.enter(rawArgs[1], "\(currentField).args[1]", .expressionValue, trueBox))
|
|
1149
|
+
stack.append(.enter(rawArgs[0], "\(currentField).args[0]", .booleanExpression, conditionBox))
|
|
1150
|
+
break expressionLoop
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
if normalized == "logicalmaximum" || normalized == "logicalminimum" {
|
|
1154
|
+
guard rawArgs.count >= 2 else {
|
|
1155
|
+
throw PipelineValidationError(
|
|
1156
|
+
"pipelineExecute() expected \(currentField).\(name) to include at least 2 arguments.")
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
let argBoxes = rawArgs.map { _ in ExprBridgeBox() }
|
|
1160
|
+
stack.append(.functionExit(box, normalizeExpressionFunctionName(name), argBoxes, currentField))
|
|
1161
|
+
for index in rawArgs.indices.reversed() {
|
|
1162
|
+
stack.append(.enter(
|
|
1163
|
+
rawArgs[index],
|
|
1164
|
+
"\(currentField).args[\(index)]",
|
|
1165
|
+
.expressionValue,
|
|
1166
|
+
argBoxes[index]
|
|
1167
|
+
))
|
|
1168
|
+
}
|
|
1169
|
+
break expressionLoop
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
if normalized == "cosinedistance" || normalized == "dotproduct" || normalized == "euclideandistance" {
|
|
1173
|
+
guard rawArgs.count == 2 else {
|
|
1174
|
+
throw PipelineValidationError(
|
|
1175
|
+
"pipelineExecute() expected \(currentField).\(name) to include exactly 2 arguments.")
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
let argBoxes = rawArgs.map { _ in ExprBridgeBox() }
|
|
1179
|
+
stack.append(.functionExit(box, normalizeExpressionFunctionName(name), argBoxes, currentField))
|
|
1180
|
+
stack.append(.enter(rawArgs[1], "\(currentField).args[1]", .vectorExpressionValue, argBoxes[1]))
|
|
1181
|
+
stack.append(.enter(rawArgs[0], "\(currentField).args[0]", .expressionValue, argBoxes[0]))
|
|
1182
|
+
break expressionLoop
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
if normalized == "and" || normalized == "or" {
|
|
1186
|
+
guard !rawArgs.isEmpty else {
|
|
1187
|
+
throw PipelineValidationError(
|
|
1188
|
+
"pipelineExecute() expected \(currentField).args to contain boolean expressions.")
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
let argBoxes = rawArgs.map { _ in ExprBridgeBox() }
|
|
1192
|
+
stack.append(.functionExit(box, normalized, argBoxes, currentField))
|
|
1193
|
+
for index in rawArgs.indices.reversed() {
|
|
1194
|
+
stack.append(.enter(
|
|
1195
|
+
rawArgs[index],
|
|
1196
|
+
"\(currentField).args[\(index)]",
|
|
1197
|
+
.booleanExpression,
|
|
1198
|
+
argBoxes[index]
|
|
1199
|
+
))
|
|
1200
|
+
}
|
|
1201
|
+
break expressionLoop
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
if comparisonFunctions.contains(normalized) {
|
|
1205
|
+
guard rawArgs.count >= 2 else {
|
|
1206
|
+
throw PipelineValidationError(
|
|
1207
|
+
"pipelineExecute() expected \(currentField).args to include left and right operands.")
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
let argBoxes = rawArgs.map { _ in ExprBridgeBox() }
|
|
1211
|
+
stack.append(.functionExit(box, canonicalComparisonFunctionName(normalized), argBoxes, currentField))
|
|
1212
|
+
stack.append(.enter(rawArgs[1], "\(currentField).args[1]", .comparisonOperand, argBoxes[1]))
|
|
1213
|
+
stack.append(.enter(rawArgs[0], "\(currentField).args[0]", .expression, argBoxes[0]))
|
|
1214
|
+
break expressionLoop
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
let argBoxes = rawArgs.map { _ in ExprBridgeBox() }
|
|
1218
|
+
stack.append(.functionExit(box, normalizeExpressionFunctionName(name), argBoxes, currentField))
|
|
1219
|
+
for index in rawArgs.indices.reversed() {
|
|
1220
|
+
stack.append(.enter(
|
|
1221
|
+
rawArgs[index],
|
|
1222
|
+
"\(currentField).args[\(index)]",
|
|
1223
|
+
.expressionValue,
|
|
1224
|
+
argBoxes[index]
|
|
1225
|
+
))
|
|
1226
|
+
}
|
|
1227
|
+
break expressionLoop
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
if map["fieldPath"] != nil || map["path"] != nil || map["segments"] != nil || map["_segments"] != nil {
|
|
1231
|
+
box.value = FieldBridge(name: try coerceFieldPath(map, fieldName: currentField))
|
|
1232
|
+
break expressionLoop
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
if let kind = (map["exprType"] as? String)?.lowercased(), kind == "constant" {
|
|
1236
|
+
box.value = ConstantBridge(map["value"] as Any)
|
|
1237
|
+
break expressionLoop
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
throw PipelineValidationError(
|
|
1241
|
+
"pipelineExecute() could not convert \(currentField) into a pipeline expression.")
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
case let .functionExit(box, name, argBoxes, currentFieldName):
|
|
1245
|
+
box.value = FunctionExprBridge(
|
|
1246
|
+
name: name,
|
|
1247
|
+
args: try argBoxes.enumerated().map { index, argBox in
|
|
1248
|
+
try requireExpressionValue(argBox, fieldName: "\(currentFieldName).args[\(index)]")
|
|
1249
|
+
}
|
|
1250
|
+
)
|
|
1251
|
+
case let .conditionalExit(box, conditionBox, trueBox, falseBox, currentFieldName):
|
|
1252
|
+
box.value = FunctionExprBridge(name: "cond", args: [
|
|
1253
|
+
try requireExpressionValue(conditionBox, fieldName: "\(currentFieldName).args[0]"),
|
|
1254
|
+
try requireExpressionValue(trueBox, fieldName: "\(currentFieldName).args[1]"),
|
|
1255
|
+
try requireExpressionValue(falseBox, fieldName: "\(currentFieldName).args[2]"),
|
|
1256
|
+
])
|
|
1257
|
+
case let .arrayExit(box, argBoxes, currentFieldName):
|
|
1258
|
+
box.value = FunctionExprBridge(
|
|
1259
|
+
name: "array",
|
|
1260
|
+
args: try argBoxes.enumerated().map { index, argBox in
|
|
1261
|
+
try requireExpressionValue(argBox, fieldName: "\(currentFieldName).args[\(index)]")
|
|
1262
|
+
}
|
|
1263
|
+
)
|
|
1264
|
+
case let .mapLiteralExit(box, entries, currentFieldName):
|
|
1265
|
+
var args: [ExprBridge] = []
|
|
1266
|
+
for (key, valueBox) in entries {
|
|
1267
|
+
args.append(ConstantBridge(key))
|
|
1268
|
+
args.append(try requireExpressionValue(valueBox, fieldName: "\(currentFieldName).args[0].\(key)"))
|
|
1269
|
+
}
|
|
1270
|
+
box.value = FunctionExprBridge(name: "map", args: args)
|
|
1271
|
+
case let .mapPassthroughExit(box, argBoxes, currentFieldName):
|
|
1272
|
+
box.value = FunctionExprBridge(
|
|
1273
|
+
name: "map",
|
|
1274
|
+
args: try argBoxes.enumerated().map { index, argBox in
|
|
1275
|
+
try requireExpressionValue(argBox, fieldName: "\(currentFieldName).args[\(index)]")
|
|
1276
|
+
}
|
|
1277
|
+
)
|
|
1278
|
+
case let .logicalOperatorExit(box, name, argBoxes, currentFieldName):
|
|
1279
|
+
box.value = FunctionExprBridge(
|
|
1280
|
+
name: name,
|
|
1281
|
+
args: try argBoxes.enumerated().map { index, argBox in
|
|
1282
|
+
try requireExpressionValue(argBox, fieldName: "\(currentFieldName).queries[\(index)]")
|
|
1283
|
+
}
|
|
1284
|
+
)
|
|
1285
|
+
case let .binaryOperatorExit(box, name, leftFieldPath, rightBox, currentFieldName):
|
|
1286
|
+
box.value = FunctionExprBridge(name: name, args: [
|
|
1287
|
+
FieldBridge(name: leftFieldPath),
|
|
1288
|
+
try requireExpressionValue(rightBox, fieldName: "\(currentFieldName).value"),
|
|
1289
|
+
])
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
return try requireExpressionValue(rootBox, fieldName: fieldName)
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
private func requireExpressionValue(_ box: ExprBridgeBox, fieldName: String) throws -> ExprBridge {
|
|
1297
|
+
guard let value = box.value else {
|
|
1298
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) to be provided.")
|
|
1299
|
+
}
|
|
1300
|
+
return value
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
private func isImmediateExpressionConstant(_ value: Any) -> Bool {
|
|
1304
|
+
value is NSNull || value is NSNumber || value is Date || value is Timestamp ||
|
|
1305
|
+
value is GeoPoint || value is DocumentReference || value is VectorValue
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
func coerceAlias(from value: Any) -> String? {
|
|
1309
|
+
guard let map = value as? [String: Any] else {
|
|
1310
|
+
return nil
|
|
1311
|
+
}
|
|
1312
|
+
if let alias = map["alias"] as? String, !alias.isEmpty {
|
|
1313
|
+
return alias
|
|
1314
|
+
}
|
|
1315
|
+
if let alias = map["as"] as? String, !alias.isEmpty {
|
|
1316
|
+
return alias
|
|
1317
|
+
}
|
|
1318
|
+
return nil
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
func coerceAlias(from value: RNFBFirestoreParsedSelectableNode) -> String? {
|
|
1322
|
+
if let alias = value.alias, !alias.isEmpty {
|
|
1323
|
+
return alias
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
if case let .field(path) = value.expression, !path.isEmpty {
|
|
1327
|
+
return path
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
return nil
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
private func expressionAlias(_ expression: ExprBridge) -> String? {
|
|
1334
|
+
if let field = expression as? FieldBridge {
|
|
1335
|
+
return field.field_name()
|
|
1336
|
+
}
|
|
1337
|
+
return nil
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
private func serializeExpressionNode(_ value: RNFBFirestoreParsedExpressionNode) -> Any {
|
|
1341
|
+
let rootBox = SerializedExpressionBox()
|
|
1342
|
+
var stack: [SerializationFrame] = [
|
|
1343
|
+
.expressionEnter(value, rootBox),
|
|
1344
|
+
]
|
|
1345
|
+
|
|
1346
|
+
while let frame = stack.popLast() {
|
|
1347
|
+
switch frame {
|
|
1348
|
+
case let .expressionEnter(expression, box):
|
|
1349
|
+
switch expression {
|
|
1350
|
+
case let .field(path):
|
|
1351
|
+
box.value = [
|
|
1352
|
+
"__kind": "expression",
|
|
1353
|
+
"exprType": "Field",
|
|
1354
|
+
"path": path,
|
|
1355
|
+
]
|
|
1356
|
+
case let .constant(constantValue):
|
|
1357
|
+
let valueBox = SerializedValueBox()
|
|
1358
|
+
stack.append(.expressionConstantExit(box, valueBox))
|
|
1359
|
+
stack.append(.valueEnter(constantValue, valueBox))
|
|
1360
|
+
case let .function(name, args):
|
|
1361
|
+
let argBoxes = args.map { _ in SerializedValueBox() }
|
|
1362
|
+
stack.append(.expressionFunctionExit(box, name, argBoxes))
|
|
1363
|
+
for index in args.indices.reversed() {
|
|
1364
|
+
stack.append(.valueEnter(args[index], argBoxes[index]))
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
case let .expressionFunctionExit(box, name, argBoxes):
|
|
1368
|
+
box.value = [
|
|
1369
|
+
"__kind": "expression",
|
|
1370
|
+
"exprType": "Function",
|
|
1371
|
+
"name": name,
|
|
1372
|
+
"args": argBoxes.map { $0.value as Any },
|
|
1373
|
+
]
|
|
1374
|
+
case let .valueEnter(value, box):
|
|
1375
|
+
switch value {
|
|
1376
|
+
case let .primitive(primitive):
|
|
1377
|
+
box.value = primitive
|
|
1378
|
+
case let .list(values):
|
|
1379
|
+
let childBoxes = values.map { _ in SerializedValueBox() }
|
|
1380
|
+
stack.append(.valueListExit(box, childBoxes))
|
|
1381
|
+
for index in values.indices.reversed() {
|
|
1382
|
+
stack.append(.valueEnter(values[index], childBoxes[index]))
|
|
1383
|
+
}
|
|
1384
|
+
case let .map(values):
|
|
1385
|
+
let entries = values.map { (key: $0.key, box: SerializedValueBox(), value: $0.value) }
|
|
1386
|
+
stack.append(.valueMapExit(box, entries.map { ($0.key, $0.box) }))
|
|
1387
|
+
for entry in entries.reversed() {
|
|
1388
|
+
stack.append(.valueEnter(entry.value, entry.box))
|
|
1389
|
+
}
|
|
1390
|
+
case let .expression(expression):
|
|
1391
|
+
let expressionBox = SerializedExpressionBox()
|
|
1392
|
+
stack.append(.valueExpressionExit(box, expressionBox))
|
|
1393
|
+
stack.append(.expressionEnter(expression, expressionBox))
|
|
1394
|
+
}
|
|
1395
|
+
case let .valueListExit(box, childBoxes):
|
|
1396
|
+
box.value = childBoxes.map { $0.value as Any }
|
|
1397
|
+
case let .valueMapExit(box, entries):
|
|
1398
|
+
var output: [String: Any] = [:]
|
|
1399
|
+
for (key, childBox) in entries {
|
|
1400
|
+
output[key] = childBox.value
|
|
1401
|
+
}
|
|
1402
|
+
box.value = output
|
|
1403
|
+
case let .expressionConstantExit(expressionBox, valueBox):
|
|
1404
|
+
expressionBox.value = [
|
|
1405
|
+
"__kind": "expression",
|
|
1406
|
+
"exprType": "constant",
|
|
1407
|
+
"value": valueBox.value as Any,
|
|
1408
|
+
]
|
|
1409
|
+
case let .valueExpressionExit(valueBox, expressionBox):
|
|
1410
|
+
valueBox.value = expressionBox.value
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
return rootBox.value as Any
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
private func serializeValueNode(_ value: RNFBFirestoreParsedValueNode) -> Any {
|
|
1418
|
+
let rootBox = SerializedValueBox()
|
|
1419
|
+
var stack: [SerializationFrame] = [
|
|
1420
|
+
.valueEnter(value, rootBox),
|
|
1421
|
+
]
|
|
1422
|
+
|
|
1423
|
+
while let frame = stack.popLast() {
|
|
1424
|
+
switch frame {
|
|
1425
|
+
case let .valueEnter(value, box):
|
|
1426
|
+
switch value {
|
|
1427
|
+
case let .primitive(primitive):
|
|
1428
|
+
box.value = primitive
|
|
1429
|
+
case let .list(values):
|
|
1430
|
+
let childBoxes = values.map { _ in SerializedValueBox() }
|
|
1431
|
+
stack.append(.valueListExit(box, childBoxes))
|
|
1432
|
+
for index in values.indices.reversed() {
|
|
1433
|
+
stack.append(.valueEnter(values[index], childBoxes[index]))
|
|
1434
|
+
}
|
|
1435
|
+
case let .map(values):
|
|
1436
|
+
let entries = values.map { (key: $0.key, box: SerializedValueBox(), value: $0.value) }
|
|
1437
|
+
stack.append(.valueMapExit(box, entries.map { ($0.key, $0.box) }))
|
|
1438
|
+
for entry in entries.reversed() {
|
|
1439
|
+
stack.append(.valueEnter(entry.value, entry.box))
|
|
1440
|
+
}
|
|
1441
|
+
case let .expression(expression):
|
|
1442
|
+
let expressionBox = SerializedExpressionBox()
|
|
1443
|
+
stack.append(.valueExpressionExit(box, expressionBox))
|
|
1444
|
+
stack.append(.expressionEnter(expression, expressionBox))
|
|
1445
|
+
}
|
|
1446
|
+
case let .valueListExit(box, childBoxes):
|
|
1447
|
+
box.value = childBoxes.map { $0.value as Any }
|
|
1448
|
+
case let .valueMapExit(box, entries):
|
|
1449
|
+
var output: [String: Any] = [:]
|
|
1450
|
+
for (key, childBox) in entries {
|
|
1451
|
+
output[key] = childBox.value
|
|
1452
|
+
}
|
|
1453
|
+
box.value = output
|
|
1454
|
+
case let .expressionEnter(expression, box):
|
|
1455
|
+
switch expression {
|
|
1456
|
+
case let .field(path):
|
|
1457
|
+
box.value = [
|
|
1458
|
+
"__kind": "expression",
|
|
1459
|
+
"exprType": "Field",
|
|
1460
|
+
"path": path,
|
|
1461
|
+
]
|
|
1462
|
+
case let .constant(constantValue):
|
|
1463
|
+
let valueBox = SerializedValueBox()
|
|
1464
|
+
stack.append(.expressionConstantExit(box, valueBox))
|
|
1465
|
+
stack.append(.valueEnter(constantValue, valueBox))
|
|
1466
|
+
case let .function(name, args):
|
|
1467
|
+
let argBoxes = args.map { _ in SerializedValueBox() }
|
|
1468
|
+
stack.append(.expressionFunctionExit(box, name, argBoxes))
|
|
1469
|
+
for index in args.indices.reversed() {
|
|
1470
|
+
stack.append(.valueEnter(args[index], argBoxes[index]))
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
case let .expressionFunctionExit(box, name, argBoxes):
|
|
1474
|
+
box.value = [
|
|
1475
|
+
"__kind": "expression",
|
|
1476
|
+
"exprType": "Function",
|
|
1477
|
+
"name": name,
|
|
1478
|
+
"args": argBoxes.map { $0.value as Any },
|
|
1479
|
+
]
|
|
1480
|
+
case let .expressionConstantExit(expressionBox, valueBox):
|
|
1481
|
+
expressionBox.value = [
|
|
1482
|
+
"__kind": "expression",
|
|
1483
|
+
"exprType": "constant",
|
|
1484
|
+
"value": valueBox.value as Any,
|
|
1485
|
+
]
|
|
1486
|
+
case let .valueExpressionExit(valueBox, expressionBox):
|
|
1487
|
+
valueBox.value = expressionBox.value
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
return rootBox.value as Any
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
private func coerceNumber(_ value: Any, fieldName: String) throws -> Double {
|
|
1495
|
+
if let number = value as? NSNumber {
|
|
1496
|
+
return number.doubleValue
|
|
1497
|
+
}
|
|
1498
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) to be a number.")
|
|
1499
|
+
}
|
|
1500
|
+
}
|