@fireemu/darwin-x64 0.44.0 → 0.45.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/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # @fireemu/darwin-x64
2
2
 
3
3
  The [fireemu](https://www.npmjs.com/package/fireemu) daemon built for `darwin-x64`
4
- (Rust target `x86_64-apple-darwin`), version 0.44.0.
4
+ (Rust target `x86_64-apple-darwin`), version 0.45.0.
5
5
 
6
6
  macOS 13 or newer on Intel. Cross-compiled from the Apple silicon runner against the same universal SDK; its unit tests run on the arm64 host, which builds the same code.
7
7
 
package/bin/fireemu CHANGED
@@ -1,4 +1,4 @@
1
1
  [diffend] Oversized file quarantined before diffing.
2
2
  name: package/bin/fireemu
3
- size: 40771600 bytes
4
- sha256: 9f136845640648162c73761f815eba34c64ed9db9705ed7cefd87178bf4e9951
3
+ size: 41141128 bytes
4
+ sha256: d99be77c155a89c86de535c51eaf3c501cc052035acd787feb19821ed2274000
@@ -724,14 +724,14 @@ function ignoredBlocking(base, eventType) {
724
724
  return ignored(base, "blocking", "unsupported", `blocking identity event ${eventType} is not served`);
725
725
  }
726
726
 
727
- // Products the official emulator has a trigger service for and fireemu does not serve.
727
+ // Trigger families outside the served surface; exact supported types are handled first.
728
728
  // The event-type substring is matched the way `getServiceFromEventType` matches it.
729
729
  const DEFERRED_TRIGGER_PRODUCTS = [
730
730
  {
731
731
  match: (type) => type.includes("remoteconfig"),
732
732
  triggerType: "remoteConfig",
733
733
  scope: "deferred",
734
- reason: "deferred: Remote Config has no emulator in the active supported surface",
734
+ reason: "deferred: only Remote Config update triggers are served by the local injection action",
735
735
  },
736
736
  {
737
737
  match: (type) => type.includes("dataconnect"),
@@ -768,6 +768,12 @@ function deferredProduct(type) {
768
768
  // firebase-functions v1 triggers: legacy event types and a resource pattern.
769
769
  function describeV1Event(base, type, resource, schedule, retry) {
770
770
  const v1 = { v1: true };
771
+ if (type === "google.firebase.remoteconfig.update") {
772
+ if (resource !== `projects/${process.env.GCLOUD_PROJECT}`) {
773
+ return ignored(base, "remoteConfig", "unsupported", "Remote Config resource must name the runtime project");
774
+ }
775
+ return { ...base, ...v1, retry, trigger: { type: "remoteConfig" } };
776
+ }
771
777
  const dbMatch = type.match(/^providers\/google\.firebase\.database\/eventTypes\/ref\.(create|update|delete|write)$/);
772
778
  if (dbMatch) {
773
779
  resource = triggerString(resource, "eventTrigger.resource", { optional: true, allowEmpty: true }) ?? "";
@@ -1020,6 +1026,9 @@ function describe(name, fn, instrumentation) {
1020
1026
  },
1021
1027
  };
1022
1028
  }
1029
+ if (type === "google.firebase.remoteconfig.remoteConfig.v1.updated" && et.channel === undefined) {
1030
+ return { ...base, trigger: { type: "remoteConfig" } };
1031
+ }
1023
1032
  if (type === "google.cloud.pubsub.topic.v1.messagePublished") {
1024
1033
  const topic = triggerString(triggerRecord(et.eventFilters, "eventFilters").topic, "eventFilters.topic");
1025
1034
  const projectedTopic = topic.replace(/^.*\/topics\//, "");
@@ -1155,6 +1164,8 @@ const strictProfile = () => process.env.FIREEMU_HTTP_PROFILE === "strict";
1155
1164
  function v1Context(msg) {
1156
1165
  const event = msg.event;
1157
1166
  switch (msg.trigger) {
1167
+ case "remoteConfig":
1168
+ return event.context;
1158
1169
  case "database": {
1159
1170
  const operation = {created:"create",updated:"update",deleted:"delete",written:"write"}[event.type.split(".").at(-1)];
1160
1171
  const context = {
@@ -1497,6 +1508,8 @@ function firestoreProtoTimestamp(value) {
1497
1508
  function firestoreProtoFields(fields) {
1498
1509
  return Object.fromEntries(Object.entries(fields ?? {}).map(([key, value]) => {
1499
1510
  const converted = { ...value };
1511
+ // protobufjs skips null members; the NullValue enum must be its numeric zero.
1512
+ if (value.nullValue === null) converted.nullValue = 0;
1500
1513
  if (value.timestampValue !== undefined) converted.timestampValue = firestoreProtoTimestamp(value.timestampValue);
1501
1514
  if (value.mapValue !== undefined) converted.mapValue = { fields: firestoreProtoFields(value.mapValue.fields) };
1502
1515
  if (value.arrayValue !== undefined) {
@@ -1507,6 +1520,14 @@ function firestoreProtoFields(fields) {
1507
1520
  }
1508
1521
 
1509
1522
  function writtenFirestoreEvent(event) {
1523
+ if (event.datacontenttype === "application/protobuf") {
1524
+ // A single character repetition avoids stack growth; require the absolute end, including no trailing newline.
1525
+ if (typeof event.data !== "string" || event.data.length % 4 !== 0 ||
1526
+ !/^[A-Za-z0-9+/]*={0,2}(?![\s\S])/.test(event.data)) {
1527
+ throw new Error("Firestore protobuf IPC data must be base64 text");
1528
+ }
1529
+ return { ...event, data: Buffer.from(event.data, "base64") };
1530
+ }
1510
1531
  if (!["google.cloud.firestore.document.v1.written", "google.cloud.firestore.document.v1.written.withAuthContext"].includes(event.type) ||
1511
1532
  !event.datacontenttype?.includes("application/json")) return event;
1512
1533
  // The SDK's protobuf decoder derives missing snapshot paths from typed metadata,
@@ -1538,7 +1559,7 @@ async function invoke(functions, manifest, msg) {
1538
1559
  throw new Error("entry point does not match the function manifest");
1539
1560
  }
1540
1561
  if (msg.trigger !== spec.trigger?.type ||
1541
- !["schedule", "database", "firestore", "storage", "pubsub", "eventarc", "auth"].includes(msg.trigger)) {
1562
+ !["schedule", "database", "firestore", "storage", "pubsub", "eventarc", "auth", "remoteConfig"].includes(msg.trigger)) {
1542
1563
  throw new Error("trigger does not match the function manifest");
1543
1564
  }
1544
1565
  const fn = functions.get(spec.entryPoint);
@@ -1581,6 +1602,7 @@ async function invoke(functions, manifest, msg) {
1581
1602
  case "storage":
1582
1603
  await fn({ ...msg.event, data: storageObjectInRecordedOrder(msg.event.data) });
1583
1604
  return;
1605
+ case "remoteConfig":
1584
1606
  case "pubsub":
1585
1607
  await fn(msg.event);
1586
1608
  return;
@@ -7,6 +7,7 @@ dict (`firestore_fn.py:108-131`). The adapter therefore builds the production fo
7
7
  (`functions_framework/__init__.py:174-188`).
8
8
  """
9
9
 
10
+ import base64
10
11
  import json
11
12
  import os
12
13
  import re
@@ -167,6 +168,13 @@ def firestore_request(event: dict, raw_data: str | None = None) -> Delivery:
167
168
  if is_strict():
168
169
  headers.setdefault("ce-dataschema", _FIRESTORE_DATASCHEMA)
169
170
  data = event.get("data")
171
+ if event.get("datacontenttype") == "application/protobuf":
172
+ # Binary IPC data is base64; preserve Rust's protobuf bytes for the real SDK.
173
+ if not isinstance(data, str):
174
+ raise ValueError("Firestore protobuf IPC data must be base64 text")
175
+ body = base64.b64decode(data, validate=True)
176
+ headers["content-type"] = "application/protobuf"
177
+ return Delivery(headers, body, body)
170
178
  message = firestore_events.DocumentEventData.from_json(json.dumps(data if isinstance(data, dict) else {}))
171
179
  headers["content-type"] = "application/protobuf"
172
180
  body = firestore_events.DocumentEventData.serialize(message)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fireemu/darwin-x64",
3
- "version": "0.44.0",
3
+ "version": "0.45.0",
4
4
  "description": "The fireemu daemon for darwin-x64",
5
5
  "homepage": "https://t-k.github.io/fireemu/",
6
6
  "repository": {