@capxul/sdk 1.2.2 → 1.2.3

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.
@@ -925,6 +925,50 @@ function isProvisioningTelemetryDebugEnabled() {
925
925
  return globalThis.process?.env?.CAPXUL_DEBUG_TELEMETRY === "1";
926
926
  }
927
927
  //#endregion
928
+ //#region src/internal/invocation-observation.ts
929
+ const INVOCATION_OBSERVATION = Symbol("capxul.invocation-observation");
930
+ const FAILURE_INVOCATION_SNAPSHOT = Symbol("capxul.failure-invocation-snapshot");
931
+ /** @internal Attach one immutable, non-wire invocation snapshot to an SDK-owned object. */
932
+ function attachInvocationObservation(target, source) {
933
+ const snapshot = Object.freeze(source.context === void 0 ? { active: source.active } : {
934
+ active: source.active,
935
+ context: Object.freeze({ ...source.context })
936
+ });
937
+ Object.defineProperty(target, INVOCATION_OBSERVATION, {
938
+ configurable: false,
939
+ enumerable: false,
940
+ value: snapshot,
941
+ writable: false
942
+ });
943
+ return target;
944
+ }
945
+ /** @internal Read the snapshot without exposing its symbol or adding a wire field. */
946
+ function readInvocationObservation(source) {
947
+ if (typeof source !== "object" || source === null) return void 0;
948
+ return source[INVOCATION_OBSERVATION];
949
+ }
950
+ /** @internal Carry a snapshot across an SDK adapter projection without resolving again. */
951
+ function copyInvocationObservation(source, target) {
952
+ const snapshot = readInvocationObservation(source);
953
+ return snapshot === void 0 ? target : attachInvocationObservation(target, snapshot);
954
+ }
955
+ /** @internal Carry the public call-start delivery decision with its failure envelope. */
956
+ function markFailureInvocationSnapshot(failure, snapshot) {
957
+ Object.defineProperty(failure, FAILURE_INVOCATION_SNAPSHOT, {
958
+ configurable: false,
959
+ enumerable: false,
960
+ value: Object.freeze(snapshot),
961
+ writable: false
962
+ });
963
+ return failure;
964
+ }
965
+ /** @internal Read the call-start delivery decision; undefined means a direct adapter call. */
966
+ function readFailureInvocationSnapshot(failure) {
967
+ if (typeof failure !== "object" || failure === null) return void 0;
968
+ const snapshot = failure[FAILURE_INVOCATION_SNAPSHOT];
969
+ return typeof snapshot === "object" && snapshot !== null && "active" in snapshot ? snapshot : void 0;
970
+ }
971
+ //#endregion
928
972
  //#region src/domain/machine/telemetry.ts
929
973
  const definedEntries = (values) => Object.fromEntries(Object.entries(values).filter((entry) => entry[1] !== void 0));
930
974
  const SAFE_ENGINEERING_ID = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
