@gamerdexapp/gamerdex-sync 0.2.0 → 0.3.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/CHANGELOG.md CHANGED
@@ -7,6 +7,26 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.3.0] - 2026-10-18
11
+
12
+ ### Added
13
+
14
+ - `DropboxProvider` keeps the files in the app folder of the Dropbox of the player. It writes with
15
+ the revision that was read and fails with a version conflict when the file changed since.
16
+ - `OneDriveProvider` keeps the files in the app folder of the OneDrive of a personal Microsoft
17
+ account. It writes with the `eTag` that was read and creates a new file only when none exists.
18
+ - `dropboxAuthorizeUrl()` with `DROPBOX_SCOPES`, and `microsoftAuthorizeUrl()` with
19
+ `ONEDRIVE_FILE_SCOPE` and `ONEDRIVE_SCOPES`, for the two sign in pages.
20
+ - `ProviderError.detail` also reads the summary of a failed Dropbox request.
21
+
22
+ ## [0.2.1] - 2026-10-18
23
+
24
+ ### Fixed
25
+
26
+ - `GoogleDriveProvider` no longer fails when the Drive search does not list a file or folder that it
27
+ has just created. Drive can lag behind a create, so the provider now counts the new item with the
28
+ creation time that Drive returns.
29
+
10
30
  ## [0.2.0] - 2026-10-17
11
31
 
12
32
  ### Added
