lucid-extension-sdk 1.1.0 → 1.1.1

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/commandtypes.d.ts CHANGED
@@ -90,6 +90,8 @@ export declare const CommandName: {
90
90
  readonly GetLLMContextFromItems: "llm";
91
91
  readonly GetOAuthClientId: "goci";
92
92
  readonly GetOAuthToken: "got";
93
+ readonly ClearOAuthToken: "cot";
94
+ readonly RevokeOAuthToken: "rot";
93
95
  readonly GetPackageSettings: "gps";
94
96
  readonly GetProduct: "gpr";
95
97
  readonly GetProperty: "gp";
@@ -430,6 +432,14 @@ export type CommandArgs = {
430
432
  query: GetOAuthTokenQuery;
431
433
  result: GetOAuthTokenResult;
432
434
  };
435
+ [CommandName.ClearOAuthToken]: {
436
+ query: ClearOAuthTokenQuery;
437
+ result: ClearOAuthTokenResult;
438
+ };
439
+ [CommandName.RevokeOAuthToken]: {
440
+ query: RevokeOAuthTokenQuery;
441
+ result: RevokeOAuthTokenResult;
442
+ };
433
443
  [CommandName.GetPackageSettings]: {
434
444
  query: GetPackageSettingsQuery;
435
445
  result: GetPackageSettingsResult;
@@ -838,6 +848,8 @@ export type AutoSyncSettings = {
838
848
  'rnml': string;
839
849
  /**no matched label */
840
850
  'nml': string;
851
+ /**empty state label */
852
+ 'esl'?: string | undefined;
841
853
  /**syncing growl message*/
842
854
  'sgm'?: string | undefined;
843
855
  /**removing growl message */
@@ -846,6 +858,12 @@ export type AutoSyncSettings = {
846
858
  'gsf': string;
847
859
  'sqs'?: string | undefined;
848
860
  'syqs'?: string | undefined;
861
+ /** getDefaultFilterFields action ID - field-only config */
862
+ 'gdf'?: string | undefined;
863
+ /** searchFields action ID - field-only config */
864
+ 'sf'?: string | undefined;
865
+ /** syncFields action ID - field-only config */
866
+ 'syf'?: string | undefined;
849
867
  /** If specified, setup fields that allow user to create query template that auto applies to dynamic tables when created*/
850
868
  'dpf'?: {
851
869
  /** Get search fields action */
@@ -854,6 +872,8 @@ export type AutoSyncSettings = {
854
872
  'gpv': string;
855
873
  } | undefined;
856
874
  'cftqs'?: string | undefined;
875
+ /** Get default filter fields action */
876
+ 'gdff'?: string | undefined;
857
877
  };
858
878
  export type SerializedFieldConfiguration = {
859
879
  /** Callback to get field definitions for all fields supported by the card integration */
@@ -1592,6 +1612,16 @@ export type GetOAuthTokenQuery = {
1592
1612
  'p': string;
1593
1613
  };
1594
1614
  export type GetOAuthTokenResult = Promise<string | undefined>;
1615
+ export type ClearOAuthTokenQuery = {
1616
+ /** OAuth provider name as specified in the package manifest */
1617
+ 'p': string;
1618
+ };
1619
+ export type ClearOAuthTokenResult = Promise<boolean>;
1620
+ export type RevokeOAuthTokenQuery = {
1621
+ /** OAuth provider name as specified in the package manifest */
1622
+ 'p': string;
1623
+ };
1624
+ export type RevokeOAuthTokenResult = Promise<boolean>;
1595
1625
  export type GetProductQuery = undefined;
1596
1626
  export type GetProductResult = LucidProduct;
1597
1627
  export type GetOAuthClientIdQuery = {
package/commandtypes.js CHANGED
@@ -78,6 +78,8 @@ exports.CommandName = {
78
78
  GetLLMContextFromItems: 'llm',
79
79
  GetOAuthClientId: 'goci',
80
80
  GetOAuthToken: 'got',
81
+ ClearOAuthToken: 'cot',
82
+ RevokeOAuthToken: 'rot',
81
83
  GetPackageSettings: 'gps',
82
84
  GetProduct: 'gpr',
83
85
  GetProperty: 'gp',
@@ -214,6 +216,8 @@ exports.commandTitles = new Map([
214
216
  [exports.CommandName.GetLLMContextFromItems, 'GetLLMContextFromItems'],
215
217
  [exports.CommandName.GetOAuthClientId, 'GetOAuthClientId'],
216
218
  [exports.CommandName.GetOAuthToken, 'GetOAuthToken'],
219
+ [exports.CommandName.ClearOAuthToken, 'ClearOAuthToken'],
220
+ [exports.CommandName.RevokeOAuthToken, 'RevokeOAuthToken'],
217
221
  [exports.CommandName.GetPackageSettings, 'GetPackageSettings'],
218
222
  [exports.CommandName.GetProduct, 'GetProduct'],
219
223
  [exports.CommandName.GetProperty, 'GetProperty'],
@@ -8,10 +8,10 @@ import type { DocumentPresetSetupFields } from './lucidcardintegrationpresetsetu
8
8
  */
9
9
  export declare const AutoSyncPaginationSize = 50;
10
10
  /**
11
- * This config defines variables and methods required to implement query sync for various container objects in Lucid
12
- * (e.g. timelines).
11
+ * Base config shared by all auto sync config types. Contains fields common to both query-based and
12
+ * field-only sync configurations.
13
13
  */
14
- export interface CardIntegrationAutoSyncConfig {
14
+ export interface BaseCardIntegrationAutoSyncConfig {
15
15
  /**
16
16
  * @deprecated Use AutoSyncConfigPhrasesType instead.
17
17
  */
@@ -28,6 +28,21 @@ export interface CardIntegrationAutoSyncConfig {
28
28
  * with a single ExtensionCardFieldOption in ExtensionCardField.options
29
29
  */
30
30
  getSyncDataSourceIdField: (searchSoFar: Map<string, SerializedFieldType>) => Promise<ExtensionCardFieldDefinition[]>;
31
+ /**
32
+ * @experimental
33
+ * Document preset setup configuration for this card integration's auto sync.
34
+ *
35
+ * When specified, this enables your card integration to participate in Lucid's document preset
36
+ * system, which allows users to configure dynamic matrix templates that automatically populate new
37
+ * documents with cards from your integration.
38
+ */
39
+ documentPresetSetupFields?: DocumentPresetSetupFields | undefined;
40
+ }
41
+ /**
42
+ * Auto sync config for query-string-based sync. Users enter a query string (e.g. JQL) to filter
43
+ * which items are synced.
44
+ */
45
+ export interface QueryStringCardIntegrationAutoSyncConfig extends BaseCardIntegrationAutoSyncConfig {
31
46
  /**
32
47
  * Perform a search on the query string. This should support pagination (See {@link AutoSyncPaginationSize}).
33
48
  *
@@ -40,15 +55,6 @@ export interface CardIntegrationAutoSyncConfig {
40
55
  * @returns the ImportResult for the query string.
41
56
  */
42
57
  syncQueryString?: (queryString: string, otherFields: Map<string, SerializedFieldType>) => Promise<ImportResult | ImportResult[]>;
43
- /**
44
- * @experimental
45
- * Document preset setup configuration for this card integration's auto sync.
46
- *
47
- * When specified, this enables your card integration to participate in Lucid's document preset
48
- * system, which allows users to configure dynamic matrix templates that automatically populate new
49
- * documents with cards from your integration.
50
- */
51
- documentPresetSetupFields?: DocumentPresetSetupFields | undefined;
52
58
  /**
53
59
  * Convert current filter selections to a query string. This is used to auto-fill the search bar
54
60
  * when advanced search mode is enabled and filters are changed.
@@ -57,7 +63,49 @@ export interface CardIntegrationAutoSyncConfig {
57
63
  * @returns The generated query string, or undefined if no query can be generated
58
64
  */
59
65
  convertFiltersToQueryString?: (filterFields: Map<string, SerializedFieldType>) => Promise<string | undefined>;
66
+ /**
67
+ * Provides the default filter fields to display in the auto-sync UI when advanced search mode is
68
+ * enabled. These fields seed the filter form so the user can refine the query without having to
69
+ * type a raw query string.
70
+ *
71
+ * @param searchSoFar The fields the user has populated so far (e.g. the selected data source id)
72
+ * @returns The filter fields to render
73
+ */
74
+ getDefaultFilterFields?: (searchSoFar: Map<string, SerializedFieldType>) => Promise<ExtensionCardFieldDefinition[]>;
75
+ }
76
+ /**
77
+ * @experimental
78
+ * Auto sync config for field-only sync. Users select values for structured filter fields rather
79
+ * than entering a free-form query string.
80
+ */
81
+ export interface FieldOnlyCardIntegrationAutoSyncConfig extends BaseCardIntegrationAutoSyncConfig {
82
+ /**
83
+ * Returns the filter field definitions available for the user to configure. Called whenever the
84
+ * current field selections change, allowing fields to be dynamically updated based on prior selections.
85
+ *
86
+ * @param searchSoFar The filter field values selected so far
87
+ * @returns the field definitions to display for filtering
88
+ */
89
+ getDefaultFilterFields: (searchSoFar: Map<string, SerializedFieldType>) => Promise<ExtensionCardFieldDefinition[]>;
90
+ /**
91
+ * Perform a search using the selected filter field values. This should support pagination
92
+ * (See {@link AutoSyncPaginationSize}).
93
+ *
94
+ * The syncDataSourceIdNonce is provided in instanceFields
95
+ * @param pageNumber If undefined, will return all results from the search
96
+ * @returns the search results after filtering using the given field values.
97
+ */
98
+ searchFields: (fields: Map<string, SerializedFieldType>, instanceFields: Map<string, SerializedFieldType>, pageNumber: number | undefined, nextPageToken: string | undefined) => Promise<SearchResult>;
99
+ /**
100
+ * @returns the ImportResult for the selected filter field values.
101
+ */
102
+ syncFields: (fields: Map<string, SerializedFieldType>, instanceFields: Map<string, SerializedFieldType>) => Promise<ImportResult | ImportResult[]>;
60
103
  }
104
+ /**
105
+ * Union of all supported auto sync config types. Use {@link QueryStringCardIntegrationAutoSyncConfig}
106
+ * for query-string-based sync or {@link FieldOnlyCardIntegrationAutoSyncConfig} for field-based sync.
107
+ */
108
+ export type CardIntegrationAutoSyncConfig = QueryStringCardIntegrationAutoSyncConfig | FieldOnlyCardIntegrationAutoSyncConfig;
61
109
  export interface AutoSyncConfigPhrasesType {
62
110
  /**
63
111
  * The header to display for the integration in the autosync modal.
@@ -79,6 +127,11 @@ export interface AutoSyncConfigPhrasesType {
79
127
  * The string to display when there are no matched items. For example, the Jira integration displays "No issues matched your search query"
80
128
  */
81
129
  noMatchedLabel: string;
130
+ /**
131
+ * The string to display in the empty state placeholder before any search has been executed. For example, the Jira
132
+ * integration displays "Input a query or set filters to search for Jira issues".
133
+ */
134
+ emptyStateLabel?: string | undefined;
82
135
  /**
83
136
  * If defined, A growl would show up with the string when the cards are syncing / importing into generators. For example, the Jira integration displays "Syncing 1 issue" or "Syncing 5 issues"
84
137
  */
@@ -36,10 +36,26 @@ export interface QuerySyncParam {
36
36
  'of': [string, SerializedFieldType][];
37
37
  }
38
38
  /** @ignore */
39
+ export interface FieldSearchParam {
40
+ 'f': [string, SerializedFieldType][];
41
+ 'if': [string, SerializedFieldType][];
42
+ 'pn': number | undefined;
43
+ 'npt': string | undefined;
44
+ }
45
+ /** @ignore */
46
+ export interface FieldSyncParam {
47
+ 'f': [string, SerializedFieldType][];
48
+ 'if': [string, SerializedFieldType][];
49
+ }
50
+ /** @ignore */
39
51
  export interface ConvertFiltersToQueryStringParam {
40
52
  'ff': [string, SerializedFieldType][];
41
53
  }
42
54
  /** @ignore */
55
+ export interface GetDefaultFilterFieldsParam {
56
+ 's': [string, SerializedFieldType][];
57
+ }
58
+ /** @ignore */
43
59
  export interface QuerySyncRemoveNonMatchingLabelParam {
44
60
  'g': string;
45
61
  }
@@ -347,6 +347,7 @@ class LucidCardIntegrationRegistry {
347
347
  'qph': autoSync.phrases.queryPlaceholder,
348
348
  'rnml': LucidCardIntegrationRegistry.nextHookName(),
349
349
  'nml': autoSync.phrases.noMatchedLabel,
350
+ 'esl': autoSync.phrases.emptyStateLabel,
350
351
  };
351
352
  client.registerAction(serialized['as']['p']['rnml'], ({ 'g': inputSoFar }) => {
352
353
  var _a;
@@ -374,11 +375,16 @@ class LucidCardIntegrationRegistry {
374
375
  const result = await autoSync.getSyncDataSourceIdField(new Map(inputSoFar));
375
376
  return (0, cardintegrationdefinitions_1.serializeCardFieldArrayDefinition)(result);
376
377
  });
377
- if (autoSync.searchQueryString) {
378
- serialized['as']['sqs'] = LucidCardIntegrationRegistry.nextHookName();
379
- client.registerAction(serialized['as']['sqs'], async ({ 'qs': queryString, 'of': otherFields, 'pn': pageNumber, 'npt': nextPageToken, }) => {
380
- var _a;
381
- const result = await ((_a = autoSync.searchQueryString) === null || _a === void 0 ? void 0 : _a.call(autoSync, queryString, new Map(otherFields), pageNumber, nextPageToken));
378
+ if ('searchFields' in autoSync) {
379
+ const fieldOnlyAutoSync = autoSync;
380
+ serialized['as']['gdf'] = LucidCardIntegrationRegistry.nextHookName();
381
+ client.registerAction(serialized['as']['gdf'], async ({ 'i': inputSoFar }) => {
382
+ const result = await fieldOnlyAutoSync.getDefaultFilterFields(new Map(inputSoFar));
383
+ return (0, cardintegrationdefinitions_1.serializeCardFieldArrayDefinition)(result);
384
+ });
385
+ serialized['as']['sf'] = LucidCardIntegrationRegistry.nextHookName();
386
+ client.registerAction(serialized['as']['sf'], async ({ 'f': fields, 'if': instanceFields, 'pn': pageNumber, 'npt': nextPageToken, }) => {
387
+ const result = await fieldOnlyAutoSync.searchFields(new Map(fields), new Map(instanceFields), pageNumber, nextPageToken);
382
388
  if (result) {
383
389
  return this.serializeSearchResult(result);
384
390
  }
@@ -389,15 +395,55 @@ class LucidCardIntegrationRegistry {
389
395
  });
390
396
  }
391
397
  });
392
- }
393
- if (autoSync.syncQueryString) {
394
- serialized['as']['syqs'] = LucidCardIntegrationRegistry.nextHookName();
395
- client.registerAction(serialized['as']['syqs'], async ({ 'qs': queryString, 'of': otherFields }) => {
396
- var _a, _b;
397
- const result = (_b = (await ((_a = autoSync.syncQueryString) === null || _a === void 0 ? void 0 : _a.call(autoSync, queryString, new Map(otherFields))))) !== null && _b !== void 0 ? _b : [];
398
+ serialized['as']['syf'] = LucidCardIntegrationRegistry.nextHookName();
399
+ client.registerAction(serialized['as']['syf'], async ({ 'f': fields, 'if': instanceFields }) => {
400
+ var _a;
401
+ const result = (_a = (await fieldOnlyAutoSync.syncFields(new Map(fields), new Map(instanceFields)))) !== null && _a !== void 0 ? _a : [];
398
402
  return LucidCardIntegrationRegistry.serializeImportResults(result);
399
403
  });
400
404
  }
405
+ else {
406
+ const queryStringAutoSync = autoSync;
407
+ if (queryStringAutoSync.searchQueryString) {
408
+ serialized['as']['sqs'] = LucidCardIntegrationRegistry.nextHookName();
409
+ client.registerAction(serialized['as']['sqs'], async ({ 'qs': queryString, 'of': otherFields, 'pn': pageNumber, 'npt': nextPageToken, }) => {
410
+ var _a;
411
+ const result = await ((_a = queryStringAutoSync.searchQueryString) === null || _a === void 0 ? void 0 : _a.call(queryStringAutoSync, queryString, new Map(otherFields), pageNumber, nextPageToken));
412
+ if (result) {
413
+ return this.serializeSearchResult(result);
414
+ }
415
+ else {
416
+ return this.serializeSearchResult({
417
+ searchResults: [],
418
+ error: 'Unknown Error Occurred',
419
+ });
420
+ }
421
+ });
422
+ }
423
+ if (queryStringAutoSync.syncQueryString) {
424
+ serialized['as']['syqs'] = LucidCardIntegrationRegistry.nextHookName();
425
+ client.registerAction(serialized['as']['syqs'], async ({ 'qs': queryString, 'of': otherFields }) => {
426
+ var _a, _b;
427
+ const result = (_b = (await ((_a = queryStringAutoSync.syncQueryString) === null || _a === void 0 ? void 0 : _a.call(queryStringAutoSync, queryString, new Map(otherFields))))) !== null && _b !== void 0 ? _b : [];
428
+ return LucidCardIntegrationRegistry.serializeImportResults(result);
429
+ });
430
+ }
431
+ if (queryStringAutoSync.convertFiltersToQueryString) {
432
+ serialized['as']['cftqs'] = LucidCardIntegrationRegistry.nextHookName();
433
+ client.registerAction(serialized['as']['cftqs'], async ({ 'ff': filterFields }) => {
434
+ var _a;
435
+ return await ((_a = queryStringAutoSync.convertFiltersToQueryString) === null || _a === void 0 ? void 0 : _a.call(queryStringAutoSync, new Map(filterFields)));
436
+ });
437
+ }
438
+ if (queryStringAutoSync.getDefaultFilterFields) {
439
+ serialized['as']['gdff'] = LucidCardIntegrationRegistry.nextHookName();
440
+ client.registerAction(serialized['as']['gdff'], async ({ 's': searchSoFar }) => {
441
+ var _a, _b;
442
+ const result = (_b = (await ((_a = queryStringAutoSync.getDefaultFilterFields) === null || _a === void 0 ? void 0 : _a.call(queryStringAutoSync, new Map(searchSoFar))))) !== null && _b !== void 0 ? _b : [];
443
+ return (0, cardintegrationdefinitions_1.serializeCardFieldArrayDefinition)(result);
444
+ });
445
+ }
446
+ }
401
447
  if (autoSync.documentPresetSetupFields) {
402
448
  const documentPresetSetupFields = autoSync.documentPresetSetupFields;
403
449
  serialized['as']['dpf'] = {
@@ -417,13 +463,6 @@ class LucidCardIntegrationRegistry {
417
463
  };
418
464
  });
419
465
  }
420
- if (autoSync.convertFiltersToQueryString) {
421
- serialized['as']['cftqs'] = LucidCardIntegrationRegistry.nextHookName();
422
- client.registerAction(serialized['as']['cftqs'], async ({ 'ff': filterFields }) => {
423
- var _a;
424
- return await ((_a = autoSync.convertFiltersToQueryString) === null || _a === void 0 ? void 0 : _a.call(autoSync, new Map(filterFields)));
425
- });
426
- }
427
466
  }
428
467
  if (cardIntegration.fieldConfiguration.customFieldDisplaySettings) {
429
468
  serialized['fc']['cfds'] = {
package/core/checks.d.ts CHANGED
@@ -124,6 +124,13 @@ export declare function isTypedArray<T>(typeGuard: (a: unknown) => a is T): (val
124
124
  * @returns Whether variable is an array of defined and not null elements.
125
125
  */
126
126
  export declare function isDefinedArray<T>(val: (T | undefined | null)[]): val is T[];
127
+ /**
128
+ * Returns true if the specified value is an array with the same length as the provided type guards, and each element
129
+ * passes the type guard at the same index in guards.
130
+ */
131
+ export declare function isTypedTuple<const Tuple extends readonly unknown[]>(guards: {
132
+ [K in keyof Tuple]: (a: unknown) => a is Tuple[K];
133
+ }): (val: unknown) => val is Tuple;
127
134
  /**
128
135
  * Returns true if the specified value is a map.
129
136
  *
package/core/checks.js CHANGED
@@ -19,6 +19,7 @@ exports.isRecord = isRecord;
19
19
  exports.isArray = isArray;
20
20
  exports.isTypedArray = isTypedArray;
21
21
  exports.isDefinedArray = isDefinedArray;
22
+ exports.isTypedTuple = isTypedTuple;
22
23
  exports.isMap = isMap;
23
24
  exports.isExactLength = isExactLength;
24
25
  exports.isAtLeastLength = isAtLeastLength;
@@ -196,6 +197,15 @@ function isTypedArray(typeGuard) {
196
197
  function isDefinedArray(val) {
197
198
  return isArray(val) && val.every(isDefAndNotNull);
198
199
  }
200
+ /**
201
+ * Returns true if the specified value is an array with the same length as the provided type guards, and each element
202
+ * passes the type guard at the same index in guards.
203
+ */
204
+ function isTypedTuple(guards) {
205
+ return function (val) {
206
+ return isArray(val) && val.length == guards.length && guards.every((guard, i) => guard(val[i]));
207
+ };
208
+ }
199
209
  /**
200
210
  * Returns true if the specified value is a map.
201
211
  *
@@ -0,0 +1,10 @@
1
+ import { Format } from './format';
2
+ import { FormatInput, FormatOutput } from './simpleformat';
3
+ type Raw<Formats extends readonly Format<any, any>[]> = {
4
+ [K in keyof Formats]: FormatInput<Formats[K]>;
5
+ };
6
+ type Tuple<Formats extends readonly Format<any, any>[]> = {
7
+ [K in keyof Formats]: FormatOutput<Formats[K]>;
8
+ };
9
+ export declare function TupleFormat<const T extends readonly Format<any, any>[]>(formats: T): Format<Raw<T>, Tuple<T>>;
10
+ export {};
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TupleFormat = TupleFormat;
4
+ const checks_1 = require("../checks");
5
+ const primitivedata_1 = require("../primitivedata");
6
+ const format_1 = require("./format");
7
+ function TupleFormat(formats) {
8
+ return new format_1.Format((tuple) => tuple.map((x, i) => formats[i].fromJson(x)), (tuple) => tuple.map((x, i) => formats[i].toJson(x)), (0, checks_1.isTypedTuple)(formats.map((f) => f.validator)), (0, primitivedata_1.generateTuple)(formats.map((f) => f.generator)));
9
+ }
@@ -17,6 +17,6 @@ export declare function mergeGenerators<T>(...generatorFunctions: (() => Iterabl
17
17
  */
18
18
  export declare function zipHoldShorter<T extends {
19
19
  [key: string]: Iterable<unknown>;
20
- }>(obj: T): Iterable<{
20
+ } | Iterable<unknown>[]>(obj: T): Iterable<{
21
21
  [key in keyof T]-?: T[key] extends Iterable<infer U> ? U : never;
22
22
  }>;
package/core/iterable.js CHANGED
@@ -29,6 +29,7 @@ function mergeGenerators(...generatorFunctions) {
29
29
  * Assumes that no iterables are empty
30
30
  */
31
31
  function* zipHoldShorter(obj) {
32
+ const array = Array.isArray(obj);
32
33
  const unheldKeys = new Set(Object.keys(obj));
33
34
  const iterators = {};
34
35
  for (const key of unheldKeys) {
@@ -39,13 +40,16 @@ function* zipHoldShorter(obj) {
39
40
  }
40
41
  iterators[key] = value[Symbol.iterator]();
41
42
  }
42
- const heldValues = {};
43
- let previous = {};
43
+ const heldValues = array ? [] : {};
44
+ let previous = array ? [] : {};
44
45
  while (unheldKeys.size > 0) {
45
- const zipped = Object.assign({}, heldValues);
46
+ const zipped = array ? [...heldValues] : Object.assign({}, heldValues);
46
47
  for (const key of unheldKeys) {
47
48
  const result = iterators[key].next();
48
49
  if (result.done) {
50
+ // if obj is an array, heldValues will likely be a sparse array
51
+ // splatting a sparse array creates a dense array, so zipped will never be sparse
52
+ // thus the sparse array never escapes this function
49
53
  heldValues[key] = previous[key];
50
54
  unheldKeys.delete(key);
51
55
  zipped[key] = heldValues[key];
@@ -29,6 +29,10 @@ export declare function generateUnknown(): Iterable<unknown>;
29
29
  /** @see {generateData} */
30
30
  export declare function generateArray<T>(dataGenerator: () => Iterable<T>): () => Iterable<T[]>;
31
31
  /** @see {generateData} */
32
+ export declare function generateTuple<const Tuple extends readonly unknown[]>(generators: {
33
+ [K in keyof Tuple]: () => Iterable<Tuple[K]>;
34
+ }): () => Iterable<Tuple>;
35
+ /** @see {generateData} */
32
36
  export declare function generateNull(): Iterable<null>;
33
37
  /** @see {generateData} */
34
38
  export declare function generateUndefined(): Iterable<undefined>;
@@ -13,6 +13,7 @@ exports.generateUrlStrings = generateUrlStrings;
13
13
  exports.generateBooleans = generateBooleans;
14
14
  exports.generateUnknown = generateUnknown;
15
15
  exports.generateArray = generateArray;
16
+ exports.generateTuple = generateTuple;
16
17
  exports.generateNull = generateNull;
17
18
  exports.generateUndefined = generateUndefined;
18
19
  exports.generateVoid = generateVoid;
@@ -89,6 +90,13 @@ function generateArray(dataGenerator) {
89
90
  };
90
91
  }
91
92
  /** @see {generateData} */
93
+ function generateTuple(generators) {
94
+ return function () {
95
+ const gs = generators.map((g) => g());
96
+ return (0, iterable_1.zipHoldShorter)(gs);
97
+ };
98
+ }
99
+ /** @see {generateData} */
92
100
  function generateNull() {
93
101
  return [null];
94
102
  }
package/editorclient.d.ts CHANGED
@@ -183,6 +183,25 @@ export declare class EditorClient {
183
183
  * @returns An oauth token, or undefined if a valid token cannot be obtained
184
184
  */
185
185
  getOAuthToken(providerName: string): Promise<string | undefined>;
186
+ /**
187
+ * Clear the cached OAuth token for a provider, removing it from Lucid's storage without
188
+ * revoking it at the external provider. After clearing, the next call to getOAuthToken will
189
+ * re-prompt the user for authorization.
190
+ *
191
+ * @param providerName Name of the OAuth provider
192
+ * @returns true if the token was cleared successfully
193
+ */
194
+ clearOAuthToken(providerName: string): Promise<boolean>;
195
+ /**
196
+ * Revoke the OAuth token at the external provider's revocation endpoint (if configured in the
197
+ * package manifest via revokeTokenUrl), then remove it from Lucid's storage. If revocation at
198
+ * the external provider fails, the token is preserved and this method returns false so you can
199
+ * retry.
200
+ *
201
+ * @param providerName Name of the OAuth provider
202
+ * @returns true if the token was revoked and cleared successfully, false if external revocation failed
203
+ */
204
+ revokeOAuthToken(providerName: string): Promise<boolean>;
186
205
  /**
187
206
  * Fetch the OAuth Client Id if there is one
188
207
  *
package/editorclient.js CHANGED
@@ -260,6 +260,29 @@ class EditorClient {
260
260
  async getOAuthToken(providerName) {
261
261
  return await this.sendCommand(commandtypes_1.CommandName.GetOAuthToken, { 'p': providerName });
262
262
  }
263
+ /**
264
+ * Clear the cached OAuth token for a provider, removing it from Lucid's storage without
265
+ * revoking it at the external provider. After clearing, the next call to getOAuthToken will
266
+ * re-prompt the user for authorization.
267
+ *
268
+ * @param providerName Name of the OAuth provider
269
+ * @returns true if the token was cleared successfully
270
+ */
271
+ async clearOAuthToken(providerName) {
272
+ return await this.sendCommand(commandtypes_1.CommandName.ClearOAuthToken, { 'p': providerName });
273
+ }
274
+ /**
275
+ * Revoke the OAuth token at the external provider's revocation endpoint (if configured in the
276
+ * package manifest via revokeTokenUrl), then remove it from Lucid's storage. If revocation at
277
+ * the external provider fails, the token is preserved and this method returns false so you can
278
+ * retry.
279
+ *
280
+ * @param providerName Name of the OAuth provider
281
+ * @returns true if the token was revoked and cleared successfully, false if external revocation failed
282
+ */
283
+ async revokeOAuthToken(providerName) {
284
+ return await this.sendCommand(commandtypes_1.CommandName.RevokeOAuthToken, { 'p': providerName });
285
+ }
263
286
  /**
264
287
  * Fetch the OAuth Client Id if there is one
265
288
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lucid-extension-sdk",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "Utility classes for writing Lucid Software editor extensions",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",