@tinycloud/operations 0.2.1-beta.0 → 0.3.0-beta.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.
@@ -90,6 +90,12 @@ type OperationResult<O> = OperationOkResult<O> | OperationAuthorityRequiredResul
90
90
  interface InvocationTarget {
91
91
  readonly profile?: string;
92
92
  readonly host?: string;
93
+ /**
94
+ * Absolute home directory used for this invocation's TinyCloud profile
95
+ * state. Hosted projections set this per authenticated principal so
96
+ * concurrent tenants never share process-global TC_HOME state.
97
+ */
98
+ readonly stateRoot?: string;
93
99
  readonly allowOwnerProfile?: boolean;
94
100
  readonly privateKey?: string;
95
101
  }
@@ -90,6 +90,12 @@ type OperationResult<O> = OperationOkResult<O> | OperationAuthorityRequiredResul
90
90
  interface InvocationTarget {
91
91
  readonly profile?: string;
92
92
  readonly host?: string;
93
+ /**
94
+ * Absolute home directory used for this invocation's TinyCloud profile
95
+ * state. Hosted projections set this per authenticated principal so
96
+ * concurrent tenants never share process-global TC_HOME state.
97
+ */
98
+ readonly stateRoot?: string;
93
99
  readonly allowOwnerProfile?: boolean;
94
100
  readonly privateKey?: string;
95
101
  }
