@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.js CHANGED
@@ -983,6 +983,31 @@ var KVService = class extends BaseService {
983
983
  }
984
984
  return void 0;
985
985
  }
986
+ /**
987
+ * Classify a KV 404 by reading the response body once.
988
+ *
989
+ * The server returns 404 both for a genuinely missing key AND for an
990
+ * un-hosted space (body "Space not found"). Previously get/head/delete
991
+ * collapsed every 404 to KV_NOT_FOUND before reading the body, so an
992
+ * un-hosted-space read was indistinguishable from a missing key. We now
993
+ * preserve status + the "Space not found" body for the un-hosted case (so the
994
+ * CLI/SDK can normalize it to SPACE_NOT_HOSTED, matching put/list/sql), and
995
+ * fall through to KV_NOT_FOUND for a real missing key.
996
+ */
997
+ async classifyNotFound(response, key) {
998
+ const errorText = await response.text();
999
+ if (/space not found/i.test(errorText)) {
1000
+ return err(
1001
+ serviceError(
1002
+ ErrorCodes.KV_NOT_FOUND,
1003
+ `KV ${response.status} - ${errorText}`,
1004
+ "kv",
1005
+ { meta: { status: response.status, statusText: response.statusText } }
1006
+ )
1007
+ );
1008
+ }
1009
+ return err(serviceError(ErrorCodes.KV_NOT_FOUND, `Key not found: ${key}`, "kv"));
1010
+ }
986
1011
  /**
987
1012
  * Get the full path with optional prefix.
988
1013
  *
@@ -1229,13 +1254,7 @@ var KVService = class extends BaseService {
1229
1254
  }));
1230
1255
  }
1231
1256
  if (response.status === 404) {
1232
- return err(
1233
- serviceError(
1234
- ErrorCodes.KV_NOT_FOUND,
1235
- `Key not found: ${key}`,
1236
- "kv"
1237
- )
1238
- );
1257
+ return this.classifyNotFound(response, key);
1239
1258
  }
1240
1259
  const errorText = await response.text();
1241
1260
  return err(
@@ -1496,13 +1515,7 @@ var KVService = class extends BaseService {
1496
1515
  }));
1497
1516
  }
1498
1517
  if (response.status === 404) {
1499
- return err(
1500
- serviceError(
1501
- ErrorCodes.KV_NOT_FOUND,
1502
- `Key not found: ${key}`,
1503
- "kv"
1504
- )
1505
- );
1518
+ return this.classifyNotFound(response, key);
1506
1519
  }
1507
1520
  const errorText = await response.text();
1508
1521
  return err(
@@ -1547,13 +1560,7 @@ var KVService = class extends BaseService {
1547
1560
  }));
1548
1561
  }
1549
1562
  if (response.status === 404) {
1550
- return err(
1551
- serviceError(
1552
- ErrorCodes.KV_NOT_FOUND,
1553
- `Key not found: ${key}`,
1554
- "kv"
1555
- )
1556
- );
1563
+ return this.classifyNotFound(response, key);
1557
1564
  }
1558
1565
  const errorText = await response.text();
1559
1566
  return err(
@@ -1707,6 +1714,7 @@ var DatabaseHandle = class {
1707
1714
  var SQLAction = {
1708
1715
  READ: "tinycloud.sql/read",
1709
1716
  WRITE: "tinycloud.sql/write",
1717
+ DDL: "tinycloud.sql/ddl",
1710
1718
  ADMIN: "tinycloud.sql/admin",
1711
1719
  SELECT: "tinycloud.sql/select",
1712
1720
  INSERT: "tinycloud.sql/insert",
@@ -1718,6 +1726,7 @@ var SQLAction = {
1718
1726
  };
1719
1727
 
1720
1728
  // src/sql/SQLService.ts
1729
+ var DDL_TOKENS = /* @__PURE__ */ new Set(["alter", "create", "drop"]);
1721
1730
  var SQLService = class extends BaseService {
1722
1731
  constructor(config = {}) {
1723
1732
  super();
@@ -1793,9 +1802,15 @@ var SQLService = class extends BaseService {
1793
1802
  if (options?.schema) {
1794
1803
  body.schema = options.schema;
1795
1804
  }
1805
+ const actions = [
1806
+ this.actionForSql(sql, SQLAction.WRITE),
1807
+ ...(options?.schema ?? []).map(
1808
+ (statement) => this.actionForSql(statement, SQLAction.DDL)
1809
+ )
1810
+ ];
1796
1811
  const response = await this.invokeSQL(
1797
1812
  dbName,
1798
- this.actionForSql(sql, SQLAction.WRITE),
1813
+ this.dedupeActions(actions),
1799
1814
  body,
1800
1815
  options?.signal
1801
1816
  );
@@ -1817,7 +1832,7 @@ var SQLService = class extends BaseService {
1817
1832
  try {
1818
1833
  const response = await this.invokeSQL(
1819
1834
  dbName,
1820
- this.actionForSqlBatch(statements),
1835
+ this.actionsForSqlBatch(statements),
1821
1836
  { action: "batch", statements },
1822
1837
  options?.signal
1823
1838
  );
@@ -1881,9 +1896,10 @@ var SQLService = class extends BaseService {
1881
1896
  });
1882
1897
  }
1883
1898
  // === Private helpers ===
1884
- async invokeSQL(dbName, action, body, signal) {
1899
+ async invokeSQL(dbName, actions, body, signal) {
1885
1900
  const session = this.context.session;
1886
- const headers = this.context.invoke(session, "sql", dbName, action);
1901
+ const actionList = Array.isArray(actions) ? actions : [actions];
1902
+ const headers = actionList.length === 1 ? this.context.invoke(session, "sql", dbName, actionList[0]) : this.invokeSQLAny(session, dbName, actionList);
1887
1903
  return this.context.fetch(`${this.host}/invoke`, {
1888
1904
  method: "POST",
1889
1905
  headers: {
@@ -1895,12 +1911,34 @@ var SQLService = class extends BaseService {
1895
1911
  });
1896
1912
  }
1897
1913
  actionForSql(sql, fallback) {
1898
- return firstSqlToken(sql) === "pragma" ? SQLAction.ADMIN : fallback;
1914
+ const token = firstSqlToken(sql);
1915
+ if (token === "pragma") return SQLAction.ADMIN;
1916
+ if (token !== void 0 && DDL_TOKENS.has(token)) return SQLAction.DDL;
1917
+ return fallback;
1918
+ }
1919
+ actionsForSqlBatch(statements) {
1920
+ return this.dedupeActions(
1921
+ statements.map((statement) => this.actionForSql(statement.sql, SQLAction.WRITE))
1922
+ );
1899
1923
  }
1900
- actionForSqlBatch(statements) {
1901
- return statements.some(
1902
- (statement) => this.actionForSql(statement.sql, SQLAction.WRITE) === SQLAction.ADMIN
1903
- ) ? SQLAction.ADMIN : SQLAction.WRITE;
1924
+ dedupeActions(actions) {
1925
+ return [...new Set(actions)];
1926
+ }
1927
+ invokeSQLAny(session, dbName, actions) {
1928
+ if (!this.context.invokeAny) {
1929
+ throw new Error(
1930
+ `SQL operation requires multiple permissions (${actions.join(", ")}) but this SDK runtime does not support multi-resource invocations`
1931
+ );
1932
+ }
1933
+ return this.context.invokeAny(
1934
+ session,
1935
+ actions.map((action) => ({
1936
+ spaceId: session.spaceId,
1937
+ service: "sql",
1938
+ path: dbName,
1939
+ action
1940
+ }))
1941
+ );
1904
1942
  }
1905
1943
  async handleErrorResponse(response, operation) {
1906
1944
  const errorText = await response.text();