@fireemu/darwin-arm64 0.9.0 → 0.11.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-arm64
2
2
 
3
3
  The [fireemu](https://www.npmjs.com/package/fireemu) daemon built for `darwin-arm64`
4
- (Rust target `aarch64-apple-darwin`), version 0.9.0.
4
+ (Rust target `aarch64-apple-darwin`), version 0.11.0.
5
5
 
6
6
  macOS 13 or newer on Apple silicon.
7
7
 
package/bin/fireemu CHANGED
Binary file
@@ -1056,6 +1056,13 @@ function describe(name, fn, instrumentation) {
1056
1056
 
1057
1057
  // v1 functions are called as (data, context) with the legacy event shapes.
1058
1058
 
1059
+ // An RFC 3339 time with exactly three fractional digits (the extra digits are cut), or the input
1060
+ // unchanged when it is not a time.
1061
+ function millisecondTimestamp(time) {
1062
+ const parsed = new Date(time);
1063
+ return Number.isNaN(parsed.getTime()) ? time : parsed.toISOString();
1064
+ }
1065
+
1059
1066
  function v1Context(msg) {
1060
1067
  const event = msg.event;
1061
1068
  switch (msg.trigger) {
@@ -1071,12 +1078,18 @@ function v1Context(msg) {
1071
1078
  "providers/cloud.firestore/eventTypes/document.write",
1072
1079
  }[event.type];
1073
1080
  return {
1074
- eventId: event.id,
1081
+ // Production hands a 1st gen Firestore handler `<uuid>-0` (observed 2026-09-30): the
1082
+ // CloudEvent id plus the delivery's trigger index.
1083
+ eventId: `${event.id}-0`,
1075
1084
  timestamp: event.time,
1076
1085
  eventType: legacy,
1077
1086
  // A string: firebase-functions rewrites a legacy event type's resource into
1078
1087
  // { service, name } itself (makeCloudFunction); an object here would be nested.
1079
- resource: event.source,
1088
+ resource: [event.project, event.database, event.document].every(
1089
+ value => typeof value === "string" && value.length > 0,
1090
+ )
1091
+ ? `projects/${event.project}/databases/${event.database}/documents/${event.document}`
1092
+ : event.source,
1080
1093
  params: event.params || {},
1081
1094
  };
1082
1095
  }
@@ -1084,7 +1097,12 @@ function v1Context(msg) {
1084
1097
  const o = event.data;
1085
1098
  return {
1086
1099
  eventId: event.id,
1087
- timestamp: event.time,
1100
+ // Production hands a 1st gen Storage handler a timestamp with exactly three fractional
1101
+ // digits (observed 2026-10-01: `2026-10-01T08:49:26.577Z`, after the object's
1102
+ // `timeCreated` of `.486Z`). The runtime admits the event later than it creates the
1103
+ // object, so the timestamp is cut from the admission instant the frame carries; the
1104
+ // CloudEvent time of a finalize event is the creation instant.
1105
+ timestamp: millisecondTimestamp(msg.admittedAt ?? event.time),
1088
1106
  eventType: {
1089
1107
  "google.cloud.storage.object.v1.finalized": "google.storage.object.finalize",
1090
1108
  "google.cloud.storage.object.v1.deleted": "google.storage.object.delete",
@@ -1344,6 +1362,54 @@ async function makeHttpServer(functions, manifest) {
1344
1362
  });
1345
1363
  }
1346
1364
 
1365
+ let writtenFirestoreCodec;
1366
+
1367
+ function firestoreProtoTimestamp(value) {
1368
+ if (typeof value !== "string") return value;
1369
+ const match = /^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})(?:\.(\d{1,9}))?(Z|[+-]\d{2}:\d{2})$/.exec(value);
1370
+ if (!match) throw new Error("invalid Firestore protobuf timestamp");
1371
+ const milliseconds = Date.parse(`${match[1]}${match[3]}`);
1372
+ if (!Number.isFinite(milliseconds)) throw new Error("invalid Firestore protobuf timestamp");
1373
+ return { seconds: Math.floor(milliseconds / 1000), nanos: Number((match[2] ?? "").padEnd(9, "0")) };
1374
+ }
1375
+
1376
+ function firestoreProtoFields(fields) {
1377
+ return Object.fromEntries(Object.entries(fields ?? {}).map(([key, value]) => {
1378
+ const converted = { ...value };
1379
+ if (value.timestampValue !== undefined) converted.timestampValue = firestoreProtoTimestamp(value.timestampValue);
1380
+ if (value.mapValue !== undefined) converted.mapValue = { fields: firestoreProtoFields(value.mapValue.fields) };
1381
+ if (value.arrayValue !== undefined) {
1382
+ converted.arrayValue = { values: (value.arrayValue.values ?? []).map(item => firestoreProtoFields({ item }).item) };
1383
+ }
1384
+ return [key, converted];
1385
+ }));
1386
+ }
1387
+
1388
+ function writtenFirestoreEvent(event) {
1389
+ if (!["google.cloud.firestore.document.v1.written", "google.cloud.firestore.document.v1.written.withAuthContext"].includes(event.type) ||
1390
+ !event.datacontenttype?.includes("application/json")) return event;
1391
+ // The SDK's protobuf decoder derives missing snapshot paths from typed metadata,
1392
+ // independently of the canonical database source. Reuse the codebase's shipped codec.
1393
+ if (!writtenFirestoreCodec) {
1394
+ const require = createRequire(join(sourceDir, "package.json"));
1395
+ const { root } = firebaseFunctionsPackage(require);
1396
+ writtenFirestoreCodec = require(join(root, "protos/compiledFirestore.js")).google.events.cloud.firestore.v1.DocumentEventData;
1397
+ }
1398
+ const data = { ...event.data };
1399
+ for (const side of ["value", "oldValue"]) {
1400
+ if (data[side] === undefined || data[side] === null) continue;
1401
+ const document = { ...data[side], fields: firestoreProtoFields(data[side].fields) };
1402
+ for (const time of ["createTime", "updateTime"]) {
1403
+ if (document[time] !== undefined) document[time] = firestoreProtoTimestamp(document[time]);
1404
+ }
1405
+ data[side] = document;
1406
+ }
1407
+ const message = writtenFirestoreCodec.fromObject(data);
1408
+ const invalid = writtenFirestoreCodec.verify(message);
1409
+ if (invalid) throw new Error(`invalid Firestore protobuf event: ${invalid}`);
1410
+ return { ...event, datacontenttype: "application/protobuf", data: Buffer.from(writtenFirestoreCodec.encode(message).finish()) };
1411
+ }
1412
+
1347
1413
  async function invoke(functions, manifest, msg) {
1348
1414
  const spec = manifest.functions.find((f) => f.name === msg.function);
1349
1415
  if (!spec) throw new Error("unknown function");
@@ -1375,6 +1441,8 @@ async function invoke(functions, manifest, msg) {
1375
1441
  return;
1376
1442
  }
1377
1443
  case "firestore":
1444
+ await fn(writtenFirestoreEvent(msg.event));
1445
+ return;
1378
1446
  case "storage":
1379
1447
  case "pubsub":
1380
1448
  // A custom event reaches the handler as the CloudEvent itself, exactly as the official
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fireemu/darwin-arm64",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "description": "The fireemu daemon for darwin-arm64",
5
5
  "homepage": "https://github.com/t-k/fireemu#readme",
6
6
  "repository": {