package/dist/index.cjs CHANGED
@@ -6754,9 +6754,16 @@ var init_authority = __esm({
6754
6754
 
6755
6755
  // src/state.ts
6756
6756
  function tinycloudHomePath() {
6757
- const home = process.env.TC_HOME ?? process.env.HOME ?? process.env.USERPROFILE ?? (0, import_node_os.homedir)();
6757
+ const home = invocationStateRoot.getStore() ?? process.env.TC_HOME ?? process.env.HOME ?? process.env.USERPROFILE ?? (0, import_node_os.homedir)();
6758
6758
  return (0, import_node_path.join)(home, ".tinycloud");
6759
6759
  }
6760
+ function withTinyCloudStateRoot(stateRoot, action) {
6761
+ if (stateRoot === void 0) return action();
6762
+ if (!(0, import_node_path.isAbsolute)(stateRoot) || stateRoot.includes("\0")) {
6763
+ throw new TypeError("The TinyCloud state root must be an absolute path.");
6764
+ }
6765
+ return invocationStateRoot.run((0, import_node_path.resolve)(stateRoot), action);
6766
+ }
6760
6767
  function tinycloudConfigPath() {
6761
6768
  return (0, import_node_path.join)(tinycloudHomePath(), "config.json");
6762
6769
  }
@@ -7008,11 +7015,12 @@ function isErrno(error, code) {
7008
7015
  function sleep(milliseconds) {
7009
7016
  return new Promise((resolve2) => setTimeout(resolve2, milliseconds));
7010
7017
  }
7011
- var import_node_crypto, import_promises, import_node_os, import_node_path, DEFAULT_PROFILE, DEFAULT_LOCK_TIMEOUT_MS, DEFAULT_LOCK_RETRY_MS, DEFAULT_STALE_LOCK_MS, TEST_LOCK_CONTENTION_SIGNAL_PATH, TEST_LOCK_RECOVERY_BARRIER_DIR, ProfileLockTimeoutError;
7018
+ var import_node_crypto, import_node_async_hooks, import_promises, import_node_os, import_node_path, DEFAULT_PROFILE, DEFAULT_LOCK_TIMEOUT_MS, DEFAULT_LOCK_RETRY_MS, DEFAULT_STALE_LOCK_MS, TEST_LOCK_CONTENTION_SIGNAL_PATH, TEST_LOCK_RECOVERY_BARRIER_DIR, invocationStateRoot, ProfileLockTimeoutError;
7012
7019
  var init_state = __esm({
7013
7020
  "src/state.ts"() {
7014
7021
  "use strict";
7015
7022
  import_node_crypto = require("crypto");
7023
+ import_node_async_hooks = require("async_hooks");
7016
7024
  import_promises = require("fs/promises");
7017
7025
  import_node_os = require("os");
7018
7026
  import_node_path = require("path");
@@ -7022,6 +7030,7 @@ var init_state = __esm({
7022
7030
  DEFAULT_STALE_LOCK_MS = 3e4;
7023
7031
  TEST_LOCK_CONTENTION_SIGNAL_PATH = "TC_TEST_PROFILE_LOCK_CONTENTION_SIGNAL_PATH";
7024
7032
  TEST_LOCK_RECOVERY_BARRIER_DIR = "TC_TEST_PROFILE_LOCK_RECOVERY_BARRIER_DIR";
7033
+ invocationStateRoot = new import_node_async_hooks.AsyncLocalStorage();
7025
7034
  ProfileLockTimeoutError = class extends Error {
7026
7035
  constructor(profile, timeoutMs) {
7027
7036
  super(`Timed out waiting for the profile lock for "${profile}" after ${timeoutMs}ms.`);
@@ -9908,6 +9917,7 @@ module.exports = __toCommonJS(index_exports);
9908
9917
 
9909
9918
  // src/invoke.ts
9910
9919
  var import_node_crypto3 = require("crypto");
9920
+ var import_node_path2 = require("path");
9911
9921
  var import_node_sdk2 = require("@tinycloud/node-sdk");
9912
9922
 
9913
9923
  // ../sdk-core/src/policy/errors.ts
@@ -10247,16 +10257,27 @@ function isRecord(value) {
10247
10257
  }
10248
10258
 
10249
10259
  // src/invoke.ts
10260
+ init_state();
10250
10261
  init_secrets();
10251
10262
  async function invokeOperation(operationId, operationVersion, invocationTarget, unknownInput) {
10252
- const prepared = await prepareInvocation(
10253
- operationId,
10254
- operationVersion,
10255
- invocationTarget,
10256
- unknownInput
10257
- );
10258
- if (isPreparedError(prepared)) return prepared.result;
10259
- return executePreparedInvocation(prepared);
10263
+ const target = normalizeInvocationTarget(invocationTarget);
10264
+ if (target === void 0) {
10265
+ return errorResult(
10266
+ { operationId, operationVersion },
10267
+ unresolvedContextSummary(),
10268
+ operationError("INPUT_INVALID", "The invocation target is invalid.")
10269
+ );
10270
+ }
10271
+ return withTinyCloudStateRoot(target.stateRoot, async () => {
10272
+ const prepared = await prepareInvocation(
10273
+ operationId,
10274
+ operationVersion,
10275
+ target,
10276
+ unknownInput
10277
+ );
10278
+ if (isPreparedError(prepared)) return prepared.result;
10279
+ return executePreparedInvocation(prepared);
10280
+ });
10260
10281
  }
10261
10282
  function isPreparedError(prepared) {
10262
10283
  return "status" in prepared;
@@ -10599,14 +10620,16 @@ function normalizeInvocationTarget(target) {
10599
10620
  const candidate = target;
10600
10621
  const profile = candidate.profile;
10601
10622
  const host = candidate.host;
10623
+ const stateRoot = candidate.stateRoot;
10602
10624
  const allowOwnerProfile = candidate.allowOwnerProfile;
10603
10625
  const privateKey = candidate.privateKey;
10604
- if (profile !== void 0 && typeof profile !== "string" || host !== void 0 && typeof host !== "string" || allowOwnerProfile !== void 0 && typeof allowOwnerProfile !== "boolean" || privateKey !== void 0 && typeof privateKey !== "string") {
10626
+ if (profile !== void 0 && typeof profile !== "string" || host !== void 0 && typeof host !== "string" || stateRoot !== void 0 && (typeof stateRoot !== "string" || !(0, import_node_path2.isAbsolute)(stateRoot)) || allowOwnerProfile !== void 0 && typeof allowOwnerProfile !== "boolean" || privateKey !== void 0 && typeof privateKey !== "string") {
10605
10627
  return void 0;
10606
10628
  }
10607
10629
  return {
10608
10630
  ...profile === void 0 ? {} : { profile },
10609
10631
  ...host === void 0 ? {} : { host },
10632
+ ...stateRoot === void 0 ? {} : { stateRoot },
10610
10633
  ...allowOwnerProfile === true ? { allowOwnerProfile: true } : {},
10611
10634
  ...privateKey === void 0 ? {} : { privateKey }
10612
10635
  };