@palettelab/cli 0.3.45 → 0.3.46

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/README.md CHANGED
@@ -328,6 +328,8 @@ async def sync_invoices(ctx: PluginContext = Depends(get_plugin_context)):
328
328
  return {"room": room, "folder": folder, "bytes": len(content or b"")}
329
329
  ```
330
330
 
331
+ Storage upload responses include both snake_case and camelCase aliases for object metadata (`object_path`/`objectPath`, `file_url`/`fileUrl`, `content_type`/`contentType`) so local simulator and hosted OS responses can be consumed with the same app code.
332
+
331
333
  App storage is different from Data Room uploads. Use `ctx.storage` or `palette.storage` for app-owned files written directly to the OS-configured storage backend, currently GCS in hosted environments. Use `ctx.data_rooms` / `palette.dataRooms` only when the file should be managed as a Data Room document.
332
334
 
333
335
  Python backend app-storage example:
@@ -83,6 +83,47 @@ def _content_type(filename: str | None, content_type: str | None) -> str:
83
83
  return browser_ct
84
84
 
85
85
 
86
+ def _storage_metadata(
87
+ *,
88
+ bucket: str,
89
+ object_path: str,
90
+ file_url: str,
91
+ content_type: str,
92
+ size: int | None,
93
+ ) -> dict[str, Any]:
94
+ return {
95
+ "bucket": bucket,
96
+ "object_path": object_path,
97
+ "objectPath": object_path,
98
+ "file_url": file_url,
99
+ "fileUrl": file_url,
100
+ "content_type": content_type,
101
+ "contentType": content_type,
102
+ "size": size,
103
+ }
104
+
105
+
106
+ def _resumable_metadata(
107
+ *,
108
+ bucket: str,
109
+ object_path: str,
110
+ file_url: str,
111
+ upload_url: str | None,
112
+ content_type: str,
113
+ size: int | None,
114
+ ) -> dict[str, Any]:
115
+ payload = _storage_metadata(
116
+ bucket=bucket,
117
+ object_path=object_path,
118
+ file_url=file_url,
119
+ content_type=content_type,
120
+ size=size,
121
+ )
122
+ payload["upload_url"] = upload_url
123
+ payload["uploadUrl"] = upload_url
124
+ return payload
125
+
126
+
86
127
  @dataclass
87
128
  class LocalStorageService:
88
129
  """Filesystem-backed ctx.storage implementation used by local SDK dev."""
@@ -122,13 +163,13 @@ class LocalStorageService:
122
163
  target = self._target(object_path)
123
164
  target.parent.mkdir(parents=True, exist_ok=True)
124
165
  target.write_bytes(content)
125
- return {
126
- "bucket": "local",
127
- "object_path": object_path,
128
- "file_url": target.as_uri(),
129
- "content_type": _content_type(filename, content_type),
130
- "size": len(content),
131
- }
166
+ return _storage_metadata(
167
+ bucket="local",
168
+ object_path=object_path,
169
+ file_url=target.as_uri(),
170
+ content_type=_content_type(filename, content_type),
171
+ size=len(content),
172
+ )
132
173
 
133
174
  async def upload_file_stream(
134
175
  self,
@@ -168,13 +209,13 @@ class LocalStorageService:
168
209
  chunk_count=chunk_count,
169
210
  state="complete",
170
211
  )
171
- return {
172
- "bucket": "local",
173
- "object_path": object_path,
174
- "file_url": target.as_uri(),
175
- "content_type": _content_type(filename, content_type),
176
- "size": 0,
177
- }
212
+ return _storage_metadata(
213
+ bucket="local",
214
+ object_path=object_path,
215
+ file_url=target.as_uri(),
216
+ content_type=_content_type(filename, content_type),
217
+ size=0,
218
+ )
178
219
  with target.open("wb") as out:
179
220
  chunk_index = 0
180
221
  while True:
@@ -194,13 +235,13 @@ class LocalStorageService:
194
235
  )
195
236
  if loaded != total:
196
237
  raise RuntimeError(f"storage upload incomplete: uploaded {loaded} of {total} bytes")
197
- return {
198
- "bucket": "local",
199
- "object_path": object_path,
200
- "file_url": target.as_uri(),
201
- "content_type": _content_type(filename, content_type),
202
- "size": loaded,
203
- }
238
+ return _storage_metadata(
239
+ bucket="local",
240
+ object_path=object_path,
241
+ file_url=target.as_uri(),
242
+ content_type=_content_type(filename, content_type),
243
+ size=loaded,
244
+ )
204
245
 
205
246
  async def upload_file_path(
206
247
  self,
@@ -234,11 +275,11 @@ class LocalStorageService:
234
275
  key: str | None = None,
235
276
  ) -> dict[str, Any]:
236
277
  object_path = self.object_path(filename, key=key)
237
- return {
238
- "bucket": "local",
239
- "object_path": object_path,
240
- "file_url": self._target(object_path).as_uri(),
241
- "upload_url": None,
242
- "content_type": _content_type(filename, content_type),
243
- "size": size,
244
- }
278
+ return _resumable_metadata(
279
+ bucket="local",
280
+ object_path=object_path,
281
+ file_url=self._target(object_path).as_uri(),
282
+ upload_url=None,
283
+ content_type=_content_type(filename, content_type),
284
+ size=size,
285
+ )
@@ -1044,3 +1044,5 @@ async def create_note(
1044
1044
 
1045
1045
  This route stores app-owned data in the app database and writes a related file
1046
1046
  into Palette Data Rooms from the Python backend.
1047
+
1048
+ Backend storage responses include both snake_case and camelCase aliases for object metadata (`object_path`/`objectPath`, `file_url`/`fileUrl`, `content_type`/`contentType`). The concrete values differ by environment, but the key contract is stable across local dev and hosted OS.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@palettelab/cli",
3
- "version": "0.3.45",
3
+ "version": "0.3.46",
4
4
  "description": "Developer CLI for building Palette platform plugins — no platform source access required.",
5
5
  "bin": {
6
6
  "pltt": "bin/pltt.js"