@tinycloud/sdk-services 2.4.0-beta.1 → 2.4.0-beta.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/index.cjs CHANGED
@@ -1113,6 +1113,31 @@ var KVService = class extends BaseService {
1113
1113
  }
1114
1114
  return void 0;
1115
1115
  }
1116
+ /**
1117
+ * Classify a KV 404 by reading the response body once.
1118
+ *
1119
+ * The server returns 404 both for a genuinely missing key AND for an
1120
+ * un-hosted space (body "Space not found"). Previously get/head/delete
1121
+ * collapsed every 404 to KV_NOT_FOUND before reading the body, so an
1122
+ * un-hosted-space read was indistinguishable from a missing key. We now
1123
+ * preserve status + the "Space not found" body for the un-hosted case (so the
1124
+ * CLI/SDK can normalize it to SPACE_NOT_HOSTED, matching put/list/sql), and
1125
+ * fall through to KV_NOT_FOUND for a real missing key.
1126
+ */
1127
+ async classifyNotFound(response, key) {
1128
+ const errorText = await response.text();
1129
+ if (/space not found/i.test(errorText)) {
1130
+ return err(
1131
+ serviceError(
1132
+ ErrorCodes.KV_NOT_FOUND,
1133
+ `KV ${response.status} - ${errorText}`,
1134
+ "kv",
1135
+ { meta: { status: response.status, statusText: response.statusText } }
1136
+ )
1137
+ );
1138
+ }
1139
+ return err(serviceError(ErrorCodes.KV_NOT_FOUND, `Key not found: ${key}`, "kv"));
1140
+ }
1116
1141
  /**
1117
1142
  * Get the full path with optional prefix.
1118
1143
  *
@@ -1359,13 +1384,7 @@ var KVService = class extends BaseService {
1359
1384
  }));
1360
1385
  }
1361
1386
  if (response.status === 404) {
1362
- return err(
1363
- serviceError(
1364
- ErrorCodes.KV_NOT_FOUND,
1365
- `Key not found: ${key}`,
1366
- "kv"
1367
- )
1368
- );
1387
+ return this.classifyNotFound(response, key);
1369
1388
  }
1370
1389
  const errorText = await response.text();
1371
1390
  return err(
@@ -1626,13 +1645,7 @@ var KVService = class extends BaseService {
1626
1645
  }));
1627
1646
  }
1628
1647
  if (response.status === 404) {
1629
- return err(
1630
- serviceError(
1631
- ErrorCodes.KV_NOT_FOUND,
1632
- `Key not found: ${key}`,
1633
- "kv"
1634
- )
1635
- );
1648
+ return this.classifyNotFound(response, key);
1636
1649
  }
1637
1650
  const errorText = await response.text();
1638
1651
  return err(
@@ -1677,13 +1690,7 @@ var KVService = class extends BaseService {
1677
1690
  }));
1678
1691
  }
1679
1692
  if (response.status === 404) {
1680
- return err(
1681
- serviceError(
1682
- ErrorCodes.KV_NOT_FOUND,
1683
- `Key not found: ${key}`,
1684
- "kv"
1685
- )
1686
- );
1693
+ return this.classifyNotFound(response, key);
1687
1694
  }
1688
1695
  const errorText = await response.text();
1689
1696
  return err(
@@ -1837,6 +1844,7 @@ var DatabaseHandle = class {
1837
1844
  var SQLAction = {
1838
1845
  READ: "tinycloud.sql/read",
1839
1846
  WRITE: "tinycloud.sql/write",
1847
+ DDL: "tinycloud.sql/ddl",
1840
1848
  ADMIN: "tinycloud.sql/admin",
1841
1849
  SELECT: "tinycloud.sql/select",
1842
1850
  INSERT: "tinycloud.sql/insert",
@@ -1848,6 +1856,7 @@ var SQLAction = {
1848
1856
  };
1849
1857
 
1850
1858
  // src/sql/SQLService.ts
1859
+ var DDL_TOKENS = /* @__PURE__ */ new Set(["alter", "create", "drop"]);
1851
1860
  var SQLService = class extends BaseService {
1852
1861
  constructor(config = {}) {
1853
1862
  super();
@@ -1923,9 +1932,15 @@ var SQLService = class extends BaseService {
1923
1932
  if (options?.schema) {
1924
1933
  body.schema = options.schema;
1925
1934
  }
1935
+ const actions = [
1936
+ this.actionForSql(sql, SQLAction.WRITE),
1937
+ ...(options?.schema ?? []).map(
1938
+ (statement) => this.actionForSql(statement, SQLAction.DDL)
1939
+ )
1940
+ ];
1926
1941
  const response = await this.invokeSQL(
1927
1942
  dbName,
1928
- this.actionForSql(sql, SQLAction.WRITE),
1943
+ this.dedupeActions(actions),
1929
1944
  body,
1930
1945
  options?.signal
1931
1946
  );
@@ -1947,7 +1962,7 @@ var SQLService = class extends BaseService {
1947
1962
  try {
1948
1963
  const response = await this.invokeSQL(
1949
1964
  dbName,
1950
- this.actionForSqlBatch(statements),
1965
+ this.actionsForSqlBatch(statements),
1951
1966
  { action: "batch", statements },
1952
1967
  options?.signal
1953
1968
  );
@@ -2011,9 +2026,10 @@ var SQLService = class extends BaseService {
2011
2026
  });
2012
2027
  }
2013
2028
  // === Private helpers ===
2014
- async invokeSQL(dbName, action, body, signal) {
2029
+ async invokeSQL(dbName, actions, body, signal) {
2015
2030
  const session = this.context.session;
2016
- const headers = this.context.invoke(session, "sql", dbName, action);
2031
+ const actionList = Array.isArray(actions) ? actions : [actions];
2032
+ const headers = actionList.length === 1 ? this.context.invoke(session, "sql", dbName, actionList[0]) : this.invokeSQLAny(session, dbName, actionList);
2017
2033
  return this.context.fetch(`${this.host}/invoke`, {
2018
2034
  method: "POST",
2019
2035
  headers: {
@@ -2025,12 +2041,34 @@ var SQLService = class extends BaseService {
2025
2041
  });
2026
2042
  }
2027
2043
  actionForSql(sql, fallback) {
2028
- return firstSqlToken(sql) === "pragma" ? SQLAction.ADMIN : fallback;
2044
+ const token = firstSqlToken(sql);
2045
+ if (token === "pragma") return SQLAction.ADMIN;
2046
+ if (token !== void 0 && DDL_TOKENS.has(token)) return SQLAction.DDL;
2047
+ return fallback;
2048
+ }
2049
+ actionsForSqlBatch(statements) {
2050
+ return this.dedupeActions(
2051
+ statements.map((statement) => this.actionForSql(statement.sql, SQLAction.WRITE))
2052
+ );
2029
2053
  }
2030
- actionForSqlBatch(statements) {
2031
- return statements.some(
2032
- (statement) => this.actionForSql(statement.sql, SQLAction.WRITE) === SQLAction.ADMIN
2033
- ) ? SQLAction.ADMIN : SQLAction.WRITE;
2054
+ dedupeActions(actions) {
2055
+ return [...new Set(actions)];
2056
+ }
2057
+ invokeSQLAny(session, dbName, actions) {
2058
+ if (!this.context.invokeAny) {
2059
+ throw new Error(
2060
+ `SQL operation requires multiple permissions (${actions.join(", ")}) but this SDK runtime does not support multi-resource invocations`
2061
+ );
2062
+ }
2063
+ return this.context.invokeAny(
2064
+ session,
2065
+ actions.map((action) => ({
2066
+ spaceId: session.spaceId,
2067
+ service: "sql",
2068
+ path: dbName,
2069
+ action
2070
+ }))
2071
+ );
2034
2072
  }
2035
2073
  async handleErrorResponse(response, operation) {
2036
2074
  const errorText = await response.text();