@@ -1067,7 +1111,7 @@ const boot = (spec, options = {}) => Effect.gen(function* () {
1067
1111
  const makeFailure = (state, event, reason, details) => new ActorFailure(reason, spec.machine, spec.label(state), event._tag, details);
1068
1112
  const nonApplied = (state, event, reason, outcome, env) => {
1069
1113
  const slot = env.origin?.slot ?? spec.slot(event);
1070
- return {
1114
+ return copyInvocationObservation(env.invocation, {
1071
1115
  machine: spec.machine,
1072
1116
  state: spec.label(state),
1073
1117
  event: event._tag,
@@ -1077,9 +1121,9 @@ const boot = (spec, options = {}) => Effect.gen(function* () {
1077
1121
  duration_ms: duration(env.startedAt),
1078
1122
  ...withCarriage(env.invocation),
1079
1123
  ...outcome === "refused" ? { refusal_code: reason } : { error_code: reason }
1080
- };
1124
+ });
1081
1125
  };
1082
- const applied = (from, to, event, slot, epoch, env) => ({
1126
+ const applied = (from, to, event, slot, epoch, env) => copyInvocationObservation(env.invocation, {
1083
1127
  machine: spec.machine,
1084
1128
  from: spec.label(from),
1085
1129
  event: event._tag,
@@ -1606,6 +1650,25 @@ const EpochMsSchema$1 = Schema.Number.pipe(Schema.refine((n) => Number.isSafeInt
1606
1650
  const DurationMsSchema$1 = Schema.Number.pipe(Schema.refine((n) => Number.isSafeInteger(n) && n >= 0, { message: "must be a non-negative safe integer" }));
1607
1651
  //#endregion
1608
1652
  //#region ../wire/src/bootstrap.ts
1653
+ const PUBLIC_POSTHOG_PROJECT_TOKEN = /^phc_[A-Za-z0-9_-]{1,191}$/u;
1654
+ const PostHogIngestOrigin = Schema.String.pipe(Schema.refine((value) => {
1655
+ try {
1656
+ const url = new URL(value);
1657
+ return url.protocol === "https:" && url.username.length === 0 && url.password.length === 0 && url.pathname === "/" && url.search.length === 0 && url.hash.length === 0 && (url.hostname === "posthog.com" || url.hostname.endsWith(".posthog.com"));
1658
+ } catch {
1659
+ return false;
1660
+ }
1661
+ }, { message: "must be a credential-free PostHog HTTPS ingest origin" }));
1662
+ const EngineeringTelemetryBootstrapPolicy = Schema.Struct({
1663
+ host: PostHogIngestOrigin,
1664
+ projectToken: Schema.String.pipe(Schema.refine((value) => PUBLIC_POSTHOG_PROJECT_TOKEN.test(value), { message: "must be a public PostHog project token" })),
1665
+ capxulEnv: Schema.Union([
1666
+ Schema.Literal("development"),
1667
+ Schema.Literal("e2e"),
1668
+ Schema.Literal("staging"),
1669
+ Schema.Literal("production")
1670
+ ])
1671
+ });
1609
1672
  /**
1610
1673
  * `BootstrapEnvelope` v1.
1611
1674
  *
@@ -1631,7 +1694,8 @@ const BootstrapEnvelope = Schema.Struct({
1631
1694
  convexUrl: Schema.String,
1632
1695
  siteBaseUrl: Schema.String,
1633
1696
  openfortPublishableKey: Schema.String,
1634
- shieldPublishableKey: Schema.String
1697
+ shieldPublishableKey: Schema.String,
1698
+ engineeringTelemetry: Schema.optional(EngineeringTelemetryBootstrapPolicy)
1635
1699
  })
1636
1700
  });
1637
1701
  //#endregion
@@ -2879,6 +2943,9 @@ function makeAccountMethods(deps) {
2879
2943
  };
2880
2944
  }
2881
2945
  //#endregion
2946
+ //#region package.json
2947
+ var version = "1.2.3";
2948
+ //#endregion
2882
2949
  //#region src/ports/auth-client.ts
2883
2950
  var AuthClientError = class extends Data.TaggedError("AuthClientError") {};
2884
2951
  var AuthClientPortTag = class extends Context.Service()("@capxul/sdk/ports/AuthClientPort") {};
@@ -2904,11 +2971,11 @@ function fromResult(operation, run) {
2904
2971
  })
2905
2972
  }).pipe(Effect.flatMap((result) => result.ok ? Effect.succeed(result.value) : Effect.fail(new AuthClientError({
2906
2973
  operation,
2907
- kind: errorKind(result.error),
2974
+ kind: errorKind$1(result.error),
2908
2975
  cause: result.error
2909
2976
  }))));
2910
2977
  }
2911
- function errorKind(error) {
2978
+ function errorKind$1(error) {
2912
2979
  if (error.code === "CANCELLED") return "cancelled";
2913
2980
  if (error.code === "NETWORK_ERROR") return "network";
2914
2981
  if (error.code === "INVALID_INPUT" || error.code === "OTP_EXPIRED") return "validation";
@@ -2945,45 +3012,6 @@ function convexCallErrorFromCapxul(operation, error) {
2945
3012
  }
2946
3013
  var ConvexCallPortTag = class extends Context.Service()("@capxul/sdk/ports/ConvexCallPort") {};
2947
3014
  //#endregion
2948
- //#region src/internal/invocation-observation.ts
2949
- const INVOCATION_OBSERVATION = Symbol("capxul.invocation-observation");
2950
- const FAILURE_INVOCATION_SNAPSHOT = Symbol("capxul.failure-invocation-snapshot");
2951
- /** @internal Attach one immutable, non-wire invocation snapshot to an SDK-owned object. */
2952
- function attachInvocationObservation(target, context) {
2953
- const snapshot = Object.freeze(context === void 0 ? {} : { context: Object.freeze({ ...context }) });
2954
- Object.defineProperty(target, INVOCATION_OBSERVATION, {
2955
- configurable: false,
2956
- enumerable: false,
2957
- value: snapshot,
2958
- writable: false
2959
- });
2960
- return target;
2961
- }
2962
- /** @internal Read the snapshot without exposing its symbol or adding a wire field. */
2963
- function readInvocationObservation(source) {
2964
- if (typeof source !== "object" || source === null) return void 0;
2965
- return source[INVOCATION_OBSERVATION];
2966
- }
2967
- /** @internal Carry a snapshot across an SDK adapter projection without resolving again. */
2968
- function copyInvocationObservation(source, target) {
2969
- const snapshot = readInvocationObservation(source);
2970
- return snapshot === void 0 ? target : attachInvocationObservation(target, snapshot.context);
2971
- }
2972
- /** @internal Mark a failure envelope as already resolved at the public invocation boundary. */
2973
- function markFailureInvocationSnapshot(failure) {
2974
- Object.defineProperty(failure, FAILURE_INVOCATION_SNAPSHOT, {
2975
- configurable: false,
2976
- enumerable: false,
2977
- value: true,
2978
- writable: false
2979
- });
2980
- return failure;
2981
- }
2982
- /** @internal Distinguish public-boundary failures from direct adapter calls. */
2983
- function hasFailureInvocationSnapshot(failure) {
2984
- return typeof failure === "object" && failure !== null && failure[FAILURE_INVOCATION_SNAPSHOT] === true;
2985
- }
2986
- //#endregion
2987
3015
  //#region src/ports/identity.ts
2988
3016
  var IdentityError = class extends Data.TaggedError("IdentityError") {};
2989
3017
  function identityErrorFromCapxul(operation, error, cause = error) {
@@ -3162,7 +3190,6 @@ const TelemetryEnvelopeProps = {
3162
3190
  "mcp",
3163
3191
  "e2e"
3164
3192
  ]),
3165
- capxul_e2e_run_id: OptionalString,
3166
3193
  journey_id: OptionalString,
3167
3194
  correlation_id: OptionalString,
3168
3195
  sdk_version: OptionalString
@@ -3764,7 +3791,7 @@ function redactTelemetryEvent(event, options = {}) {
3764
3791
  }
3765
3792
  function redactTelemetryProps(name, props, options = {}) {
3766
3793
  if (props === void 0) return void 0;
3767
- const clone = cloneProps(props);
3794
+ const clone = cloneProps$1(props);
3768
3795
  if (options.rawMode === true) return clone;
3769
3796
  for (const key of PII_PROP_KEYS_BY_EVENT[name] ?? []) redactProperty(clone, key);
3770
3797
  return clone;
@@ -3779,17 +3806,17 @@ function redactProperty(props, key) {
3779
3806
  const value = props[key];
3780
3807
  if (typeof value === "string") props[key] = sha256Hex(value).slice(0, 12);
3781
3808
  }
3782
- function cloneProps(props) {
3809
+ function cloneProps$1(props) {
3783
3810
  const cloned = {};
3784
- for (const [key, value] of Object.entries(props)) cloned[key] = cloneTelemetryValue(value);
3811
+ for (const [key, value] of Object.entries(props)) cloned[key] = cloneTelemetryValue$1(value);
3785
3812
  return cloned;
3786
3813
  }
3787
- function cloneTelemetryValue(value) {
3788
- if (Array.isArray(value)) return value.map(cloneTelemetryValue);
3814
+ function cloneTelemetryValue$1(value) {
3815
+ if (Array.isArray(value)) return value.map(cloneTelemetryValue$1);
3789
3816
  if (value === null || typeof value !== "object") return value;
3790
3817
  if (Object.getPrototypeOf(value) !== Object.prototype) return value;
3791
3818
  const cloned = {};
3792
- for (const [key, nested] of Object.entries(value)) cloned[key] = cloneTelemetryValue(nested);
3819
+ for (const [key, nested] of Object.entries(value)) cloned[key] = cloneTelemetryValue$1(nested);
3793
3820
  return cloned;
3794
3821
  }
3795
3822
  const SHA256_INITIAL_HASH = [
@@ -3947,6 +3974,90 @@ function rotr(value, bits) {
3947
3974
  //#region src/ports/telemetry.ts
3948
3975
  var TelemetryPortTag = class extends Context.Service()("@capxul/sdk/ports/TelemetryPort") {};
3949
3976
  //#endregion
3977
+ //#region src/adapters/telemetry/PostHogTelemetryAdapter.ts
3978
+ var PostHogTelemetryAdapter = class {
3979
+ #capture;
3980
+ #identify;
3981
+ #group;
3982
+ #reset;
3983
+ constructor(deps) {
3984
+ this.#capture = deps.capture;
3985
+ this.#identify = deps.identify ?? (() => void 0);
3986
+ this.#group = deps.group ?? (() => void 0);
3987
+ this.#reset = deps.reset ?? (() => void 0);
3988
+ }
3989
+ emit(event) {
3990
+ return this.#run(() => this.#capture(event.name, redactTelemetryProps(event.name, cloneProps(event.props)), event), "emit", event.name);
3991
+ }
3992
+ identify(input) {
3993
+ return this.#run(() => this.#identify(cloneIdentifyInput(input)), "identify");
3994
+ }
3995
+ group(input) {
3996
+ return this.#run(() => this.#group(cloneGroupInput(input)), "group");
3997
+ }
3998
+ reset() {
3999
+ return this.#run(() => this.#reset(), "reset");
4000
+ }
4001
+ #run(operation, operationName, eventName) {
4002
+ const diagnose = Effect.logWarning("product.telemetry.transport.dropped").pipe(Effect.annotateLogs({
4003
+ operation: operationName,
4004
+ ...eventName === void 0 ? {} : { product_event: eventName }
4005
+ }), Effect.catchCause(() => Effect.void));
4006
+ return Effect.suspend(() => {
4007
+ let pending;
4008
+ try {
4009
+ pending = operation();
4010
+ } catch {
4011
+ return diagnose;
4012
+ }
4013
+ if (pending === void 0) return Effect.void;
4014
+ const transport = Effect.tryPromise({
4015
+ try: () => pending,
4016
+ catch: () => void 0
4017
+ }).pipe(Effect.catch(() => diagnose));
4018
+ return Effect.forkDetach(transport, { startImmediately: true }).pipe(Effect.asVoid);
4019
+ });
4020
+ }
4021
+ };
4022
+ function PostHogTelemetryLayer(deps) {
4023
+ return Layer.succeed(TelemetryPortTag, new PostHogTelemetryAdapter(deps));
4024
+ }
4025
+ function cloneIdentifyInput(input) {
4026
+ const traits = input.traits === void 0 ? void 0 : cloneProps(input.traits);
4027
+ const properties = input.properties === void 0 ? void 0 : cloneProps(input.properties);
4028
+ return {
4029
+ distinctId: input.distinctId,
4030
+ ...input.anonDistinctId === void 0 ? {} : { anonDistinctId: input.anonDistinctId },
4031
+ ...traits === void 0 ? {} : { traits },
4032
+ ...properties === void 0 ? {} : { properties }
4033
+ };
4034
+ }
4035
+ function cloneGroupInput(input) {
4036
+ const properties = input.properties === void 0 ? void 0 : cloneProps(input.properties);
4037
+ return properties === void 0 ? {
4038
+ groupType: input.groupType,
4039
+ groupKey: input.groupKey
4040
+ } : {
4041
+ groupType: input.groupType,
4042
+ groupKey: input.groupKey,
4043
+ properties
4044
+ };
4045
+ }
4046
+ function cloneProps(props) {
4047
+ if (props === void 0) return void 0;
4048
+ const cloned = {};
4049
+ for (const [key, value] of Object.entries(props)) cloned[key] = cloneTelemetryValue(value);
4050
+ return cloned;
4051
+ }
4052
+ function cloneTelemetryValue(value) {
4053
+ if (Array.isArray(value)) return value.map(cloneTelemetryValue);
4054
+ if (value === null || typeof value !== "object") return value;
4055
+ if (Object.getPrototypeOf(value) !== Object.prototype) return value;
4056
+ const cloned = {};
4057
+ for (const [key, nested] of Object.entries(value)) cloned[key] = cloneTelemetryValue(nested);
4058
+ return cloned;
4059
+ }
4060
+ //#endregion
3950
4061
  //#region src/flows/identity.ts
3951
4062
  const IDENTITY_SLOT = {
3952
4063
  session: "identity:session",
@@ -4288,7 +4399,7 @@ const identitySpec = (input, sessionStore = { current: null }) => {
4288
4399
  case "RequestOtp": return {
4289
4400
  slot: IDENTITY_SLOT.auth,
4290
4401
  port: "auth-client",
4291
- run: (send) => call("auth-client.send-otp", input.ports.authClient.sendOtp({ email: toEmail(event.email) })).pipe(Effect.andThen(call("clock.now", input.ports.clock.now)), Effect.flatMap((at) => send({
4402
+ run: (send, controls) => call("auth-client.send-otp", input.ports.authClient.sendOtp(copyInvocationObservation(controls, { email: toEmail(event.email) }))).pipe(Effect.andThen(call("clock.now", input.ports.clock.now)), Effect.flatMap((at) => send({
4292
4403
  _tag: "OtpSent",
4293
4404
  at
4294
4405
  })), Effect.catch((failure) => send({
@@ -4299,10 +4410,10 @@ const identitySpec = (input, sessionStore = { current: null }) => {
4299
4410
  case "VerifyOtp": return {
4300
4411
  slot: IDENTITY_SLOT.auth,
4301
4412
  port: "auth-client",
4302
- run: (send) => call("auth-client.verify-otp", input.ports.authClient.verifyOtp({
4413
+ run: (send, controls) => call("auth-client.verify-otp", input.ports.authClient.verifyOtp(copyInvocationObservation(controls, {
4303
4414
  email: toEmail(event.email),
4304
4415
  otp: event.otp
4305
- })).pipe(Effect.tap((session) => Effect.sync(() => {
4416
+ }))).pipe(Effect.tap((session) => Effect.sync(() => {
4306
4417
  sessionStore.current = session;
4307
4418
  })), Effect.flatMap((session) => sessionProfile(input, session).pipe(Effect.flatMap((loaded) => send({
4308
4419
  _tag: "Verified",
@@ -4316,7 +4427,7 @@ const identitySpec = (input, sessionStore = { current: null }) => {
4316
4427
  case "ReadSession": return {
4317
4428
  slot: IDENTITY_SLOT.session,
4318
4429
  port: "auth-client",
4319
- run: (send) => call("auth-client.get-session", input.ports.authClient.getSession()).pipe(Effect.tap((session) => Effect.sync(() => {
4430
+ run: (send, controls) => call("auth-client.get-session", input.ports.authClient.getSession(copyInvocationObservation(controls, {}))).pipe(Effect.tap((session) => Effect.sync(() => {
4320
4431
  sessionStore.current = session;
4321
4432
  })), Effect.flatMap((session) => sessionProfile(input, session)), Effect.flatMap((loaded) => send({
4322
4433
  _tag: "SessionRead",
@@ -4331,7 +4442,7 @@ const identitySpec = (input, sessionStore = { current: null }) => {
4331
4442
  case "SignOut": return {
4332
4443
  slot: IDENTITY_SLOT.auth,
4333
4444
  port: "auth-client",
4334
- run: (send) => call("auth-client.sign-out", input.ports.authClient.signOut()).pipe(Effect.andThen(send({ _tag: "SignedOut" })), Effect.catch((failure) => send({
4445
+ run: (send, controls) => call("auth-client.sign-out", input.ports.authClient.signOut(copyInvocationObservation(controls, {}))).pipe(Effect.andThen(send({ _tag: "SignedOut" })), Effect.catch((failure) => send({
4335
4446
  _tag: "SignOutFailed",
4336
4447
  failure
4337
4448
  }).pipe(Effect.andThen(Effect.fail(failure)))), Effect.ensuring(Effect.sync(() => {
@@ -6709,6 +6820,275 @@ function detectAuthCacheAdapter() {
6709
6820
  return new InMemoryAuthCacheAdapter();
6710
6821
  }
6711
6822
  //#endregion
6823
+ //#region src/observation.ts
6824
+ const SDK_VERSION = version;
6825
+ /** Stable PostHog event used for typed failures that are expected product outcomes. */
6826
+ const CAPXUL_SDK_EXPECTED_OUTCOME_EVENT = "capxul_sdk_expected_outcome";
6827
+ /**
6828
+ * Adapt the host's already-initialized PostHog-like client. This function does
6829
+ * not import, initialize, configure, or own PostHog.
6830
+ */
6831
+ function postHogFailureObservation(policy, fixedSnapshot) {
6832
+ const prepare = (failure) => {
6833
+ const callStartSnapshot = readFailureInvocationSnapshot(failure) ?? fixedSnapshot;
6834
+ const directSnapshot = callStartSnapshot === void 0 ? policy.snapshot() : void 0;
6835
+ if (!(callStartSnapshot?.active ?? directSnapshot?.active) || policy.client === null || policy.client === void 0) return;
6836
+ const safeFailure = sanitizeFailureObservation(failure);
6837
+ return [safeFailure, postHogProperties(safeFailure, callStartSnapshot?.context ?? directSnapshot?.context)];
6838
+ };
6839
+ const captureOperationFailure = (failure) => {
6840
+ const prepared = prepare(failure);
6841
+ if (prepared === void 0 || policy.client === null || policy.client === void 0) return;
6842
+ const [safeFailure, properties] = prepared;
6843
+ if (classifyOperationOutcome(safeFailure.errorKind) === "expected") {
6844
+ policy.deliver(() => policy.client.capture(CAPXUL_SDK_EXPECTED_OUTCOME_EVENT, {
6845
+ ...properties,
6846
+ outcome_class: "expected"
6847
+ }));
6848
+ return;
6849
+ }
6850
+ policy.deliver(() => policy.client.capture("$exception", postHogExceptionProperties(safeFailure, properties)));
6851
+ };
6852
+ const captureException = (failure) => {
6853
+ const prepared = prepare(failure);
6854
+ if (prepared === void 0 || policy.client === null || policy.client === void 0) return;
6855
+ const [safeFailure, properties] = prepared;
6856
+ policy.deliver(() => policy.client.capture("$exception", postHogExceptionProperties(safeFailure, properties)));
6857
+ };
6858
+ return {
6859
+ resolveContext: () => {
6860
+ const snapshot = fixedSnapshot ?? policy.snapshot();
6861
+ if (!snapshot.active) return void 0;
6862
+ return snapshot.context ?? {};
6863
+ },
6864
+ captureOperationFailure,
6865
+ captureException
6866
+ };
6867
+ }
6868
+ function classifyOperationOutcome(kind) {
6869
+ return EXPECTED_OPERATION_OUTCOMES.has(kind) ? "expected" : "unexpected";
6870
+ }
6871
+ /** @internal Reports a factory-level typed failure without changing its identity. */
6872
+ function observeFailedResult(result, adapter, operation) {
6873
+ if (adapter !== void 0 && isFailedResult(result)) report(adapter, "operation", operation, result.error, resolveAdapterContext(adapter));
6874
+ return result;
6875
+ }
6876
+ function report(adapter, kind, operation, cause, invocationContext) {
6877
+ const operationName = normalizeOperation(operation);
6878
+ const kindName = normalizeErrorKind(errorKind(cause));
6879
+ const context = sanitizeObservationContext({
6880
+ ...invocationContext,
6881
+ ...isCapxulError(cause) && cause.correlationId !== void 0 ? { correlationId: cause.correlationId } : {}
6882
+ });
6883
+ const failure = markFailureInvocationSnapshot({
6884
+ exception: syntheticException(operationName, kindName),
6885
+ sdkVersion: SDK_VERSION,
6886
+ operation: operationName,
6887
+ errorKind: kindName,
6888
+ ...context === void 0 ? {} : { context }
6889
+ }, {
6890
+ active: invocationContext !== void 0,
6891
+ ...context === void 0 ? {} : { context }
6892
+ });
6893
+ try {
6894
+ ignoreDeliveryFailure(kind === "operation" ? adapter.captureOperationFailure(failure) : adapter.captureException(failure));
6895
+ } catch {}
6896
+ }
6897
+ function resolveAdapterContext(adapter) {
6898
+ try {
6899
+ return adapter.resolveContext?.();
6900
+ } catch {
6901
+ return;
6902
+ }
6903
+ }
6904
+ function ignoreDeliveryFailure(delivery) {
6905
+ if (!isPromiseLike(delivery)) return;
6906
+ try {
6907
+ Promise.resolve(delivery).catch(() => void 0);
6908
+ } catch {}
6909
+ }
6910
+ /**
6911
+ * Map an ALREADY-sanitized observation context to the snake_case PostHog
6912
+ * property keys. Shared by the failure boundary here and the host
6913
+ * success-telemetry seam (`telemetry/from-posthog.ts`) so both attach identical
6914
+ * correlation fields from one definition.
6915
+ */
6916
+ function observationContextProps(context) {
6917
+ const props = {};
6918
+ if (context?.application !== void 0) props.application = context.application;
6919
+ if (context?.release !== void 0) props.release = context.release;
6920
+ if (context?.sessionId !== void 0) props.session_id = context.sessionId;
6921
+ if (context?.organizationId !== void 0) props.organization_id = context.organizationId;
6922
+ if (context?.journeyId !== void 0) props.journey_id = context.journeyId;
6923
+ if (context?.correlationId !== void 0) props.correlation_id = context.correlationId;
6924
+ if (context?.anonymousId !== void 0) props.anonymous_id = context.anonymousId;
6925
+ return props;
6926
+ }
6927
+ function postHogProperties(failure, context) {
6928
+ const merged = sanitizeObservationContext({
6929
+ ...context,
6930
+ ...failure.context
6931
+ });
6932
+ return {
6933
+ sdk_version: failure.sdkVersion,
6934
+ operation: failure.operation,
6935
+ error_kind: failure.errorKind,
6936
+ handled: true,
6937
+ ...observationContextProps(merged)
6938
+ };
6939
+ }
6940
+ function postHogExceptionProperties(failure, properties) {
6941
+ const filename = `capxul-sdk-observation://boundary/${failure.operation}`;
6942
+ return {
6943
+ ...properties,
6944
+ $exception_type: failure.errorKind,
6945
+ $exception_message: EXCEPTION_MESSAGE,
6946
+ $exception_level: "error",
6947
+ $exception_list: [{
6948
+ type: failure.errorKind,
6949
+ value: EXCEPTION_MESSAGE,
6950
+ mechanism: {
6951
+ type: "capxul_sdk_boundary",
6952
+ handled: true,
6953
+ synthetic: true
6954
+ },
6955
+ stacktrace: {
6956
+ type: "raw",
6957
+ frames: [{
6958
+ platform: "javascript",
6959
+ filename,
6960
+ function: `CapxulSdkBoundary.${failure.operation}`,
6961
+ lineno: 1,
6962
+ colno: 1,
6963
+ in_app: true
6964
+ }]
6965
+ }
6966
+ }]
6967
+ };
6968
+ }
6969
+ function errorKind(cause) {
6970
+ try {
6971
+ if (isCapxulError(cause)) return normalizeErrorKind(cause.code);
6972
+ if (cause instanceof Error) return normalizeErrorKind(cause.name);
6973
+ return "UnknownFailure";
6974
+ } catch {
6975
+ return "Error";
6976
+ }
6977
+ }
6978
+ function sanitizeFailureObservation(failure) {
6979
+ const operation = normalizeOperation(failure.operation);
6980
+ const kind = normalizeErrorKind(failure.errorKind);
6981
+ const context = sanitizeObservationContext(failure.context);
6982
+ return {
6983
+ exception: syntheticException(operation, kind),
6984
+ sdkVersion: normalizeSdkVersion(failure.sdkVersion),
6985
+ operation,
6986
+ errorKind: kind,
6987
+ ...context === void 0 ? {} : { context }
6988
+ };
6989
+ }
6990
+ function normalizeSdkVersion(value) {
6991
+ return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9.+_-]{0,127}$/u.test(value) ? value : "unknown";
6992
+ }
6993
+ function normalizeOperation(value) {
6994
+ return typeof value === "string" && /^[A-Za-z0-9_][A-Za-z0-9_.-]{0,255}$/u.test(value) ? value : "unknown";
6995
+ }
6996
+ function normalizeErrorKind(value) {
6997
+ return typeof value === "string" && /^[A-Za-z][A-Za-z0-9_.-]{0,63}$/u.test(value) ? value : "Error";
6998
+ }
6999
+ function syntheticException(operation, kind) {
7000
+ const error = /* @__PURE__ */ new Error(EXCEPTION_MESSAGE);
7001
+ error.name = kind;
7002
+ error.stack = `${kind}: ${error.message}\n at CapxulSdkBoundary.${operation} (capxul-sdk-observation://boundary/${operation}:1:1)`;
7003
+ return error;
7004
+ }
7005
+ function isPromiseLike(value) {
7006
+ return (typeof value === "object" || typeof value === "function") && value !== null && typeof value.then === "function";
7007
+ }
7008
+ function isPlainObject(value) {
7009
+ if (typeof value !== "object" || value === null) return false;
7010
+ const prototype = Object.getPrototypeOf(value);
7011
+ return prototype === Object.prototype || prototype === null;
7012
+ }
7013
+ function isCapxulResult(value) {
7014
+ return isPlainObject(value) && typeof value.ok === "boolean";
7015
+ }
7016
+ function isFailedResult(value) {
7017
+ return isCapxulResult(value) && value.ok === false && "error" in value;
7018
+ }
7019
+ //#endregion
7020
+ //#region src/telemetry/from-posthog.ts
7021
+ const PRODUCT_INVOCATION = Symbol("capxul.product-telemetry-invocation");
7022
+ /** @internal Bind paired emit/identify/reset work to one call-start snapshot. */
7023
+ function bindProductTelemetryInvocation(telemetry, source) {
7024
+ return telemetry[PRODUCT_INVOCATION]?.(source) ?? telemetry;
7025
+ }
7026
+ /**
7027
+ * Drop props that must never cross to a host-owned external sink. Today that is
7028
+ * the `$exception` `details` blob: `captureException` serializes
7029
+ * `CapxulError.details` (e.g. `{ asset, available, required }`, `{ name }`,
7030
+ * `{ accountId }` — errors.ts) into it, and the shared redactor has no
7031
+ * `$exception` rule, so it is stripped here at the boundary (infra#1037). The
7032
+ * safe fields (error code, operation, failure_mode, the fixed leak-safe message,
7033
+ * stack frames) are preserved.
7034
+ */
7035
+ function stripHostUnsafeProps(props) {
7036
+ if (props === void 0) return void 0;
7037
+ const { details: _details, ...safe } = props;
7038
+ return safe;
7039
+ }
7040
+ /**
7041
+ * The subset of a posthog-js client the seam calls. `identify` / `group` /
7042
+ * `reset` are optional — a host that only wants event capture can omit them.
7043
+ */
7044
+ /**
7045
+ * Adapt the host's already-initialized posthog-like client into a
7046
+ * `TelemetryPort` for the `telemetry` prop / input. This port SUPPLANTS the
7047
+ * SDK's no-op default telemetry sink (`production.ts` binds it via
7048
+ * `Layer.succeed`, not `compose` — there is no client-side success relay to
7049
+ * compose with); it is additive to Capxul's backend first-party record and
7050
+ * never owns the client.
7051
+ */
7052
+ function postHogProductTelemetry(policy, fixedSnapshot) {
7053
+ const telemetry = new PostHogTelemetryAdapter({
7054
+ capture: (name, props, source) => {
7055
+ const snapshot = readInvocationObservation(source) ?? fixedSnapshot ?? policy.snapshot();
7056
+ if (!snapshot.active || policy.client === null || policy.client === void 0) return;
7057
+ const event = stampTelemetryEnvelope({
7058
+ name,
7059
+ props
7060
+ }, {
7061
+ capxul_env: policy.capxulEnv,
7062
+ producer: "sdk"
7063
+ });
7064
+ policy.deliver(() => policy.client.capture(name, {
7065
+ ...stripHostUnsafeProps(event.props),
7066
+ ...observationContextProps(snapshot.context)
7067
+ }));
7068
+ },
7069
+ identify: (input) => {
7070
+ if (!(fixedSnapshot ?? policy.snapshot()).active || policy.client?.identify === void 0) return;
7071
+ policy.deliver(() => policy.client.identify(input.distinctId, {
7072
+ ...input.traits,
7073
+ ...input.properties
7074
+ }));
7075
+ },
7076
+ group: (input) => {
7077
+ if (!(fixedSnapshot ?? policy.snapshot()).active || policy.client?.group === void 0) return;
7078
+ policy.deliver(() => policy.client.group(input.groupType, input.groupKey, input.properties === void 0 ? void 0 : { ...input.properties }));
7079
+ },
7080
+ reset: () => {
7081
+ if (!(fixedSnapshot ?? policy.snapshot()).active || policy.client?.reset === void 0) return;
7082
+ policy.deliver(() => policy.client.reset());
7083
+ }
7084
+ });
7085
+ Object.defineProperty(telemetry, PRODUCT_INVOCATION, {
7086
+ enumerable: false,
7087
+ value: (source) => postHogProductTelemetry(policy, readInvocationObservation(source) ?? fixedSnapshot ?? policy.snapshot())
7088
+ });
7089
+ return telemetry;
7090
+ }
7091
+ //#endregion
6712
7092
  //#region src/telemetry/identity-product.ts
6713
7093
  const NONE = null;
6714
7094
  const IDENTITY_EVENT_TAG_SET = new Set(IDENTITY_EVENT_TAGS);
@@ -6791,13 +7171,15 @@ const ignoreTransportFailure = (operation, operationName, eventName) => Effect.s
6791
7171
  product_event: eventName
6792
7172
  }), Effect.catchCause(() => Effect.void))));
6793
7173
  function executeIdentityProductObservation(telemetry, record, state) {
6794
- const observation = mapIdentityProductObservation(record, state);
6795
- if (observation === null) return Effect.void;
6796
- const after = () => observation.name === "auth_verified" && state.phase === "authenticated" ? telemetry.identify({
7174
+ const mapped = mapIdentityProductObservation(record, state);
7175
+ if (mapped === null) return Effect.void;
7176
+ copyInvocationObservation(record, mapped);
7177
+ const invocationTelemetry = bindProductTelemetryInvocation(telemetry, mapped);
7178
+ const after = () => mapped.name === "auth_verified" && state.phase === "authenticated" ? invocationTelemetry.identify({
6797
7179
  distinctId: state.session.authUserId,
6798
7180
  traits: { email_domain: emailDomain(state.session.email) }
6799
- }) : observation.name === "auth_signed_out" ? telemetry.reset() : Effect.void;
6800
- return ignoreTransportFailure(() => telemetry.emit(observation), "emit", observation.name).pipe(Effect.andThen(ignoreTransportFailure(after, observation.name === "auth_signed_out" ? "reset" : "identify", observation.name)));
7181
+ }) : mapped.name === "auth_signed_out" ? invocationTelemetry.reset() : Effect.void;
7182
+ return ignoreTransportFailure(() => invocationTelemetry.emit(mapped), "emit", mapped.name).pipe(Effect.andThen(ignoreTransportFailure(after, mapped.name === "auth_signed_out" ? "reset" : "identify", mapped.name)));
6801
7183
  }
6802
7184
  //#endregion
6803
7185
  //#region src/surface/create-capxul-client.ts
@@ -6808,14 +7190,14 @@ function assembleCapxulClient(input) {
6808
7190
  runPromise: Effect.runPromise
6809
7191
  };
6810
7192
  const scope = effectRunner.runSync(Scope.make());
6811
- const actor = effectRunner.runSync(Scope.provide(bootIdentityFlow({
7193
+ const actor = withHostObservation(effectRunner.runSync(Scope.provide(bootIdentityFlow({
6812
7194
  ports: input.ports,
6813
7195
  chainId: input.bootstrap.chainId,
6814
7196
  requirement: input.requirement,
6815
7197
  ...input.signer === void 0 ? {} : { signer: input.signer },
6816
7198
  ...input.organizationSetup === void 0 ? {} : { organizationSetup: input.organizationSetup },
6817
7199
  ...input.otpTtlMs === void 0 ? {} : { otpTtlMs: input.otpTtlMs }
6818
- }), scope));
7200
+ }), scope)), input.hostObservationSnapshot);
6819
7201
  const unsubscribeProductTelemetry = actor.subscribeTransitions((record) => {
6820
7202
  effectRunner.runPromise(executeIdentityProductObservation(input.ports.telemetry, record, actor.snapshot())).catch(() => {});
6821
7203
  });
@@ -6997,7 +7379,21 @@ function assembleCapxulClient(input) {
6997
7379
  }
6998
7380
  };
6999
7381
  }
7382
+ function withHostObservation(actor, snapshot) {
7383
+ if (snapshot === void 0) return actor;
7384
+ const controls = (input) => {
7385
+ const captured = snapshot();
7386
+ const copied = { ...input };
7387
+ return captured === void 0 ? copied : attachInvocationObservation(copied, captured);
7388
+ };
7389
+ return {
7390
+ ...actor,
7391
+ ask: (event, input) => actor.ask(event, controls(input)),
7392
+ tell: (event, input) => actor.tell(event, controls(input)),
7393
+ restoreAuthSession: (session, input) => actor.restoreAuthSession(session, controls(input))
7394
+ };
7395
+ }
7000
7396
  //#endregion
7001
- export { OBSERVATION_CONTEXT_HEADER as A, normalizeBindingEmail as B, convexCallErrorFromCapxul as C, bootstrapErrorFromCapxul as D, BootstrapPortTag as E, isSettingUpLifecycle as F, deriveCapxulSafeAddress as H, formatTraceparent as I, EXCEPTION_MESSAGE as L, sanitizeObservationContext as M, CAPXUL_FUNCTIONS as N, authClientPortFromPromiseAdapter as O, BootstrapEnvelope as P, captureException as R, ConvexCallPortTag as S, ClockPortTag as T, destination as U, BASE_SEPOLIA_CHAIN_ID as V, identityErrorFromCapxul as _, redactTelemetryProps as a, markFailureInvocationSnapshot as b, smartAccountErrorFromCapxul as c, fromWei as d, AccountReadPortTag as f, IdentityPortTag as g, wireChainId as h, redactTelemetryEvent as i, encodeObservationContextHeader as j, AuthClientPortTag as k, SubAccountPortTag as l, toWei as m, detectAuthCacheAdapter as n, stampTelemetryEnvelope as o, accountReadErrorFromCapxul as p, TelemetryPortTag as r, SmartAccountPortTag as s, assembleCapxulClient as t, subAccountErrorFromCapxul as u, copyInvocationObservation as v, ClockError as w, readInvocationObservation as x, hasFailureInvocationSnapshot as y, captureExceptionSync as z };
7397
+ export { version as A, readInvocationObservation as B, convexCallErrorFromCapxul as C, bootstrapErrorFromCapxul as D, BootstrapPortTag as E, BootstrapEnvelope as F, deriveCapxulSafeAddress as G, captureExceptionSync as H, EngineeringTelemetryBootstrapPolicy as I, destination as K, isSettingUpLifecycle as L, encodeObservationContextHeader as M, sanitizeObservationContext as N, authClientPortFromPromiseAdapter as O, CAPXUL_FUNCTIONS as P, formatTraceparent as R, ConvexCallPortTag as S, ClockPortTag as T, normalizeBindingEmail as U, captureException as V, BASE_SEPOLIA_CHAIN_ID as W, accountReadErrorFromCapxul as _, observeFailedResult as a, IdentityPortTag as b, PostHogTelemetryLayer as c, SmartAccountPortTag as d, smartAccountErrorFromCapxul as f, AccountReadPortTag as g, fromWei as h, observationContextProps as i, OBSERVATION_CONTEXT_HEADER as j, AuthClientPortTag as k, TelemetryPortTag as l, subAccountErrorFromCapxul as m, postHogProductTelemetry as n, postHogFailureObservation as o, SubAccountPortTag as p, CAPXUL_SDK_EXPECTED_OUTCOME_EVENT as r, detectAuthCacheAdapter as s, assembleCapxulClient as t, redactTelemetryEvent as u, toWei as v, ClockError as w, identityErrorFromCapxul as x, wireChainId as y, copyInvocationObservation as z };
7002
7398
 
7003
- //# sourceMappingURL=create-capxul-client-DVzm78RU.mjs.map
7399
+ //# sourceMappingURL=create-capxul-client-BTnlLLag.mjs.map