@wtfalch/files 0.9.1 → 0.10.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.
package/README.md CHANGED
@@ -6,7 +6,7 @@ package dependencies.
6
6
 
7
7
  ## Status
8
8
 
9
- Published: yes, v0.9.1, 2026-09-28.
9
+ Published: yes, v0.10.0, 2026-10-03.
10
10
 
11
11
  ```sh
12
12
  npm install @wtfalch/files
@@ -50,7 +50,8 @@ exact file version. A mismatch against the file's current version throws
50
50
  `ApiError` with `status: 409` rather than serving the current version's
51
51
  bytes -- the backend keeps only the current version's object, so there is no
52
52
  older version to fall back to. Every `StoredFile` also carries `sha256`
53
- (`null` unless the storage backend exposed byte access at confirm time) and
53
+ (`null` unless the storage backend exposed byte access at confirm time),
54
+ `createdAt` (ISO 8601 creation time of the file row) and
54
55
  `principalRef`, the principal whose upload last confirmed it at its current
55
56
  version (`null` if that upload is no longer on record, or the file was never
56
57
  confirmed).
package/dist/client.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { type ClientOptions, type ManagementClientOptions } from './transport.js';
2
- import type { AddMemberInput, AuditPage, AuditQuery, BudgetSummary, CopyFileInput, CreatedShare, FilesPage, FilesRate, Group, GroupMember, Member, MoveFileInput, OrganisationShare, OrganisationSummary, ProvisionOrganisationInput, ProvisionOrganisationResult, QuotaSummary, SetFileAudienceInput, SetRateInput, SetWebhookResult, Share, SharedFilesPage, StoredFile, UpdateOrganisationCeilingInput, UploadInput, UploadReservation, UploadedPart, UsageDay, VariantFormat, VariantWidth, WebhookConfig } from './types.js';
2
+ import type { AddMemberInput, AuditPage, AuditQuery, BudgetSummary, CopyFileInput, CreatedShare, FilesPage, FilesRate, Group, GroupMember, IssueKeyInput, IssueOrganisationKeyInput, IssuedOrganisationKey, Member, MoveFileInput, OrganisationShare, OrganisationSummary, ProvisionOrganisationInput, ProvisionOrganisationResult, QuotaSummary, RevokedOrganisationKey, SetFileAudienceInput, SetRateInput, SetWebhookResult, Share, SharedFilesPage, StoredFile, UpdateOrganisationCeilingInput, UploadInput, UploadReservation, UploadedPart, UsageDay, VariantFormat, VariantWidth, WebhookConfig } from './types.js';
3
3
  export { ApiError, type ClientOptions, type ManagementClientOptions } from './transport.js';
4
4
  export declare function createFilesClient(options: ClientOptions): {
5
5
  createCollection: (name: string) => Promise<{
@@ -122,6 +122,14 @@ export declare function createFilesManagementClient(options: ManagementClientOpt
122
122
  organisations: OrganisationSummary[];
123
123
  }>;
124
124
  provisionOrganisation: (input: ProvisionOrganisationInput) => Promise<ProvisionOrganisationResult>;
125
+ issueOrganisationKey: (organisationId: string, input: IssueOrganisationKeyInput) => Promise<IssuedOrganisationKey>;
126
+ revokeOrganisationKey: (organisationId: string, keyId: string) => Promise<RevokedOrganisationKey>;
127
+ issueKeyBelow: (organisationId: string, minterId: string, input: IssueKeyInput) => Promise<IssuedOrganisationKey>;
128
+ ensureCollection: (organisationId: string, name: string) => Promise<{
129
+ created: boolean;
130
+ id: string;
131
+ name: string;
132
+ }>;
125
133
  updateOrganisationCeiling: (organisationId: string, input: UpdateOrganisationCeilingInput) => Promise<OrganisationSummary>;
126
134
  deleteOrganisation: (organisationId: string) => Promise<OrganisationSummary>;
127
135
  restoreOrganisation: (organisationId: string) => Promise<OrganisationSummary>;
@@ -139,6 +147,8 @@ export declare function createFilesManagementClient(options: ManagementClientOpt
139
147
  }>;
140
148
  getWebhook: () => Promise<WebhookConfig>;
141
149
  setWebhook: (url: string | null) => Promise<SetWebhookResult>;
150
+ issueKey: (input: IssueKeyInput) => Promise<IssuedOrganisationKey>;
151
+ revokeKey: (keyId: string) => Promise<RevokedOrganisationKey>;
142
152
  listMembers: () => Promise<{
143
153
  members: Member[];
144
154
  }>;
package/dist/client.js CHANGED
@@ -139,6 +139,23 @@ export function createFilesManagementClient(options) {
139
139
  const { data, status } = await callWithStatus('POST', '/v1/manage/organisations', input);
140
140
  return { ...data, created: status === 201 };
141
141
  },
142
+ // Mints an organisation's root key; the secret comes back once. Throws
143
+ // `ApiError` 409 for an organisation with no owner, 422 when the issuer
144
+ // is not a member or lacks a requested permission.
145
+ issueOrganisationKey: (organisationId, input) => call('POST', `/v1/manage/organisations/${encodeURIComponent(organisationId)}/keys`, input),
146
+ // Revokes the key and every key it minted; a second call is a no-op.
147
+ revokeOrganisationKey: (organisationId, keyId) => call('DELETE', `/v1/manage/organisations/${encodeURIComponent(organisationId)}/keys/${encodeURIComponent(keyId)}`),
148
+ // Mints a key below `minterId`, a live key of the organisation, without the
149
+ // minter's secret. Same input, result and errors as `issueKey`, plus 404
150
+ // when `minterId` is not a live key of the organisation. Revoking the
151
+ // minter (`revokeOrganisationKey`) revokes the new key too.
152
+ issueKeyBelow: (organisationId, minterId, input) => call('POST', `/v1/manage/organisations/${encodeURIComponent(organisationId)}/keys/${encodeURIComponent(minterId)}/keys`, input),
153
+ // Returns the organisation's collection of that name, creating it when
154
+ // missing; `created` is true only on the call that made it.
155
+ ensureCollection: async (organisationId, name) => {
156
+ const { data, status } = await callWithStatus('POST', `/v1/manage/organisations/${encodeURIComponent(organisationId)}/collections`, { name });
157
+ return { ...data, created: status === 201 };
158
+ },
142
159
  updateOrganisationCeiling: (organisationId, input) => call('PATCH', `/v1/manage/organisations/${encodeURIComponent(organisationId)}`, input),
143
160
  // Delete an organisation with a 7-day grace period (task O, issue #67).
144
161
  // Idempotent: deleting an already-deleted organisation succeeds again
@@ -170,6 +187,14 @@ export function createFilesManagementClient(options) {
170
187
  // Outbound event delivery (task gap-36). setWebhook(null) disables it.
171
188
  getWebhook: () => call('GET', '/v1/manage/webhook'),
172
189
  setWebhook: (url) => call('PUT', '/v1/manage/webhook', { url }),
190
+ // Mints a key below the calling key (which needs `credentials:create`);
191
+ // the secret comes back once. Throws `ApiError` 422 for a permission the
192
+ // caller lacks, `credentials:create`/`credentials:revoke`, or an expiry
193
+ // or limit past the caller's own; 404 when the caller may not mint.
194
+ issueKey: (input) => call('POST', '/v1/manage/keys', input),
195
+ // Revokes a key of the caller's own subtree (the caller included) and
196
+ // every key it minted; 404 for any other key. A second call is a no-op.
197
+ revokeKey: (keyId) => call('DELETE', `/v1/manage/keys/${encodeURIComponent(keyId)}`),
173
198
  // Membership (task M): the product that owns its users writes
174
199
  // memberships in its own files organisation. `files.members:read`/
175
200
  // `files.members:update`, held by the calling key for its own
package/dist/types.d.ts CHANGED
@@ -92,6 +92,8 @@ export interface StoredFile {
92
92
  bytes: string;
93
93
  version: number;
94
94
  collectionId: string;
95
+ /** When the file row was created (ISO 8601). */
96
+ createdAt: string;
95
97
  expiresAt: string | null;
96
98
  /** `"md5:<hex>"` of the confirmed bytes, or `null` for a file confirmed
97
99
  * before this field existed. Derived from the storage backend's own etag
@@ -269,6 +271,46 @@ export interface ProvisionOrganisationResult extends OrganisationSummary {
269
271
  secret: string;
270
272
  };
271
273
  }
274
+ export interface IssueOrganisationKeyInput {
275
+ name: string;
276
+ kind: 'api_key' | 'agent' | 'service';
277
+ /** Subject id of a live human member of the organisation; the key holds
278
+ * only permissions that person holds. */
279
+ issuer: string;
280
+ permissions: string[];
281
+ /** 1 to 365. */
282
+ expiresDays: number;
283
+ app?: string;
284
+ }
285
+ export interface IssuedOrganisationKey {
286
+ id: string;
287
+ /** Shown once; files keeps no copy. */
288
+ secret: string;
289
+ kind: 'api_key' | 'agent' | 'service';
290
+ expiresAt: string;
291
+ permissions: string[];
292
+ }
293
+ export interface RevokedOrganisationKey {
294
+ id: string;
295
+ /** The key and every key it minted; empty when it was already revoked. */
296
+ revoked: string[];
297
+ }
298
+ export interface IssueKeyInput {
299
+ name: string;
300
+ kind: 'api_key' | 'agent' | 'service';
301
+ /** Each must be held by the calling key; never `credentials:create` or `credentials:revoke`. */
302
+ permissions: string[];
303
+ /** 1 to 365, and not past the calling key's own expiry. */
304
+ expiresDays: number;
305
+ /** Defaults to the calling key's own app. */
306
+ app?: string;
307
+ /** Byte quota for the new key, at most the organisation's limit; `null` is uncapped. */
308
+ limitBytes?: number | null;
309
+ /** Spend limit in micros for the new key, at most the organisation's limit; `null` is uncapped. */
310
+ limitMicros?: number | null;
311
+ }
312
+ export type IssuedKey = IssuedOrganisationKey;
313
+ export type RevokedKey = RevokedOrganisationKey;
272
314
  export interface UpdateOrganisationCeilingInput {
273
315
  ceilingBytes?: string | null;
274
316
  ceilingMicros?: string | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wtfalch/files",
3
- "version": "0.9.1",
3
+ "version": "0.10.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Public client SDK and API types for the Files service.",