package/README.md CHANGED
@@ -28,7 +28,7 @@ const result = await sync({
28
28
  appId: "my-app",
29
29
  deviceId: "device-1",
30
30
  deviceName: "Laptop",
31
- provider: new MemoryProvider(), // GitHubProvider in an app
31
+ provider: new MemoryProvider(), // GitHubProvider, GoogleDriveProvider, DropboxProvider or OneDriveProvider in an app
32
32
  schema,
33
33
  local: { chief: { level: 5 } },
34
34
  state: null, // the state that the last sync returned, or null on the first sync
package/dist/index.d.mts CHANGED
@@ -429,6 +429,7 @@ declare class GoogleDriveProvider implements SyncProvider {
429
429
  private upload;
430
430
  private createFile;
431
431
  private deleteItem;
432
+ private itemOf;
432
433
  private stringField;
433
434
  private request;
434
435
  private failure;
@@ -442,4 +443,84 @@ declare const GOOGLE_DRIVE_SCOPE = "https://www.googleapis.com/auth/drive.file";
442
443
  */
443
444
  declare const googleAuthorizeUrl: (clientId: string, redirectUri: string, state: string) => string;
444
445
 
445
- export { type CallbackResult, type Conflict, type ConflictKind, DATA_FILE, type DeviceRecord, type FirstSyncChoice, GITHUB_APP, GOOGLE_DRIVE_SCOPE, type GitHubOptions, GitHubProvider, type GoogleDriveOptions, GoogleDriveProvider, type InstalledRepository, META_FILE, META_FORMAT, MemoryProvider, type MergeInput, type MergeResult, type ParsedMeta, type Path, type PathStep, ProviderError, type ProviderErrorKind, ROOT_FOLDER, RemoteDataError, type RemoteFile, type RepositoryCheck, type Schema, type Side, type StoredTokens, SyncBusyError, type SyncMeta, type SyncOptions, type SyncProvider, type SyncResult, type SyncState, type TokenResponse, type TokenSourceOptions, VersionConflictError, authorizeUrl, createTokenSource, dataPath, decodeBase64, emptyMeta, encodeBase64, equal, googleAuthorizeUrl, installUrl, isAppId, isObject, listInstalledRepositories, merge, metaPath, newState, parseMeta, parseTokenResponse, readCallback, resolveConflict, restoreCloudValues, sync, toStoredTokens, withChiefRemoved, withChiefRevision, withDevice, withSettingsRevision };
446
+ interface DropboxOptions {
447
+ /** Returns the token. It may refresh the token first, so it is called for every request. */
448
+ getToken: () => Promise<string>;
449
+ fetch?: typeof fetch;
450
+ }
451
+ /**
452
+ * Keeps the files of an app in the Dropbox of the player. The app should have the App folder
453
+ * access type with the `files.content.read` and `files.content.write` scopes, so it sees only its
454
+ * own folder under `Apps`.
455
+ *
456
+ * Dropbox writes with an expected revision. An upload in `update` mode with `strict_conflict`
457
+ * fails when the file changed since, and an upload in `add` mode fails when the file exists.
458
+ */
459
+ declare class DropboxProvider implements SyncProvider {
460
+ private readonly options;
461
+ private readonly send;
462
+ constructor(options: DropboxOptions);
463
+ read(path: string): Promise<RemoteFile | null>;
464
+ write(path: string, content: string, expectedVersion: string | null): Promise<{
465
+ version: string;
466
+ }>;
467
+ remove(path: string, expectedVersion: string): Promise<void>;
468
+ private request;
469
+ /**
470
+ * Reads a failed answer. Dropbox reports a failure that belongs to the call, such as a missing
471
+ * file or a conflict, with status 409. The caller decides what that means. Every other status
472
+ * is a failure of the request and is thrown here.
473
+ */
474
+ private failure;
475
+ private unexpected;
476
+ private invalid;
477
+ }
478
+
479
+ interface OneDriveOptions {
480
+ /** Returns the token. It may refresh the token first, so it is called for every request. */
481
+ getToken: () => Promise<string>;
482
+ fetch?: typeof fetch;
483
+ }
484
+ /**
485
+ * Keeps the files of an app in the OneDrive of the player, in the app folder under `Apps`. It
486
+ * needs only the `Files.ReadWrite.AppFolder` scope, so it sees no other file. The app folder
487
+ * exists for personal Microsoft accounts.
488
+ *
489
+ * A write sends the `eTag` that was read in an `If-Match` header, and a new file is created with
490
+ * `conflictBehavior=fail`. OneDrive answers 412 and 409 when the file changed or exists.
491
+ */
492
+ declare class OneDriveProvider implements SyncProvider {
493
+ private readonly options;
494
+ private readonly send;
495
+ constructor(options: OneDriveOptions);
496
+ read(path: string): Promise<RemoteFile | null>;
497
+ write(path: string, content: string, expectedVersion: string | null): Promise<{
498
+ version: string;
499
+ }>;
500
+ remove(path: string, expectedVersion: string): Promise<void>;
501
+ private itemUrl;
502
+ private ifMatch;
503
+ private info;
504
+ /** The download address is signed, so it must not carry the token. */
505
+ private fetchContent;
506
+ private request;
507
+ private assertOk;
508
+ private invalid;
509
+ }
510
+
511
+ /** The scopes that sync asks for. Together with the App folder access type, they reach one folder. */
512
+ declare const DROPBOX_SCOPES: readonly ["files.content.read", "files.content.write"];
513
+ /**
514
+ * The address of the Dropbox sign in page. `token_access_type=offline` makes Dropbox send a
515
+ * refresh token, which does not expire until the player removes the access.
516
+ */
517
+ declare const dropboxAuthorizeUrl: (clientId: string, redirectUri: string, state: string) => string;
518
+
519
+ /** The file scope that sync asks for. It reaches only the app folder of the player. */
520
+ declare const ONEDRIVE_FILE_SCOPE = "Files.ReadWrite.AppFolder";
521
+ /** The file scope, and `offline_access`, which makes Microsoft send a refresh token. */
522
+ declare const ONEDRIVE_SCOPES: readonly ["Files.ReadWrite.AppFolder", "offline_access"];
523
+ /** The address of the Microsoft sign in page for personal accounts. */
524
+ declare const microsoftAuthorizeUrl: (clientId: string, redirectUri: string, state: string) => string;
525
+
526
+ export { type CallbackResult, type Conflict, type ConflictKind, DATA_FILE, DROPBOX_SCOPES, type DeviceRecord, type DropboxOptions, DropboxProvider, type FirstSyncChoice, GITHUB_APP, GOOGLE_DRIVE_SCOPE, type GitHubOptions, GitHubProvider, type GoogleDriveOptions, GoogleDriveProvider, type InstalledRepository, META_FILE, META_FORMAT, MemoryProvider, type MergeInput, type MergeResult, ONEDRIVE_FILE_SCOPE, ONEDRIVE_SCOPES, type OneDriveOptions, OneDriveProvider, type ParsedMeta, type Path, type PathStep, ProviderError, type ProviderErrorKind, ROOT_FOLDER, RemoteDataError, type RemoteFile, type RepositoryCheck, type Schema, type Side, type StoredTokens, SyncBusyError, type SyncMeta, type SyncOptions, type SyncProvider, type SyncResult, type SyncState, type TokenResponse, type TokenSourceOptions, VersionConflictError, authorizeUrl, createTokenSource, dataPath, decodeBase64, dropboxAuthorizeUrl, emptyMeta, encodeBase64, equal, googleAuthorizeUrl, installUrl, isAppId, isObject, listInstalledRepositories, merge, metaPath, microsoftAuthorizeUrl, newState, parseMeta, parseTokenResponse, readCallback, resolveConflict, restoreCloudValues, sync, toStoredTokens, withChiefRemoved, withChiefRevision, withDevice, withSettingsRevision };
package/dist/index.d.ts CHANGED
@@ -429,6 +429,7 @@ declare class GoogleDriveProvider implements SyncProvider {
429
429
  private upload;
430
430
  private createFile;
431
431
  private deleteItem;
432
+ private itemOf;
432
433
  private stringField;
433
434
  private request;
434
435
  private failure;
@@ -442,4 +443,84 @@ declare const GOOGLE_DRIVE_SCOPE = "https://www.googleapis.com/auth/drive.file";
442
443
  */
443
444
  declare const googleAuthorizeUrl: (clientId: string, redirectUri: string, state: string) => string;
444
445
 
445
- export { type CallbackResult, type Conflict, type ConflictKind, DATA_FILE, type DeviceRecord, type FirstSyncChoice, GITHUB_APP, GOOGLE_DRIVE_SCOPE, type GitHubOptions, GitHubProvider, type GoogleDriveOptions, GoogleDriveProvider, type InstalledRepository, META_FILE, META_FORMAT, MemoryProvider, type MergeInput, type MergeResult, type ParsedMeta, type Path, type PathStep, ProviderError, type ProviderErrorKind, ROOT_FOLDER, RemoteDataError, type RemoteFile, type RepositoryCheck, type Schema, type Side, type StoredTokens, SyncBusyError, type SyncMeta, type SyncOptions, type SyncProvider, type SyncResult, type SyncState, type TokenResponse, type TokenSourceOptions, VersionConflictError, authorizeUrl, createTokenSource, dataPath, decodeBase64, emptyMeta, encodeBase64, equal, googleAuthorizeUrl, installUrl, isAppId, isObject, listInstalledRepositories, merge, metaPath, newState, parseMeta, parseTokenResponse, readCallback, resolveConflict, restoreCloudValues, sync, toStoredTokens, withChiefRemoved, withChiefRevision, withDevice, withSettingsRevision };
446
+ interface DropboxOptions {
447
+ /** Returns the token. It may refresh the token first, so it is called for every request. */
448
+ getToken: () => Promise<string>;
449
+ fetch?: typeof fetch;
450
+ }
451
+ /**
452
+ * Keeps the files of an app in the Dropbox of the player. The app should have the App folder
453
+ * access type with the `files.content.read` and `files.content.write` scopes, so it sees only its
454
+ * own folder under `Apps`.
455
+ *
456
+ * Dropbox writes with an expected revision. An upload in `update` mode with `strict_conflict`
457
+ * fails when the file changed since, and an upload in `add` mode fails when the file exists.
458
+ */
459
+ declare class DropboxProvider implements SyncProvider {
460
+ private readonly options;
461
+ private readonly send;
462
+ constructor(options: DropboxOptions);
463
+ read(path: string): Promise<RemoteFile | null>;
464
+ write(path: string, content: string, expectedVersion: string | null): Promise<{
465
+ version: string;
466
+ }>;
467
+ remove(path: string, expectedVersion: string): Promise<void>;
468
+ private request;
469
+ /**
470
+ * Reads a failed answer. Dropbox reports a failure that belongs to the call, such as a missing
471
+ * file or a conflict, with status 409. The caller decides what that means. Every other status
472
+ * is a failure of the request and is thrown here.
473
+ */
474
+ private failure;
475
+ private unexpected;
476
+ private invalid;
477
+ }
478
+
479
+ interface OneDriveOptions {
480
+ /** Returns the token. It may refresh the token first, so it is called for every request. */
481
+ getToken: () => Promise<string>;
482
+ fetch?: typeof fetch;
483
+ }
484
+ /**
485
+ * Keeps the files of an app in the OneDrive of the player, in the app folder under `Apps`. It
486
+ * needs only the `Files.ReadWrite.AppFolder` scope, so it sees no other file. The app folder
487
+ * exists for personal Microsoft accounts.
488
+ *
489
+ * A write sends the `eTag` that was read in an `If-Match` header, and a new file is created with
490
+ * `conflictBehavior=fail`. OneDrive answers 412 and 409 when the file changed or exists.
491
+ */
492
+ declare class OneDriveProvider implements SyncProvider {
493
+ private readonly options;
494
+ private readonly send;
495
+ constructor(options: OneDriveOptions);
496
+ read(path: string): Promise<RemoteFile | null>;
497
+ write(path: string, content: string, expectedVersion: string | null): Promise<{
498
+ version: string;
499
+ }>;
500
+ remove(path: string, expectedVersion: string): Promise<void>;
501
+ private itemUrl;
502
+ private ifMatch;
503
+ private info;
504
+ /** The download address is signed, so it must not carry the token. */
505
+ private fetchContent;
506
+ private request;
507
+ private assertOk;
508
+ private invalid;
509
+ }
510
+
511
+ /** The scopes that sync asks for. Together with the App folder access type, they reach one folder. */
512
+ declare const DROPBOX_SCOPES: readonly ["files.content.read", "files.content.write"];
513
+ /**
514
+ * The address of the Dropbox sign in page. `token_access_type=offline` makes Dropbox send a
515
+ * refresh token, which does not expire until the player removes the access.
516
+ */
517
+ declare const dropboxAuthorizeUrl: (clientId: string, redirectUri: string, state: string) => string;
518
+
519
+ /** The file scope that sync asks for. It reaches only the app folder of the player. */
520
+ declare const ONEDRIVE_FILE_SCOPE = "Files.ReadWrite.AppFolder";
521
+ /** The file scope, and `offline_access`, which makes Microsoft send a refresh token. */
522
+ declare const ONEDRIVE_SCOPES: readonly ["Files.ReadWrite.AppFolder", "offline_access"];
523
+ /** The address of the Microsoft sign in page for personal accounts. */
524
+ declare const microsoftAuthorizeUrl: (clientId: string, redirectUri: string, state: string) => string;
525
+
526
+ export { type CallbackResult, type Conflict, type ConflictKind, DATA_FILE, DROPBOX_SCOPES, type DeviceRecord, type DropboxOptions, DropboxProvider, type FirstSyncChoice, GITHUB_APP, GOOGLE_DRIVE_SCOPE, type GitHubOptions, GitHubProvider, type GoogleDriveOptions, GoogleDriveProvider, type InstalledRepository, META_FILE, META_FORMAT, MemoryProvider, type MergeInput, type MergeResult, ONEDRIVE_FILE_SCOPE, ONEDRIVE_SCOPES, type OneDriveOptions, OneDriveProvider, type ParsedMeta, type Path, type PathStep, ProviderError, type ProviderErrorKind, ROOT_FOLDER, RemoteDataError, type RemoteFile, type RepositoryCheck, type Schema, type Side, type StoredTokens, SyncBusyError, type SyncMeta, type SyncOptions, type SyncProvider, type SyncResult, type SyncState, type TokenResponse, type TokenSourceOptions, VersionConflictError, authorizeUrl, createTokenSource, dataPath, decodeBase64, dropboxAuthorizeUrl, emptyMeta, encodeBase64, equal, googleAuthorizeUrl, installUrl, isAppId, isObject, listInstalledRepositories, merge, metaPath, microsoftAuthorizeUrl, newState, parseMeta, parseTokenResponse, readCallback, resolveConflict, restoreCloudValues, sync, toStoredTokens, withChiefRemoved, withChiefRevision, withDevice, withSettingsRevision };