service-bridge 2.0.0-alpha.7 → 2.0.0-alpha.8
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/dist/{chunk-UBTCKHIQ.js → chunk-3RW45BQJ.js} +4 -18
- package/dist/chunk-3RW45BQJ.js.map +1 -0
- package/dist/{chunk-BBFSAJ5H.js → chunk-UWGAMSLS.js} +3 -3
- package/dist/chunk-UWGAMSLS.js.map +1 -0
- package/dist/{contract-hash-FZ2NCGCH.js → contract-hash-FYYBVXVN.js} +2 -2
- package/dist/http/express/index.d.ts +1 -1
- package/dist/http/fastify/index.d.ts +1 -1
- package/dist/http/hono/index.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +178 -91
- package/dist/index.js.map +1 -1
- package/dist/serializer-KBOMGBVR.js +8 -0
- package/dist/{service-bridge-DGZXGuhl.d.ts → service-bridge-DJG5x0sb.d.ts} +205 -129
- package/package.json +1 -1
- package/dist/chunk-BBFSAJ5H.js.map +0 -1
- package/dist/chunk-UBTCKHIQ.js.map +0 -1
- package/dist/serializer-V55NB2PE.js +0 -8
- /package/dist/{contract-hash-FZ2NCGCH.js.map → contract-hash-FYYBVXVN.js.map} +0 -0
- /package/dist/{serializer-V55NB2PE.js.map → serializer-KBOMGBVR.js.map} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
computeSerializerHash
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-UWGAMSLS.js";
|
|
4
4
|
|
|
5
5
|
// src/serde/serializer.ts
|
|
6
6
|
import protobuf2 from "protobufjs";
|
|
@@ -117,13 +117,7 @@ function buildType(root, typeName, fields) {
|
|
|
117
117
|
}
|
|
118
118
|
function typeToSerializer(type) {
|
|
119
119
|
const jsonSchema = type.toJSON();
|
|
120
|
-
const
|
|
121
|
-
encode: () => new Uint8Array(),
|
|
122
|
-
decode: () => null,
|
|
123
|
-
contractHash: () => "",
|
|
124
|
-
toJsonSchema: () => jsonSchema
|
|
125
|
-
};
|
|
126
|
-
const hash = computeSerializerHash(stub);
|
|
120
|
+
const hash = computeSerializerHash(jsonSchema);
|
|
127
121
|
return {
|
|
128
122
|
encode(value) {
|
|
129
123
|
const err = type.verify(value);
|
|
@@ -214,7 +208,7 @@ function typeToSerializer2(root, file, messageName) {
|
|
|
214
208
|
);
|
|
215
209
|
}
|
|
216
210
|
const jsonSchema = type.toJSON();
|
|
217
|
-
const hash =
|
|
211
|
+
const hash = computeSerializerHash(jsonSchema);
|
|
218
212
|
return {
|
|
219
213
|
encode(value) {
|
|
220
214
|
const err = type.verify(value);
|
|
@@ -236,16 +230,8 @@ function typeToSerializer2(root, file, messageName) {
|
|
|
236
230
|
}
|
|
237
231
|
};
|
|
238
232
|
}
|
|
239
|
-
function perSerializerHash(descriptor) {
|
|
240
|
-
return computeSerializerHash({
|
|
241
|
-
encode: () => new Uint8Array(),
|
|
242
|
-
decode: () => null,
|
|
243
|
-
contractHash: () => "",
|
|
244
|
-
toJsonSchema: () => descriptor
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
233
|
|
|
248
234
|
export {
|
|
249
235
|
buildSchemaPair
|
|
250
236
|
};
|
|
251
|
-
//# sourceMappingURL=chunk-
|
|
237
|
+
//# sourceMappingURL=chunk-3RW45BQJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/serde/serializer.ts","../src/serde/json-schema-serde.ts"],"sourcesContent":["import protobuf from \"protobufjs\";\nimport {\n\tbuildSchemaPairFromJsonFile,\n\ttype JsonSchemaFileSpec,\n} from \"./json-schema-serde\";\n\n// ProtoFileSpec references a user .proto file. `input`/`output` are optional —\n// when omitted, the SDK resolves them from the file's `service` definition by\n// matching the registered method name (gRPC-native, the only supported path).\n// Pass `input`/`output` explicitly for .proto files without a service block.\nexport interface ProtoFileSpec {\n\tprotoFile: string;\n\t// Optional: explicit message names. Required only when the .proto file has\n\t// no matching `service { rpc <method>(In) returns (Out); }` entry.\n\tinput?: string;\n\toutput?: string;\n\t// Optional: method name to resolve against the service definition. Defaults\n\t// to the name passed to `Handle.rpc`/`stream` (set automatically by Handle).\n\tmethod?: string;\n}\n\n// SchemaSpec is the public type for handler/caller schema declaration.\n// Two sources supported: .proto file or .schema.json file (with explicit\n// fieldNumber per property). See ./README.md and ./json-schema-serde.ts.\nexport type SchemaSpec = ProtoFileSpec | JsonSchemaFileSpec;\n\nfunction isProtoFileSpec(spec: SchemaSpec): spec is ProtoFileSpec {\n\treturn (spec as ProtoFileSpec).protoFile !== undefined;\n}\n\n// Serializer encodes/decodes a single Protobuf message type at runtime,\n// produced from a user-supplied .proto file via protobufjs.\nexport interface Serializer {\n\tencode(value: unknown): Uint8Array;\n\tdecode(bytes: Uint8Array): unknown;\n\tcontractHash(): string;\n\ttoJsonSchema(): Record<string, unknown>;\n}\n\nexport interface SchemaPair {\n\tinput: Serializer;\n\toutput: Serializer;\n}\n\n// buildSchemaPair dispatches to the correct loader based on SchemaSpec kind.\n// .proto files load via protobufjs.load (async); .schema.json files via\n// json-schema-serde (sync). Result is always a SchemaPair with stable contract\n// hashes.\nexport async function buildSchemaPair(spec: SchemaSpec): Promise<SchemaPair> {\n\tif (isProtoFileSpec(spec)) {\n\t\tlet root: protobuf.Root;\n\t\ttry {\n\t\t\troot = await protobuf.load(spec.protoFile);\n\t\t} catch (err) {\n\t\t\tthrow new Error(\n\t\t\t\t`serde: load proto file ${spec.protoFile}: ${(err as Error).message}`,\n\t\t\t);\n\t\t}\n\t\tconst { input, output } = resolveProtoMessages(root, spec);\n\t\treturn {\n\t\t\tinput: typeToSerializer(root, spec.protoFile, input),\n\t\t\toutput: typeToSerializer(root, spec.protoFile, output),\n\t\t};\n\t}\n\t// JsonSchemaFileSpec — synchronous file read + dynamic Type build.\n\treturn buildSchemaPairFromJsonFile(spec);\n}\n\n// resolveProtoMessages picks input/output message names from a ProtoFileSpec.\n// Resolution order:\n// 1. Explicit spec.input + spec.output → used as-is.\n// 2. spec.method provided + service { rpc <method>(In) returns (Out); } found\n// → take requestType/responseType from the service definition.\n//\n// Throws with a clear error otherwise — convention-based and unique-pair\n// fallbacks were removed (ADR 0001 / cleanup #32) because they hid contract\n// mistakes behind name similarity instead of failing loudly.\nfunction resolveProtoMessages(\n\troot: protobuf.Root,\n\tspec: ProtoFileSpec,\n): { input: string; output: string } {\n\tif (spec.input && spec.output) {\n\t\treturn { input: spec.input, output: spec.output };\n\t}\n\n\tconst method = spec.method;\n\tif (method) {\n\t\tconst fromService = findInService(root, method);\n\t\tif (fromService) return fromService;\n\t}\n\n\tthrow new Error(\n\t\t`serde: cannot resolve input/output for ${spec.protoFile}` +\n\t\t\t(method ? ` (method=${method})` : \"\") +\n\t\t\t\". Add a `service { rpc <method>(In) returns (Out); }` block or pass `input` and `output` explicitly.\",\n\t);\n}\n\nfunction findInService(\n\troot: protobuf.Root,\n\tmethod: string,\n): { input: string; output: string } | null {\n\tlet found: { input: string; output: string } | null = null;\n\twalk(root, (obj) => {\n\t\tif (!(obj instanceof protobuf.Service)) return;\n\t\tconst m = obj.methods[method];\n\t\tif (m) {\n\t\t\tfound = { input: m.requestType, output: m.responseType };\n\t\t}\n\t});\n\treturn found;\n}\n\nfunction walk(\n\tobj: protobuf.NamespaceBase,\n\tfn: (item: protobuf.ReflectionObject) => void,\n): void {\n\tfn(obj);\n\tconst nested = obj.nested;\n\tif (!nested) return;\n\tfor (const child of Object.values(nested)) {\n\t\tif (child instanceof protobuf.Namespace) {\n\t\t\twalk(child, fn);\n\t\t} else {\n\t\t\tfn(child);\n\t\t}\n\t}\n}\n\nfunction typeToSerializer(\n\troot: protobuf.Root,\n\tfile: string,\n\tmessageName: string,\n): Serializer {\n\tlet type: protobuf.Type;\n\ttry {\n\t\ttype = root.lookupType(messageName);\n\t} catch (err) {\n\t\tthrow new Error(\n\t\t\t`serde: message ${messageName} not found in ${file}: ${(err as Error).message}`,\n\t\t);\n\t}\n\n\tconst jsonSchema = type.toJSON() as unknown as Record<string, unknown>;\n\t// The per-serializer hash here is informational only; the cross-side\n\t// compatibility hash that LB filters on is computeContractHash(pair) from\n\t// ./contract-hash.ts, which combines input + output.\n\tconst hash = perSerializerHash(jsonSchema);\n\n\treturn {\n\t\tencode(value: unknown): Uint8Array {\n\t\t\tconst err = type.verify(value as object);\n\t\t\tif (err) {\n\t\t\t\tthrow new Error(`serde: encode ${messageName}: ${err}`);\n\t\t\t}\n\t\t\tconst msg = type.fromObject(value as object);\n\t\t\treturn type.encode(msg).finish();\n\t\t},\n\t\tdecode(bytes: Uint8Array): unknown {\n\t\t\tconst msg = type.decode(bytes);\n\t\t\treturn type.toObject(msg, { defaults: true });\n\t\t},\n\t\tcontractHash(): string {\n\t\t\treturn hash;\n\t\t},\n\t\ttoJsonSchema(): Record<string, unknown> {\n\t\t\treturn jsonSchema;\n\t\t},\n\t};\n}\n\n// perSerializerHash reuses the shared canonical-JSON algorithm from\n// contract-hash.ts so the per-serializer informational hash and the\n// SchemaPair compatibility hash stay in lockstep.\nimport { computeSerializerHash as perSerializerHash } from \"./contract-hash\";\n","import { readFileSync } from \"node:fs\";\nimport protobuf from \"protobufjs\";\nimport { computeSerializerHash } from \"./contract-hash\";\nimport type { SchemaPair, Serializer } from \"./serializer\";\n\n// JsonSchemaFileSpec references a .schema.json file describing both input and\n// output messages with explicit field numbers per property. Unlike inline JSON\n// schema (removed permanently), this format is safe for schema evolution\n// because field numbers are pinned by the schema author.\nexport interface JsonSchemaFileSpec {\n\tschemaFile: string;\n}\n\n// .schema.json file shape:\n// {\n// \"input\": { \"<MessageName>\": { \"<field>\": { type, fieldNumber }, ... } },\n// \"output\": { \"<MessageName>\": { ... } }\n// }\ninterface SchemaFile {\n\tinput: Record<string, SchemaFields>;\n\toutput: Record<string, SchemaFields>;\n}\n\ntype SchemaFields = Record<string, SchemaField>;\n\ninterface SchemaField {\n\ttype: ProtoFieldType;\n\tfieldNumber?: number;\n\t// For type=\"array\": the element type and its own field number rules.\n\t// The repeated field itself uses the outer `fieldNumber`.\n\tarray?: { type: ProtoFieldType; nested?: SchemaFields };\n\t// For type=\"object\": nested message fields with their own field numbers.\n\tnested?: SchemaFields;\n}\n\ntype ProtoFieldType =\n\t| \"string\"\n\t| \"bool\"\n\t| \"int32\"\n\t| \"int64\"\n\t| \"uint32\"\n\t| \"uint64\"\n\t| \"float\"\n\t| \"double\"\n\t| \"bytes\"\n\t| \"object\"\n\t| \"array\";\n\n// buildSchemaPairFromJsonFile loads the schema file, validates field numbers,\n// constructs protobufjs Types dynamically, and returns a SchemaPair compatible\n// with the rest of the rpc/serde stack.\nexport function buildSchemaPairFromJsonFile(\n\tspec: JsonSchemaFileSpec,\n): SchemaPair {\n\tlet raw: string;\n\ttry {\n\t\traw = readFileSync(spec.schemaFile, \"utf8\");\n\t} catch (err) {\n\t\tthrow new Error(\n\t\t\t`serde: read schema file ${spec.schemaFile}: ${(err as Error).message}`,\n\t\t);\n\t}\n\n\tlet parsed: SchemaFile;\n\ttry {\n\t\tparsed = JSON.parse(raw) as SchemaFile;\n\t} catch (err) {\n\t\tthrow new Error(\n\t\t\t`serde: parse schema file ${spec.schemaFile}: ${(err as Error).message}`,\n\t\t);\n\t}\n\n\tif (!parsed.input || !parsed.output) {\n\t\tthrow new Error(\n\t\t\t`serde: schema file ${spec.schemaFile} must have top-level \"input\" and \"output\" objects`,\n\t\t);\n\t}\n\n\tconst root = new protobuf.Root();\n\tconst inputType = buildType(root, \"Input\", firstMessage(parsed.input));\n\tconst outputType = buildType(root, \"Output\", firstMessage(parsed.output));\n\troot.add(inputType);\n\troot.add(outputType);\n\n\treturn {\n\t\tinput: typeToSerializer(inputType),\n\t\toutput: typeToSerializer(outputType),\n\t};\n}\n\n// firstMessage extracts the single message definition from { \"<MessageName>\": fields }.\n// Throws if the section is empty or has more than one entry — exactly one\n// message per input/output section is supported.\nfunction firstMessage(section: Record<string, SchemaFields>): SchemaFields {\n\tconst names = Object.keys(section);\n\tif (names.length !== 1) {\n\t\tthrow new Error(\n\t\t\t`serde: each of input/output must declare exactly one message, got ${names.length}`,\n\t\t);\n\t}\n\treturn section[names[0]!]!;\n}\n\n// buildType recursively constructs a protobufjs.Type from a SchemaFields tree.\nfunction buildType(\n\troot: protobuf.Root,\n\ttypeName: string,\n\tfields: SchemaFields,\n): protobuf.Type {\n\tconst type = new protobuf.Type(typeName);\n\tconst seenNumbers = new Set<number>();\n\n\tfor (const [name, field] of Object.entries(fields)) {\n\t\tif (typeof field.fieldNumber !== \"number\") {\n\t\t\tthrow new Error(\n\t\t\t\t`serde: field \"${name}\" missing required fieldNumber — schema evolution requires explicit numbers`,\n\t\t\t);\n\t\t}\n\t\tif (seenNumbers.has(field.fieldNumber)) {\n\t\t\tthrow new Error(\n\t\t\t\t`serde: duplicate fieldNumber ${field.fieldNumber} in ${typeName}`,\n\t\t\t);\n\t\t}\n\t\tseenNumbers.add(field.fieldNumber);\n\n\t\tif (field.type === \"object\") {\n\t\t\tif (!field.nested) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`serde: field \"${name}\" type=object requires nested fields`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst nestedTypeName = `${typeName}_${name}`;\n\t\t\tconst nestedType = buildType(root, nestedTypeName, field.nested);\n\t\t\troot.add(nestedType);\n\t\t\ttype.add(new protobuf.Field(name, field.fieldNumber, nestedTypeName));\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (field.type === \"array\") {\n\t\t\tif (!field.array) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`serde: field \"${name}\" type=array requires array spec`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (field.array.type === \"object\") {\n\t\t\t\tif (!field.array.nested) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`serde: array field \"${name}\" of objects requires nested fields`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst nestedTypeName = `${typeName}_${name}_elem`;\n\t\t\t\tconst nestedType = buildType(root, nestedTypeName, field.array.nested);\n\t\t\t\troot.add(nestedType);\n\t\t\t\ttype.add(\n\t\t\t\t\tnew protobuf.Field(\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tfield.fieldNumber,\n\t\t\t\t\t\tnestedTypeName,\n\t\t\t\t\t\t\"repeated\",\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\ttype.add(\n\t\t\t\t\tnew protobuf.Field(\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tfield.fieldNumber,\n\t\t\t\t\t\tfield.array.type,\n\t\t\t\t\t\t\"repeated\",\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Scalar.\n\t\ttype.add(new protobuf.Field(name, field.fieldNumber, field.type));\n\t}\n\n\treturn type;\n}\n\nfunction typeToSerializer(type: protobuf.Type): Serializer {\n\tconst jsonSchema = type.toJSON() as unknown as Record<string, unknown>;\n\t// Per-serializer hash is informational only — LB uses the SchemaPair hash\n\t// from contract-hash.ts:computeContractHash.\n\tconst hash = computeSerializerHash(jsonSchema);\n\treturn {\n\t\tencode(value: unknown): Uint8Array {\n\t\t\tconst err = type.verify(value as object);\n\t\t\tif (err) {\n\t\t\t\tthrow new Error(`serde: encode ${type.name}: ${err}`);\n\t\t\t}\n\t\t\tconst msg = type.fromObject(value as object);\n\t\t\treturn type.encode(msg).finish();\n\t\t},\n\t\tdecode(bytes: Uint8Array): unknown {\n\t\t\tconst msg = type.decode(bytes);\n\t\t\treturn type.toObject(msg, { defaults: true });\n\t\t},\n\t\tcontractHash(): string {\n\t\t\treturn hash;\n\t\t},\n\t\ttoJsonSchema(): Record<string, unknown> {\n\t\t\treturn jsonSchema;\n\t\t},\n\t};\n}\n"],"mappings":"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","names":["protobuf","protobuf","typeToSerializer"]}
|
|
@@ -5,8 +5,8 @@ function computeContractHash(pair) {
|
|
|
5
5
|
const outputCanonical = canonicalize(pair.output.toJsonSchema());
|
|
6
6
|
return createHash("sha256").update(`${inputCanonical}:${outputCanonical}`).digest("hex");
|
|
7
7
|
}
|
|
8
|
-
function computeSerializerHash(
|
|
9
|
-
const canonical = canonicalize(
|
|
8
|
+
function computeSerializerHash(descriptor) {
|
|
9
|
+
const canonical = canonicalize(descriptor);
|
|
10
10
|
return createHash("sha256").update(canonical).digest("hex");
|
|
11
11
|
}
|
|
12
12
|
function canonicalize(value) {
|
|
@@ -26,4 +26,4 @@ export {
|
|
|
26
26
|
computeSerializerHash,
|
|
27
27
|
canonicalize
|
|
28
28
|
};
|
|
29
|
-
//# sourceMappingURL=chunk-
|
|
29
|
+
//# sourceMappingURL=chunk-UWGAMSLS.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/serde/contract-hash.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport type { SchemaPair } from \"./serializer\";\n\n// computeContractHash returns the canonical SHA-256 (hex) of a SchemaPair.\n// The hash identifies an exact (input, output) schema combination — caller\n// and callee that load the SAME schema source MUST produce the SAME hash.\n//\n// Algorithm:\n// 1. Take input.toJsonSchema() and output.toJsonSchema() — both are\n// protobufjs Type descriptors (deterministic structure).\n// 2. Canonicalize each (sorted keys, no whitespace) into a JSON string.\n// Arrays preserve order; objects sort by key recursively.\n// 3. Concatenate as `<input_canonical>:<output_canonical>`.\n// 4. SHA-256, hex-encode.\n//\n// This is the SINGLE source of truth for contract hashing across the SDK.\n// Runtime stores the hash opaque and does NOT recompute (ADR 0005, variant 3).\n//\n// @public — см. ./README.md\nexport function computeContractHash(pair: SchemaPair): string {\n\tconst inputCanonical = canonicalize(pair.input.toJsonSchema());\n\tconst outputCanonical = canonicalize(pair.output.toJsonSchema());\n\treturn createHash(\"sha256\")\n\t\t.update(`${inputCanonical}:${outputCanonical}`)\n\t\t.digest(\"hex\");\n}\n\n// computeSerializerHash hashes a single JSON-schema descriptor (one side of a\n// pair). Takes the descriptor directly — callers pass `serializer.toJsonSchema()`\n// — so building a serializer is not required just to hash its schema.\nexport function computeSerializerHash(\n\tdescriptor: Record<string, unknown>,\n): string {\n\tconst canonical = canonicalize(descriptor);\n\treturn createHash(\"sha256\").update(canonical).digest(\"hex\");\n}\n\n// canonicalize produces the deterministic JSON string used for hashing.\n// Rules:\n// - null / primitives → JSON.stringify (standard)\n// - arrays → preserve element order, recurse on each element\n// - objects → sort keys lexicographically, recurse on each value\n// - no whitespace, no trailing commas\n//\n// @internal — см. ./README.md\nexport function canonicalize(value: unknown): string {\n\tif (value === null || typeof value !== \"object\") {\n\t\treturn JSON.stringify(value);\n\t}\n\tif (Array.isArray(value)) {\n\t\treturn `[${value.map(canonicalize).join(\",\")}]`;\n\t}\n\tconst obj = value as Record<string, unknown>;\n\tconst keys = Object.keys(obj).sort();\n\treturn `{${keys\n\t\t.map((k) => `${JSON.stringify(k)}:${canonicalize(obj[k])}`)\n\t\t.join(\",\")}}`;\n}\n"],"mappings":";AAAA,SAAS,kBAAkB;AAmBpB,SAAS,oBAAoB,MAA0B;AAC7D,QAAM,iBAAiB,aAAa,KAAK,MAAM,aAAa,CAAC;AAC7D,QAAM,kBAAkB,aAAa,KAAK,OAAO,aAAa,CAAC;AAC/D,SAAO,WAAW,QAAQ,EACxB,OAAO,GAAG,cAAc,IAAI,eAAe,EAAE,EAC7C,OAAO,KAAK;AACf;AAKO,SAAS,sBACf,YACS;AACT,QAAM,YAAY,aAAa,UAAU;AACzC,SAAO,WAAW,QAAQ,EAAE,OAAO,SAAS,EAAE,OAAO,KAAK;AAC3D;AAUO,SAAS,aAAa,OAAwB;AACpD,MAAI,UAAU,QAAQ,OAAO,UAAU,UAAU;AAChD,WAAO,KAAK,UAAU,KAAK;AAAA,EAC5B;AACA,MAAI,MAAM,QAAQ,KAAK,GAAG;AACzB,WAAO,IAAI,MAAM,IAAI,YAAY,EAAE,KAAK,GAAG,CAAC;AAAA,EAC7C;AACA,QAAM,MAAM;AACZ,QAAM,OAAO,OAAO,KAAK,GAAG,EAAE,KAAK;AACnC,SAAO,IAAI,KACT,IAAI,CAAC,MAAM,GAAG,KAAK,UAAU,CAAC,CAAC,IAAI,aAAa,IAAI,CAAC,CAAC,CAAC,EAAE,EACzD,KAAK,GAAG,CAAC;AACZ;","names":[]}
|
|
@@ -2,10 +2,10 @@ import {
|
|
|
2
2
|
canonicalize,
|
|
3
3
|
computeContractHash,
|
|
4
4
|
computeSerializerHash
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-UWGAMSLS.js";
|
|
6
6
|
export {
|
|
7
7
|
canonicalize,
|
|
8
8
|
computeContractHash,
|
|
9
9
|
computeSerializerHash
|
|
10
10
|
};
|
|
11
|
-
//# sourceMappingURL=contract-hash-
|
|
11
|
+
//# sourceMappingURL=contract-hash-FYYBVXVN.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FastifyPluginAsync } from 'fastify';
|
|
2
|
-
import { v as OpHandle, p as ServiceBridge } from '../../service-bridge-
|
|
2
|
+
import { v as OpHandle, p as ServiceBridge } from '../../service-bridge-DJG5x0sb.js';
|
|
3
3
|
import '@grpc/grpc-js';
|
|
4
4
|
import '@bufbuild/protobuf/wire';
|
|
5
5
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { A as AdvertiseConfig, C as CallOpts, a as CatchupPolicy, b as ConnectedEvent, c as CronTrigger, D as DeclaredDep, d as DelayedTrigger, e as DisconnectedEvent, E as EventDomain, I as Identity, f as IntervalTrigger, J as JobDomain, g as JobHandler, h as JobHandlerCtx, i as JobOpts, M as MethodDescriptor, j as MethodType, O as OverlapPolicy, P as PolicyViolationEvent, k as PublishOpts, R as ReconnectingEvent, l as RetryOpts, m as RetryPolicy, n as RpcDomain, o as RpcHandlerOpts, S as SchemaSpec, p as ServiceBridge, q as ServiceBridgeError, r as ServiceBridgeOptions, s as ServiceDeps, T as Trigger, t as TypedClient, W as WorkflowDomain, u as WorkflowHandlerOpts } from './service-bridge-
|
|
1
|
+
export { A as AdvertiseConfig, C as CallOpts, a as CatchupPolicy, b as ConnectedEvent, c as CronTrigger, D as DeclaredDep, d as DelayedTrigger, e as DisconnectedEvent, E as EventDomain, I as Identity, f as IntervalTrigger, J as JobDomain, g as JobHandler, h as JobHandlerCtx, i as JobOpts, M as MethodDescriptor, j as MethodType, O as OverlapPolicy, P as PolicyViolationEvent, k as PublishOpts, R as ReconnectingEvent, l as RetryOpts, m as RetryPolicy, n as RpcDomain, o as RpcHandlerOpts, S as SchemaSpec, p as ServiceBridge, q as ServiceBridgeError, r as ServiceBridgeOptions, s as ServiceDeps, T as Trigger, t as TypedClient, W as WorkflowDomain, u as WorkflowHandlerOpts } from './service-bridge-DJG5x0sb.js';
|
|
2
2
|
import '@grpc/grpc-js';
|
|
3
3
|
import '@bufbuild/protobuf/wire';
|
|
4
4
|
|