sg-widgets-core 0.1.0 → 0.2.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.
@@ -577,6 +577,57 @@ interface FollowingOptions {
577
577
  entity?: string;
578
578
  projectId?: number;
579
579
  }
580
+ /**
581
+ * One write of a batch, in the shape `POST /entity/_batch` takes. `entity` is the
582
+ * schema name, never the URL slug, and the id key is `record_id` (recipes/002).
583
+ */
584
+ type BatchRequest = {
585
+ request_type: 'create';
586
+ entity: string;
587
+ data: Record<string, unknown>;
588
+ } | {
589
+ request_type: 'update';
590
+ entity: string;
591
+ record_id: number;
592
+ data: Record<string, unknown>;
593
+ } | {
594
+ request_type: 'delete';
595
+ entity: string;
596
+ record_id: number;
597
+ };
598
+ /**
599
+ * One row of a batch answer. A create or an update carries its record under `data`;
600
+ * a delete row is flat, and its `uuid` is generated per request (recipes/002).
601
+ */
602
+ type BatchResult = {
603
+ request_type: 'create';
604
+ data: EntityRow;
605
+ } | {
606
+ request_type: 'update';
607
+ data: EntityRow;
608
+ } | {
609
+ request_type: 'delete';
610
+ type: string;
611
+ id: number;
612
+ uuid: string;
613
+ did_delete: boolean;
614
+ };
615
+ /** A row of the `_batch` answer before it is paired with its request. */
616
+ type RawBatchRow = {
617
+ data: EntityRow;
618
+ } | {
619
+ request_type: 'delete';
620
+ type: string;
621
+ id: number;
622
+ uuid: string;
623
+ did_delete: boolean;
624
+ };
625
+ /**
626
+ * Pair each row of a batch answer with the request at its position. Rows come back
627
+ * one per request in request order, so position is the key: two creates can share a
628
+ * `code` (recipes/002).
629
+ */
630
+ declare function batchResults(requests: readonly BatchRequest[], rows: readonly RawBatchRow[]): BatchResult[];
580
631
  interface SgClient {
581
632
  /** Enabled entity types on the site with their display names. */
582
633
  entityTypes(): Promise<EntityTypeInfo[]>;
@@ -675,6 +726,32 @@ interface SgClient {
675
726
  * (get_entity_human_users_id_following).
676
727
  */
677
728
  following(userId: number, options?: FollowingOptions): Promise<EntityRef[]>;
729
+ /**
730
+ * Retire one row. It is not erased: it reads 404 and comes back with `revive`.
731
+ * A second delete is 404 (delete_entity_type_id). Deleting a row changes others:
732
+ * a Shot retires its Versions (probe 060), and a Task retires its dependencies and
733
+ * nulls `Version.sg_task` and `PublishedFile.task` without saying so
734
+ * (089_task_delete_side_effects).
735
+ */
736
+ delete(entityType: string, id: number): Promise<void>;
737
+ /**
738
+ * Bring a retired row back with the values it had, and answer whether it was
739
+ * retired: `false` means it was already live (post_entity_type_id). A revived Task
740
+ * gets its links back and its chain rescheduled (089_task_delete_side_effects).
741
+ */
742
+ revive(entityType: string, id: number): Promise<boolean>;
743
+ /**
744
+ * Apply creates, updates and deletes in one atomic call, one result per request in
745
+ * request order.
746
+ *
747
+ * One failing request rolls back every other one and rejects with its status and
748
+ * title. A request cannot point at a row another request of the same batch creates:
749
+ * send one batch per level. A create inside a batch skips the `project` check a
750
+ * single create makes and answers the id of a row no read reaches (report 001), so
751
+ * validate the payload first. A read timeout says nothing about what landed; keep a
752
+ * batch to about 200 requests and make it re-runnable (recipes/002).
753
+ */
754
+ batch(requests: BatchRequest[]): Promise<BatchResult[]>;
678
755
  }
679
756
  /** The node shape `/hierarchy/_expand` answers, before normalising. */
680
757
  interface RawHierarchyNode {
@@ -725,6 +802,9 @@ declare class RestClient implements SgClient {
725
802
  }): Promise<TextSearchRow[]>;
726
803
  update(entityType: string, id: number, patch: Record<string, unknown>): Promise<EntityRow>;
727
804
  create(entityType: string, body: Record<string, unknown>): Promise<EntityRow>;
805
+ delete(entityType: string, id: number): Promise<void>;
806
+ revive(entityType: string, id: number): Promise<boolean>;
807
+ batch(requests: BatchRequest[]): Promise<BatchResult[]>;
728
808
  upload(entityType: string, id: number, file: UploadFile): Promise<UploadResult>;
729
809
  summarize(entityType: string, options?: SummarizeOptions): Promise<SummarizeResult>;
730
810
  threadContents(noteId: number, entityFields?: Record<string, string[]>): Promise<ThreadRow[]>;
@@ -737,4 +817,4 @@ declare class RestClient implements SgClient {
737
817
  /** One `_search` row of `EventLogEntry`, with `meta` decoded as the API returns it. */
738
818
  declare function normalizeEventLogEntry(row: EntityRow): EventLogEntry;
739
819
  //#endregion
740
- export { STOCK_SPRITE_PATH as $, isOperator as $t, hierarchyEntity as A, emptyFilter as At, intersectStatuses as B, DataType as Bt, SummaryType as C, FilterNode as Ct, UploadFile as D, WireCondition as Dt, ThreadRow as E, TextSearchFilter as Et, StatusIcon as F, referencedPaths as Ft, statusLabel as G, TIME_UNITS as Gt, relativeLuminance as H, OPERATORS as Ht, StatusOption as I, toApi3Hash as It, NATIVE_STATUSES as J, ValueShape as Jt, statusPaint as K, TimeUnit as Kt, StatusPaint as L, toFilterArray as Lt, normalizeHierarchyNode as M, group as Mt, Rgb as N, isBlankCondition as Nt, UploadResult as O, WireGroup as Ot, StatusGlyph as P, isEmptyFilter as Pt, STOCK_ICON_KEYS as Q, isNumericType as Qt, StatusRecord as R, walk as Rt, SummaryGrouping as S, FilterGroup as St, ThreadAuthor as T, Scalar as Tt, rgbToCss as U, OPERATORS_BY_TYPE as Ut, parseBgColor as V, NEGATING_OPERATORS as Vt, statusGlyph as W, Operator as Wt, STOCK_ICON_CELLS as X, isFilterable as Xt, NativeStatus as Y, isDataType as Yt, STOCK_ICON_DATA_URLS as Z, isLinkType as Zt, SgClient as _, statusFieldNameFor as _t, EventLogOptions as a, stockIconSource as at, SummaryField as b, EntityRef as bt, HierarchyNode as c, FieldSchema as ct, RawHierarchyNode as d, RawProperty as dt, isTemporalType as en, SpriteCell as et, RestClient as f, displayNameOf as ft, SgApiError as g, statusFieldFor as gt, SearchResult as h, normalizeFields as ht, EventLogEntry as i, spriteStyle as it, normalizeEventLogEntry as j, fromWire as jt, eventLogFilters as k, condition as kt, HierarchyPath as l, RawFieldSchema as lt, SearchOptions as m, normalizeField as mt, EntityRow as n, supportsOperator as nn, isNativeStatus as nt, EventLogResult as o, DISPLAY_NAME_FIELDS as ot, RestClientOptions as p, fieldSchemaOverride as pt, usableStatuses as q, VALUE_SHAPE as qt, EntityTypeInfo as r, nativeStatus as rt, FollowingOptions as s, FIELD_SCHEMA_OVERRIDES as st, EVENT_LOG_FIELDS as t, operatorsFor as tn, StockIconSource as tt, HierarchyRef as u, RawFieldsResponse as ut, SummarizeOptions as v, undeclaredField as vt, TextSearchRow as w, LogicalOperator as wt, SummaryGroup as x, FilterCondition as xt, SummarizeResult as y, ConditionValue as yt, foregroundFor as z, DATA_TYPES as zt };
820
+ export { NativeStatus as $, isDataType as $t, UploadFile as A, TextSearchFilter as At, StatusOption as B, toApi3Hash as Bt, SummaryField as C, ConditionValue as Ct, TextSearchRow as D, FilterNode as Dt, SummaryType as E, FilterGroup as Et, normalizeEventLogEntry as F, fromWire as Ft, parseBgColor as G, NEGATING_OPERATORS as Gt, StatusRecord as H, walk as Ht, normalizeHierarchyNode as I, group as It, statusGlyph as J, Operator as Jt, relativeLuminance as K, OPERATORS as Kt, Rgb as L, isBlankCondition as Lt, batchResults as M, WireGroup as Mt, eventLogFilters as N, condition as Nt, ThreadAuthor as O, LogicalOperator as Ot, hierarchyEntity as P, emptyFilter as Pt, NATIVE_STATUSES as Q, ValueShape as Qt, StatusGlyph as R, isEmptyFilter as Rt, SummarizeResult as S, undeclaredField as St, SummaryGrouping as T, FilterCondition as Tt, foregroundFor as U, DATA_TYPES as Ut, StatusPaint as V, toFilterArray as Vt, intersectStatuses as W, DataType as Wt, statusPaint as X, TimeUnit as Xt, statusLabel as Y, TIME_UNITS as Yt, usableStatuses as Z, VALUE_SHAPE as Zt, SearchOptions as _, fieldSchemaOverride as _t, EntityTypeInfo as a, operatorsFor as an, StockIconSource as at, SgClient as b, statusFieldFor as bt, EventLogResult as c, spriteStyle as ct, HierarchyPath as d, FIELD_SCHEMA_OVERRIDES as dt, isFilterable as en, STOCK_ICON_CELLS as et, HierarchyRef as f, FieldSchema as ft, RestClientOptions as g, displayNameOf as gt, RestClient as h, RawProperty as ht, EntityRow as i, isTemporalType as in, SpriteCell as it, UploadResult as j, WireCondition as jt, ThreadRow as k, Scalar as kt, FollowingOptions as l, stockIconSource as lt, RawHierarchyNode as m, RawFieldsResponse as mt, BatchResult as n, isNumericType as nn, STOCK_ICON_KEYS as nt, EventLogEntry as o, supportsOperator as on, isNativeStatus as ot, RawBatchRow as p, RawFieldSchema as pt, rgbToCss as q, OPERATORS_BY_TYPE as qt, EVENT_LOG_FIELDS as r, isOperator as rn, STOCK_SPRITE_PATH as rt, EventLogOptions as s, nativeStatus as st, BatchRequest as t, isLinkType as tn, STOCK_ICON_DATA_URLS as tt, HierarchyNode as u, DISPLAY_NAME_FIELDS as ut, SearchResult as v, normalizeField as vt, SummaryGroup as w, EntityRef as wt, SummarizeOptions as x, statusFieldNameFor as xt, SgApiError as y, normalizeFields as yt, StatusIcon as z, referencedPaths as zt };
@@ -607,6 +607,30 @@ const EVENT_LOG_FIELDS = [
607
607
  "user"
608
608
  ];
609
609
  /**
610
+ * Pair each row of a batch answer with the request at its position. Rows come back
611
+ * one per request in request order, so position is the key: two creates can share a
612
+ * `code` (recipes/002).
613
+ */
614
+ function batchResults(requests, rows) {
615
+ return rows.map((row, i) => {
616
+ const kind = requests[i]?.request_type;
617
+ if (kind === "delete" || !("data" in row)) {
618
+ const flat = row;
619
+ return {
620
+ request_type: "delete",
621
+ type: flat.type,
622
+ id: flat.id,
623
+ uuid: flat.uuid,
624
+ did_delete: flat.did_delete
625
+ };
626
+ }
627
+ return {
628
+ request_type: kind === "update" ? "update" : "create",
629
+ data: row.data
630
+ };
631
+ });
632
+ }
633
+ /**
610
634
  * Normalise one node and the level below it.
611
635
  *
612
636
  * The sample response gives a child a `label`, a `ref` and `has_children` but not
@@ -755,6 +779,15 @@ var RestClient = class {
755
779
  async create(entityType, body) {
756
780
  return (await this.request("POST", `/entity/${pluralPath(entityType)}`, body, void 0, "application/json")).data;
757
781
  }
782
+ async delete(entityType, id) {
783
+ await this.request("DELETE", `/entity/${pluralPath(entityType)}/${id}`);
784
+ }
785
+ async revive(entityType, id) {
786
+ return (await this.request("POST", `/entity/${pluralPath(entityType)}/${id}`, void 0, { revive: 1 })).meta?.did_revive === true;
787
+ }
788
+ async batch(requests) {
789
+ return batchResults(requests, (await this.request("POST", "/entity/_batch", { requests }, void 0, "application/json")).data);
790
+ }
758
791
  async upload(entityType, id, file) {
759
792
  const field = file.field === void 0 ? "" : `/${file.field}`;
760
793
  const ticket = await this.request("GET", `/entity/${pluralPath(entityType)}/${id}${field}/_upload`, void 0, { filename: file.filename });
@@ -1012,4 +1045,4 @@ function toStatusIcon(a) {
1012
1045
  }
1013
1046
  }
1014
1047
  //#endregion
1015
- export { OPERATORS as A, operatorsFor as B, isEmptyFilter as C, walk as D, toFilterArray as E, isFilterable as F, isLinkType as I, isNumericType as L, TIME_UNITS as M, VALUE_SHAPE as N, DATA_TYPES as O, isDataType as P, isOperator as R, isBlankCondition as S, toApi3Hash as T, supportsOperator as V, pluralPath as _, hierarchyEntity as a, fromWire as b, DISPLAY_NAME_FIELDS as c, fieldSchemaOverride as d, normalizeField as f, undeclaredField as g, statusFieldNameFor as h, eventLogFilters as i, OPERATORS_BY_TYPE as j, NEGATING_OPERATORS as k, FIELD_SCHEMA_OVERRIDES as l, statusFieldFor as m, RestClient as n, normalizeEventLogEntry as o, normalizeFields as p, SgApiError as r, normalizeHierarchyNode as s, EVENT_LOG_FIELDS as t, displayNameOf as u, condition as v, referencedPaths as w, group as x, emptyFilter as y, isTemporalType as z };
1048
+ export { NEGATING_OPERATORS as A, isTemporalType as B, isBlankCondition as C, toFilterArray as D, toApi3Hash as E, isDataType as F, supportsOperator as H, isFilterable as I, isLinkType as L, OPERATORS_BY_TYPE as M, TIME_UNITS as N, walk as O, VALUE_SHAPE as P, isNumericType as R, group as S, referencedPaths as T, operatorsFor as V, undeclaredField as _, eventLogFilters as a, emptyFilter as b, normalizeHierarchyNode as c, displayNameOf as d, fieldSchemaOverride as f, statusFieldNameFor as g, statusFieldFor as h, batchResults as i, OPERATORS as j, DATA_TYPES as k, DISPLAY_NAME_FIELDS as l, normalizeFields as m, RestClient as n, hierarchyEntity as o, normalizeField as p, SgApiError as r, normalizeEventLogEntry as s, EVENT_LOG_FIELDS as t, FIELD_SCHEMA_OVERRIDES as u, pluralPath as v, isEmptyFilter as w, fromWire as x, condition as y, isOperator as z };
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as STOCK_SPRITE_PATH, $t as isOperator, A as hierarchyEntity, At as emptyFilter, B as intersectStatuses, Bt as DataType, C as SummaryType, Ct as FilterNode, D as UploadFile, Dt as WireCondition, E as ThreadRow, Et as TextSearchFilter, F as StatusIcon, Ft as referencedPaths, G as statusLabel, Gt as TIME_UNITS, H as relativeLuminance, Ht as OPERATORS, I as StatusOption, It as toApi3Hash, J as NATIVE_STATUSES, Jt as ValueShape, K as statusPaint, Kt as TimeUnit, L as StatusPaint, Lt as toFilterArray, M as normalizeHierarchyNode, Mt as group, N as Rgb, Nt as isBlankCondition, O as UploadResult, Ot as WireGroup, P as StatusGlyph, Pt as isEmptyFilter, Q as STOCK_ICON_KEYS, Qt as isNumericType, R as StatusRecord, Rt as walk, S as SummaryGrouping, St as FilterGroup, T as ThreadAuthor, Tt as Scalar, U as rgbToCss, Ut as OPERATORS_BY_TYPE, V as parseBgColor, Vt as NEGATING_OPERATORS, W as statusGlyph, Wt as Operator, X as STOCK_ICON_CELLS, Xt as isFilterable, Y as NativeStatus, Yt as isDataType, Z as STOCK_ICON_DATA_URLS, Zt as isLinkType, _ as SgClient, _t as statusFieldNameFor, a as EventLogOptions, at as stockIconSource, b as SummaryField, bt as EntityRef, c as HierarchyNode, ct as FieldSchema, d as RawHierarchyNode, dt as RawProperty, en as isTemporalType, et as SpriteCell, f as RestClient, ft as displayNameOf, g as SgApiError, gt as statusFieldFor, h as SearchResult, ht as normalizeFields, i as EventLogEntry, it as spriteStyle, j as normalizeEventLogEntry, jt as fromWire, k as eventLogFilters, kt as condition, l as HierarchyPath, lt as RawFieldSchema, m as SearchOptions, mt as normalizeField, n as EntityRow, nn as supportsOperator, nt as isNativeStatus, o as EventLogResult, ot as DISPLAY_NAME_FIELDS, p as RestClientOptions, pt as fieldSchemaOverride, q as usableStatuses, qt as VALUE_SHAPE, r as EntityTypeInfo, rt as nativeStatus, s as FollowingOptions, st as FIELD_SCHEMA_OVERRIDES, t as EVENT_LOG_FIELDS, tn as operatorsFor, tt as StockIconSource, u as HierarchyRef, ut as RawFieldsResponse, v as SummarizeOptions, vt as undeclaredField, w as TextSearchRow, wt as LogicalOperator, x as SummaryGroup, xt as FilterCondition, y as SummarizeResult, yt as ConditionValue, z as foregroundFor, zt as DATA_TYPES } from "./client-BhHppzTV.mjs";
1
+ import { $ as NativeStatus, $t as isDataType, A as UploadFile, At as TextSearchFilter, B as StatusOption, Bt as toApi3Hash, C as SummaryField, Ct as ConditionValue, D as TextSearchRow, Dt as FilterNode, E as SummaryType, Et as FilterGroup, F as normalizeEventLogEntry, Ft as fromWire, G as parseBgColor, Gt as NEGATING_OPERATORS, H as StatusRecord, Ht as walk, I as normalizeHierarchyNode, It as group, J as statusGlyph, Jt as Operator, K as relativeLuminance, Kt as OPERATORS, L as Rgb, Lt as isBlankCondition, M as batchResults, Mt as WireGroup, N as eventLogFilters, Nt as condition, O as ThreadAuthor, Ot as LogicalOperator, P as hierarchyEntity, Pt as emptyFilter, Q as NATIVE_STATUSES, Qt as ValueShape, R as StatusGlyph, Rt as isEmptyFilter, S as SummarizeResult, St as undeclaredField, T as SummaryGrouping, Tt as FilterCondition, U as foregroundFor, Ut as DATA_TYPES, V as StatusPaint, Vt as toFilterArray, W as intersectStatuses, Wt as DataType, X as statusPaint, Xt as TimeUnit, Y as statusLabel, Yt as TIME_UNITS, Z as usableStatuses, Zt as VALUE_SHAPE, _ as SearchOptions, _t as fieldSchemaOverride, a as EntityTypeInfo, an as operatorsFor, at as StockIconSource, b as SgClient, bt as statusFieldFor, c as EventLogResult, ct as spriteStyle, d as HierarchyPath, dt as FIELD_SCHEMA_OVERRIDES, en as isFilterable, et as STOCK_ICON_CELLS, f as HierarchyRef, ft as FieldSchema, g as RestClientOptions, gt as displayNameOf, h as RestClient, ht as RawProperty, i as EntityRow, in as isTemporalType, it as SpriteCell, j as UploadResult, jt as WireCondition, k as ThreadRow, kt as Scalar, l as FollowingOptions, lt as stockIconSource, m as RawHierarchyNode, mt as RawFieldsResponse, n as BatchResult, nn as isNumericType, nt as STOCK_ICON_KEYS, o as EventLogEntry, on as supportsOperator, ot as isNativeStatus, p as RawBatchRow, pt as RawFieldSchema, q as rgbToCss, qt as OPERATORS_BY_TYPE, r as EVENT_LOG_FIELDS, rn as isOperator, rt as STOCK_SPRITE_PATH, s as EventLogOptions, st as nativeStatus, t as BatchRequest, tn as isLinkType, tt as STOCK_ICON_DATA_URLS, u as HierarchyNode, ut as DISPLAY_NAME_FIELDS, v as SearchResult, vt as normalizeField, w as SummaryGroup, wt as EntityRef, x as SummarizeOptions, xt as statusFieldNameFor, y as SgApiError, yt as normalizeFields, z as StatusIcon, zt as referencedPaths } from "./client-B2PCySg9.mjs";
2
2
  //#region src/field-icons.d.ts
3
3
  /** The catch-all glyph, used for a data type this build does not know. */
4
4
  export declare const DEFAULT_FIELD_ICON = "file-text";
@@ -1772,6 +1772,9 @@ export declare class ProxyClient implements SgClient {
1772
1772
  }): Promise<TextSearchRow[]>;
1773
1773
  statuses(): Promise<StatusRecord[]>;
1774
1774
  create(entityType: string, body: Record<string, unknown>): Promise<EntityRow>;
1775
+ delete(entityType: string, id: number): Promise<void>;
1776
+ revive(entityType: string, id: number): Promise<boolean>;
1777
+ batch(requests: BatchRequest[]): Promise<BatchResult[]>;
1775
1778
  /** The bytes cross the protocol base64-encoded; the handler runs the three calls. */
1776
1779
  upload(entityType: string, id: number, file: UploadFile): Promise<UploadResult>;
1777
1780
  update(entityType: string, id: number, patch: Record<string, unknown>): Promise<EntityRow>;
@@ -1785,7 +1788,7 @@ export declare class ProxyClient implements SgClient {
1785
1788
  //#endregion
1786
1789
  //#region src/proxy-handler.d.ts
1787
1790
  /** The methods the protocol carries, one POST each. */
1788
- export declare const PROXY_METHODS: readonly ['entityTypes', 'fields', 'fieldWithProject', 'search', 'textSearch', 'statuses', 'create', 'update', 'upload', 'hierarchyExpand', 'hierarchySearch', 'summarize', 'threadContents', 'eventLog', 'following'];
1791
+ export declare const PROXY_METHODS: readonly ['entityTypes', 'fields', 'fieldWithProject', 'search', 'textSearch', 'statuses', 'create', 'update', 'upload', 'hierarchyExpand', 'hierarchySearch', 'summarize', 'threadContents', 'eventLog', 'following', 'delete', 'revive', 'batch'];
1789
1792
  export type ProxyMethod = (typeof PROXY_METHODS)[number];
1790
1793
  /** An error as it crosses the wire. `ProxyClient` rebuilds an `SgApiError` from it. */
1791
1794
  export interface ProxyError {
@@ -2723,4 +2726,4 @@ export declare function markOverflow(list: HTMLElement | null | undefined): bool
2723
2726
  */
2724
2727
  export declare function watchOverflow(list: HTMLElement | null | undefined): () => void;
2725
2728
  //#endregion
2726
- export { ConditionValue, DATA_TYPES, DISPLAY_NAME_FIELDS, DataType, EVENT_LOG_FIELDS, EntityRef, EntityRow, EntityTypeInfo, EventLogEntry, EventLogOptions, EventLogResult, FIELD_SCHEMA_OVERRIDES, FieldSchema, FilterCondition, FilterGroup, FilterNode, FollowingOptions, HierarchyNode, HierarchyPath, HierarchyRef, LogicalOperator, NATIVE_STATUSES, NEGATING_OPERATORS, NativeStatus, OPERATORS, OPERATORS_BY_TYPE, Operator, RawFieldSchema, RawFieldsResponse, RawHierarchyNode, RawProperty, RestClient, RestClientOptions, Rgb, STOCK_ICON_CELLS, STOCK_ICON_DATA_URLS, STOCK_ICON_KEYS, STOCK_SPRITE_PATH, Scalar, SearchOptions, SearchResult, SgApiError, SgClient, SpriteCell, StatusGlyph, StatusIcon, StatusOption, StatusPaint, StatusRecord, StockIconSource, SummarizeOptions, SummarizeResult, SummaryField, SummaryGroup, SummaryGrouping, SummaryType, TIME_UNITS, TextSearchFilter, TextSearchRow, ThreadAuthor, ThreadRow, TimeUnit, UploadFile, UploadResult, VALUE_SHAPE, ValueShape, WireCondition, WireGroup, condition, displayNameOf, emptyFilter, eventLogFilters, fieldSchemaOverride, foregroundFor, fromWire, group, hierarchyEntity, intersectStatuses, isBlankCondition, isDataType, isEmptyFilter, isFilterable, isLinkType, isNativeStatus, isNumericType, isOperator, isTemporalType, nativeStatus, normalizeEventLogEntry, normalizeField, normalizeFields, normalizeHierarchyNode, operatorsFor, parseBgColor, referencedPaths, relativeLuminance, rgbToCss, spriteStyle, statusFieldFor, statusFieldNameFor, statusGlyph, statusLabel, statusPaint, stockIconSource, supportsOperator, toApi3Hash, toFilterArray, undeclaredField, usableStatuses, walk };
2729
+ export { BatchRequest, BatchResult, ConditionValue, DATA_TYPES, DISPLAY_NAME_FIELDS, DataType, EVENT_LOG_FIELDS, EntityRef, EntityRow, EntityTypeInfo, EventLogEntry, EventLogOptions, EventLogResult, FIELD_SCHEMA_OVERRIDES, FieldSchema, FilterCondition, FilterGroup, FilterNode, FollowingOptions, HierarchyNode, HierarchyPath, HierarchyRef, LogicalOperator, NATIVE_STATUSES, NEGATING_OPERATORS, NativeStatus, OPERATORS, OPERATORS_BY_TYPE, Operator, RawBatchRow, RawFieldSchema, RawFieldsResponse, RawHierarchyNode, RawProperty, RestClient, RestClientOptions, Rgb, STOCK_ICON_CELLS, STOCK_ICON_DATA_URLS, STOCK_ICON_KEYS, STOCK_SPRITE_PATH, Scalar, SearchOptions, SearchResult, SgApiError, SgClient, SpriteCell, StatusGlyph, StatusIcon, StatusOption, StatusPaint, StatusRecord, StockIconSource, SummarizeOptions, SummarizeResult, SummaryField, SummaryGroup, SummaryGrouping, SummaryType, TIME_UNITS, TextSearchFilter, TextSearchRow, ThreadAuthor, ThreadRow, TimeUnit, UploadFile, UploadResult, VALUE_SHAPE, ValueShape, WireCondition, WireGroup, batchResults, condition, displayNameOf, emptyFilter, eventLogFilters, fieldSchemaOverride, foregroundFor, fromWire, group, hierarchyEntity, intersectStatuses, isBlankCondition, isDataType, isEmptyFilter, isFilterable, isLinkType, isNativeStatus, isNumericType, isOperator, isTemporalType, nativeStatus, normalizeEventLogEntry, normalizeField, normalizeFields, normalizeHierarchyNode, operatorsFor, parseBgColor, referencedPaths, relativeLuminance, rgbToCss, spriteStyle, statusFieldFor, statusFieldNameFor, statusGlyph, statusLabel, statusPaint, stockIconSource, supportsOperator, toApi3Hash, toFilterArray, undeclaredField, usableStatuses, walk };
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { A as OPERATORS, B as operatorsFor, C as isEmptyFilter, D as walk, E as toFilterArray, F as isFilterable, I as isLinkType, L as isNumericType, M as TIME_UNITS, N as VALUE_SHAPE, O as DATA_TYPES, P as isDataType, R as isOperator, S as isBlankCondition, T as toApi3Hash, V as supportsOperator, a as hierarchyEntity, b as fromWire, c as DISPLAY_NAME_FIELDS, d as fieldSchemaOverride, f as normalizeField, g as undeclaredField, h as statusFieldNameFor, i as eventLogFilters, j as OPERATORS_BY_TYPE, k as NEGATING_OPERATORS, l as FIELD_SCHEMA_OVERRIDES, m as statusFieldFor, n as RestClient, o as normalizeEventLogEntry, p as normalizeFields, r as SgApiError, s as normalizeHierarchyNode, t as EVENT_LOG_FIELDS, u as displayNameOf, v as condition, w as referencedPaths, x as group, y as emptyFilter, z as isTemporalType } from "./client-DwN_-UuF.mjs";
1
+ import { A as NEGATING_OPERATORS, B as isTemporalType, C as isBlankCondition, D as toFilterArray, E as toApi3Hash, F as isDataType, H as supportsOperator, I as isFilterable, L as isLinkType, M as OPERATORS_BY_TYPE, N as TIME_UNITS, O as walk, P as VALUE_SHAPE, R as isNumericType, S as group, T as referencedPaths, V as operatorsFor, _ as undeclaredField, a as eventLogFilters, b as emptyFilter, c as normalizeHierarchyNode, d as displayNameOf, f as fieldSchemaOverride, g as statusFieldNameFor, h as statusFieldFor, i as batchResults, j as OPERATORS, k as DATA_TYPES, l as DISPLAY_NAME_FIELDS, m as normalizeFields, n as RestClient, o as hierarchyEntity, p as normalizeField, r as SgApiError, s as normalizeEventLogEntry, t as EVENT_LOG_FIELDS, u as FIELD_SCHEMA_OVERRIDES, w as isEmptyFilter, x as fromWire, y as condition, z as isOperator } from "./client-BdePDloQ.mjs";
2
2
  //#region src/field-icons.ts
3
3
  const ICONS = {
4
4
  text: "type",
@@ -3700,6 +3700,21 @@ function createQueryCache(client, options = {}) {
3700
3700
  function invalidateThreads() {
3701
3701
  drop("threadContents");
3702
3702
  }
3703
+ /**
3704
+ * A delete retires and unlinks rows of other types, a Shot's Versions (probe 060)
3705
+ * and a Task's dependencies, `Version.sg_task` and `PublishedFile.task`, and a
3706
+ * revive puts them back (089_task_delete_side_effects). Every row read goes.
3707
+ */
3708
+ function invalidateAllRows() {
3709
+ for (const prefix of [
3710
+ "search",
3711
+ "textSearch",
3712
+ "summarize",
3713
+ "threadContents",
3714
+ "hierarchyExpand",
3715
+ "hierarchySearch"
3716
+ ]) drop(prefix);
3717
+ }
3703
3718
  return {
3704
3719
  entityTypes() {
3705
3720
  return run("entityTypes", [], () => client.entityTypes());
@@ -3768,6 +3783,22 @@ function createQueryCache(client, options = {}) {
3768
3783
  invalidateThreads();
3769
3784
  return row;
3770
3785
  },
3786
+ async delete(entityType, id) {
3787
+ await client.delete(entityType, id);
3788
+ invalidateAllRows();
3789
+ },
3790
+ async revive(entityType, id) {
3791
+ const revived = await client.revive(entityType, id);
3792
+ invalidateAllRows();
3793
+ return revived;
3794
+ },
3795
+ async batch(requests) {
3796
+ const results = await client.batch(requests);
3797
+ if (requests.some((r) => r.request_type === "delete")) invalidateAllRows();
3798
+ else for (const type of new Set(requests.map((r) => r.entity))) invalidateSearches(type);
3799
+ invalidateThreads();
3800
+ return results;
3801
+ },
3771
3802
  invalidate(prefix) {
3772
3803
  if (prefix === void 0) {
3773
3804
  entries.clear();
@@ -4689,7 +4720,10 @@ const PROXY_METHODS = [
4689
4720
  "summarize",
4690
4721
  "threadContents",
4691
4722
  "eventLog",
4692
- "following"
4723
+ "following",
4724
+ "delete",
4725
+ "revive",
4726
+ "batch"
4693
4727
  ];
4694
4728
  function isProxyMethod(value) {
4695
4729
  return PROXY_METHODS.includes(value);
@@ -4739,6 +4773,12 @@ function entityRef(value, name) {
4739
4773
  id: num(ref.id, `${name}.id`)
4740
4774
  };
4741
4775
  }
4776
+ /** The list is passed on as sent; the API names what is wrong with a request (recipes/002). */
4777
+ function requestList(value, name) {
4778
+ if (!Array.isArray(value)) throw new BadRequest(`'${name}' must be a list`);
4779
+ for (const [i, request] of value.entries()) obj(request, `${name}[${i}]`);
4780
+ return value;
4781
+ }
4742
4782
  function str(value, name) {
4743
4783
  if (typeof value !== "string" || value.length === 0) throw new BadRequest(`'${name}' must be a non-empty string`);
4744
4784
  return value;
@@ -4799,6 +4839,9 @@ function call(client, method, p) {
4799
4839
  case "threadContents": return client.threadContents(num(p.noteId, "noteId"), p.entityFields ?? void 0);
4800
4840
  case "eventLog": return client.eventLog(p.options ?? {});
4801
4841
  case "following": return client.following(num(p.userId, "userId"), p.options ?? {});
4842
+ case "delete": return client.delete(str(p.entityType, "entityType"), num(p.id, "id")).then(() => null);
4843
+ case "revive": return client.revive(str(p.entityType, "entityType"), num(p.id, "id"));
4844
+ case "batch": return client.batch(requestList(p.requests, "requests"));
4802
4845
  }
4803
4846
  }
4804
4847
  //#endregion
@@ -4878,6 +4921,21 @@ var ProxyClient = class {
4878
4921
  body
4879
4922
  });
4880
4923
  }
4924
+ async delete(entityType, id) {
4925
+ await this.post("delete", {
4926
+ entityType,
4927
+ id
4928
+ });
4929
+ }
4930
+ revive(entityType, id) {
4931
+ return this.post("revive", {
4932
+ entityType,
4933
+ id
4934
+ });
4935
+ }
4936
+ batch(requests) {
4937
+ return this.post("batch", { requests });
4938
+ }
4881
4939
  /** The bytes cross the protocol base64-encoded; the handler runs the three calls. */
4882
4940
  upload(entityType, id, file) {
4883
4941
  return this.post("upload", {
@@ -7929,4 +7987,4 @@ function watchOverflow(list) {
7929
7987
  };
7930
7988
  }
7931
7989
  //#endregion
7932
- export { AppSessionError, COLOR_SENTINEL, DATA_TYPES, DEFAULT_FIELD_ICON, DISPLAY_NAME_FIELDS, ELLIPSIS_CHIPS, ERROR_LABEL, EVENT_LOG_FIELDS, FIELD_ICON_NAMES, FIELD_SCHEMA_OVERRIDES, FLOAT_PRECISION, HIERARCHY_LEAF_LIMIT, INT32_MAX, INT32_MIN, LOADING_LABEL, NAME_HUES, NATIVE_STATUSES, NEGATING_OPERATORS, NOTHING_CHOSEN_LABEL, NO_MATCH_LABEL, NO_ROWS_LABEL, OPERATORS, OPERATORS_BY_TYPE, OPERATOR_LABELS, OVERFLOW_END_VAR, OVERFLOW_START_VAR, PATH_SEPARATOR, PRESS_WINDOW_MS, PROJECT_PICKER_FIELDS, PROXY_METHODS, ProxyClient, RestClient, SCROLL_THRESHOLD, SEARCHING_LABEL, SEARCH_DEBOUNCE_MS, SEARCH_PAGE_SIZE, SORTABLE_HANDLE_ATTRIBUTE, SORTABLE_ID_ATTRIBUTE, STOCK_ICON_CELLS, STOCK_ICON_DATA_URLS, STOCK_ICON_KEYS, STOCK_SPRITE_PATH, SgApiError, THUMBNAIL_PENDING_PATH, TIME_UNITS, TIME_UNIT_LABELS, TREE_STATUS_FIELDS, UNRESOLVED_PATH_LABEL, USER_PICKER_FIELDS, VALUE_SHAPE, appendAt, applyPreset, asCollapseState, asFilterGroup, base64ToBytes, breadcrumb, bytesToBase64, canLoadNext, cellValue, clearableForField, collapseAll, collapseStateFrom, collapsedKeys, collectionBottom, collectionView, colorToHex, condition, conditionArity, conditionList, conditionParts, conditionValues, contextFromClient, countActiveConditions, countConditions, countLine, createEntitySearch, createEntitySource, createProxyHandler, createQueryCache, createSchemaService, createSessionTokenAuth, createSgContext, createSortable, createSortableController, createStatusService, createTree, currentPlatform, currentType, defaultCondition, defaultValueFor, deriveFieldOptions, describeCondition, describeEntityCard, describePaging, displayNameOf, editorKindFor, editorNeedsContext, editorPlacementFor, emptyFilter, emptyValueFor, entityCardFields, entityCardSlot, entityDetailUrl, entityKey, entityTypeOptions, errorText, eventLogFilters, expandAll, facetCounts, facetLists, facetPresets, facetScopes, facetShape, facetValues, facetValuesFromGroups, fieldOperators, fieldPathOf, fieldSchemaOverride, fieldText, fileHref, fileNameFromUrl, filterEntityTypes, filterableFields, findCondition, findFacet, firstEnabledIndex, fitChips, flattenRow, focusChip, foregroundFor, formatCurrency, formatDate, formatDateTime, formatDuration, formatFloat, formatNumberInput, formatPercent, formatTimecode, friendlyFieldPath, fromApiDateTime, fromSortString, fromWire, group, groupKeyText, groupRows, groupRowsKeyed, hasFailedPage, hasMorePage, hierarchyEntity, hierarchyLoader, hierarchySearcher, hydrate, iconNameFor, idsForRefs, imageState, initialsOf, intersectStatuses, isBareRef, isBlankCondition, isCollapsed, isDataType, isEditableType, isEmptyFilter, isEmptyValue, isFilterable, isGroupingRefusal, isHiddenPath, isLinkType, isNativeStatus, isNumericType, isOperator, isParseError, isPinned, isProxyMethod, isSortable, isTemporalType, isValidListValue, isoDay, isoDayParts, listStatus, loadEntityCard, loadsOnArrowDown, markOverflow, matchRuns, matchesEveryWord, measureSortable, mergeFilters, moveAt, moveFieldPath, nameColorIndex, nameHue, nameSearchFilter, nativeStatus, nextEnabledIndex, nodeAt, normalizeEventLogEntry, normalizeField, normalizeFields, normalizeHierarchyNode, normalizeSiteUrl, numberDigits, numberDraft, numberSteps, numberWire, operatorLabel, operatorMenu, operatorsFor, overflowEdges, parseBgColor, parseColorInput, parseDurationInput, parseFloatInput, parseInteger, parseNumberInput, parseTextInput, parseTimecodeInput, parseUrlInput, pathLabel, pathOf, pathRefs, pathTypes, pickerKeyIntent, placeholderName, playSortableFlip, pollAppSession, preferencesOf, prependRecent, presetById, presetIdOf, presetsFor, pressGate, projectOfPath, projectPickerFilters, pruneFilterToFields, queryPlan, reachedEnd, referencedPaths, refsForIds, relativeFrom, relativeLuminance, relativeOperator, relativeValue, relativeWindow, removeAt, renderKindFor, replaceAt, requestAppSession, requestGate, resolveColumns, resolveFieldPathOptions, resolveRowFields, resolveTreeFields, rgbToCss, rowCode, rowFields, rowIdOf, rowIsDisabled, rowKey, rowSecondary, rowSubLabel, rowThumbnail, sameCollapse, sameFilters, sameIds, sameRefs, sameSort, scopeToProject, scrollHighlightedIntoView, searchFieldOptions, searchFieldPathOptions, searchKeyIntent, searchTypeMap, searchView, searchWords, secondaryType, selectableRefs, selectionState, serializeSort, setFacet, setFacetPreset, settleStep, shouldLoadNext, sortableAnnouncements, sortableDropIndex, sortableFields, sortableScrollStep, sortableStride, sourceModeFor, spriteStyle, stateLine, statusFieldFor, statusFieldNameFor, statusGlyph, statusLabel, statusPaint, stepNumber, stockIconSource, storedNumber, subLabelType, summariseSelection, supportsEmpty, supportsOperator, thumbnailField, timeUnitField, timeUnitLabel, timeZoneName, toApi3Hash, toApiDate, toApiDateTime, toApiFloat, toColumn, toFilterArray, toRef, toSearchHit, toSortKeys, toSortSpecs, toSortString, toWireGroup, toggleCollapsed, toggleFieldPath, toggleId, toggleRef, traversalTargets, undeclaredField, unformatNumberInput, urlLink, usableStatuses, userPickerFilters, userPickerSearchFields, userPickerSubLabel, userPickerTypes, userSearchFields, validateCondition, valueArity, valueEditorFor, walk, watchHighlight, watchOverflow, withAddedListValue, withListValue, withRelativeWindow, withSelectedPinned, withoutListValue, withoutPaths };
7990
+ export { AppSessionError, COLOR_SENTINEL, DATA_TYPES, DEFAULT_FIELD_ICON, DISPLAY_NAME_FIELDS, ELLIPSIS_CHIPS, ERROR_LABEL, EVENT_LOG_FIELDS, FIELD_ICON_NAMES, FIELD_SCHEMA_OVERRIDES, FLOAT_PRECISION, HIERARCHY_LEAF_LIMIT, INT32_MAX, INT32_MIN, LOADING_LABEL, NAME_HUES, NATIVE_STATUSES, NEGATING_OPERATORS, NOTHING_CHOSEN_LABEL, NO_MATCH_LABEL, NO_ROWS_LABEL, OPERATORS, OPERATORS_BY_TYPE, OPERATOR_LABELS, OVERFLOW_END_VAR, OVERFLOW_START_VAR, PATH_SEPARATOR, PRESS_WINDOW_MS, PROJECT_PICKER_FIELDS, PROXY_METHODS, ProxyClient, RestClient, SCROLL_THRESHOLD, SEARCHING_LABEL, SEARCH_DEBOUNCE_MS, SEARCH_PAGE_SIZE, SORTABLE_HANDLE_ATTRIBUTE, SORTABLE_ID_ATTRIBUTE, STOCK_ICON_CELLS, STOCK_ICON_DATA_URLS, STOCK_ICON_KEYS, STOCK_SPRITE_PATH, SgApiError, THUMBNAIL_PENDING_PATH, TIME_UNITS, TIME_UNIT_LABELS, TREE_STATUS_FIELDS, UNRESOLVED_PATH_LABEL, USER_PICKER_FIELDS, VALUE_SHAPE, appendAt, applyPreset, asCollapseState, asFilterGroup, base64ToBytes, batchResults, breadcrumb, bytesToBase64, canLoadNext, cellValue, clearableForField, collapseAll, collapseStateFrom, collapsedKeys, collectionBottom, collectionView, colorToHex, condition, conditionArity, conditionList, conditionParts, conditionValues, contextFromClient, countActiveConditions, countConditions, countLine, createEntitySearch, createEntitySource, createProxyHandler, createQueryCache, createSchemaService, createSessionTokenAuth, createSgContext, createSortable, createSortableController, createStatusService, createTree, currentPlatform, currentType, defaultCondition, defaultValueFor, deriveFieldOptions, describeCondition, describeEntityCard, describePaging, displayNameOf, editorKindFor, editorNeedsContext, editorPlacementFor, emptyFilter, emptyValueFor, entityCardFields, entityCardSlot, entityDetailUrl, entityKey, entityTypeOptions, errorText, eventLogFilters, expandAll, facetCounts, facetLists, facetPresets, facetScopes, facetShape, facetValues, facetValuesFromGroups, fieldOperators, fieldPathOf, fieldSchemaOverride, fieldText, fileHref, fileNameFromUrl, filterEntityTypes, filterableFields, findCondition, findFacet, firstEnabledIndex, fitChips, flattenRow, focusChip, foregroundFor, formatCurrency, formatDate, formatDateTime, formatDuration, formatFloat, formatNumberInput, formatPercent, formatTimecode, friendlyFieldPath, fromApiDateTime, fromSortString, fromWire, group, groupKeyText, groupRows, groupRowsKeyed, hasFailedPage, hasMorePage, hierarchyEntity, hierarchyLoader, hierarchySearcher, hydrate, iconNameFor, idsForRefs, imageState, initialsOf, intersectStatuses, isBareRef, isBlankCondition, isCollapsed, isDataType, isEditableType, isEmptyFilter, isEmptyValue, isFilterable, isGroupingRefusal, isHiddenPath, isLinkType, isNativeStatus, isNumericType, isOperator, isParseError, isPinned, isProxyMethod, isSortable, isTemporalType, isValidListValue, isoDay, isoDayParts, listStatus, loadEntityCard, loadsOnArrowDown, markOverflow, matchRuns, matchesEveryWord, measureSortable, mergeFilters, moveAt, moveFieldPath, nameColorIndex, nameHue, nameSearchFilter, nativeStatus, nextEnabledIndex, nodeAt, normalizeEventLogEntry, normalizeField, normalizeFields, normalizeHierarchyNode, normalizeSiteUrl, numberDigits, numberDraft, numberSteps, numberWire, operatorLabel, operatorMenu, operatorsFor, overflowEdges, parseBgColor, parseColorInput, parseDurationInput, parseFloatInput, parseInteger, parseNumberInput, parseTextInput, parseTimecodeInput, parseUrlInput, pathLabel, pathOf, pathRefs, pathTypes, pickerKeyIntent, placeholderName, playSortableFlip, pollAppSession, preferencesOf, prependRecent, presetById, presetIdOf, presetsFor, pressGate, projectOfPath, projectPickerFilters, pruneFilterToFields, queryPlan, reachedEnd, referencedPaths, refsForIds, relativeFrom, relativeLuminance, relativeOperator, relativeValue, relativeWindow, removeAt, renderKindFor, replaceAt, requestAppSession, requestGate, resolveColumns, resolveFieldPathOptions, resolveRowFields, resolveTreeFields, rgbToCss, rowCode, rowFields, rowIdOf, rowIsDisabled, rowKey, rowSecondary, rowSubLabel, rowThumbnail, sameCollapse, sameFilters, sameIds, sameRefs, sameSort, scopeToProject, scrollHighlightedIntoView, searchFieldOptions, searchFieldPathOptions, searchKeyIntent, searchTypeMap, searchView, searchWords, secondaryType, selectableRefs, selectionState, serializeSort, setFacet, setFacetPreset, settleStep, shouldLoadNext, sortableAnnouncements, sortableDropIndex, sortableFields, sortableScrollStep, sortableStride, sourceModeFor, spriteStyle, stateLine, statusFieldFor, statusFieldNameFor, statusGlyph, statusLabel, statusPaint, stepNumber, stockIconSource, storedNumber, subLabelType, summariseSelection, supportsEmpty, supportsOperator, thumbnailField, timeUnitField, timeUnitLabel, timeZoneName, toApi3Hash, toApiDate, toApiDateTime, toApiFloat, toColumn, toFilterArray, toRef, toSearchHit, toSortKeys, toSortSpecs, toSortString, toWireGroup, toggleCollapsed, toggleFieldPath, toggleId, toggleRef, traversalTargets, undeclaredField, unformatNumberInput, urlLink, usableStatuses, userPickerFilters, userPickerSearchFields, userPickerSubLabel, userPickerTypes, userSearchFields, validateCondition, valueArity, valueEditorFor, walk, watchHighlight, watchOverflow, withAddedListValue, withListValue, withRelativeWindow, withSelectedPinned, withoutListValue, withoutPaths };
package/dist/mock.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { D as UploadFile, E as ThreadRow, Et as TextSearchFilter, O as UploadResult, R as StatusRecord, _ as SgClient, a as EventLogOptions, bt as EntityRef, c as HierarchyNode, ct as FieldSchema, h as SearchResult, l as HierarchyPath, m as SearchOptions, n as EntityRow, o as EventLogResult, r as EntityTypeInfo, s as FollowingOptions, v as SummarizeOptions, w as TextSearchRow, y as SummarizeResult } from "./client-BhHppzTV.mjs";
1
+ import { A as UploadFile, At as TextSearchFilter, D as TextSearchRow, H as StatusRecord, S as SummarizeResult, _ as SearchOptions, a as EntityTypeInfo, b as SgClient, c as EventLogResult, d as HierarchyPath, ft as FieldSchema, i as EntityRow, j as UploadResult, k as ThreadRow, l as FollowingOptions, n as BatchResult, s as EventLogOptions, t as BatchRequest, u as HierarchyNode, v as SearchResult, wt as EntityRef, x as SummarizeOptions } from "./client-B2PCySg9.mjs";
2
2
  //#region src/mock.d.ts
3
3
  /** What the next call should throw instead of answering. */
4
4
  export interface MockFailure {
@@ -34,6 +34,11 @@ export declare class MockClient implements SgClient {
34
34
  private readonly latencyMs;
35
35
  private readonly clock;
36
36
  private pendingFailure;
37
+ /** Rows a delete retired, by `Type:id`. Reads skip them and a revive puts them back. */
38
+ private readonly retired;
39
+ /** Rows a batch created with no `project`: no read reaches them, a delete does (report 001). */
40
+ private readonly unreadable;
41
+ private deletes;
37
42
  constructor(options?: MockClientOptions);
38
43
  /**
39
44
  * Make the next call reject with an `SgApiError`, for demoing an error state.
@@ -53,6 +58,7 @@ export declare class MockClient implements SgClient {
53
58
  number?: number;
54
59
  }): Promise<TextSearchRow[]>;
55
60
  update(entityType: string, id: number, patch: Record<string, unknown>): Promise<EntityRow>;
61
+ private updateRow;
56
62
  /**
57
63
  * Counts without paging rows.
58
64
  *
@@ -104,6 +110,29 @@ export declare class MockClient implements SgClient {
104
110
  * entity_types/Note). Nothing is unique, so two identical creates make two rows.
105
111
  */
106
112
  create(entityType: string, body: Record<string, unknown>): Promise<EntityRow>;
113
+ /**
114
+ * A create, alone or inside a batch. The batch skips the `project` check and stores
115
+ * the row where no read reaches it (report 001), and it spells an unknown field its
116
+ * own way (recipes/002).
117
+ */
118
+ private createRow;
119
+ /**
120
+ * Retire one row: reads skip it and a second delete is 404 (delete_entity_type_id).
121
+ * The mock retires only the row; what a delete does to rows that link to it
122
+ * (probe 060, 089_task_delete_side_effects) is not modelled.
123
+ */
124
+ delete(entityType: string, id: number): Promise<void>;
125
+ private deleteRow;
126
+ /** Bring a retired row back with its values; `false` for a row that is already live (post_entity_type_id). */
127
+ revive(entityType: string, id: number): Promise<boolean>;
128
+ /**
129
+ * Apply the requests in order, and undo all of them when one fails (recipes/002).
130
+ * A create or update row carries the record under `data`; a delete row is flat.
131
+ */
132
+ batch(requests: BatchRequest[]): Promise<BatchResult[]>;
133
+ private batchOne;
134
+ /** Everything a batch can change, and a function that puts it back. */
135
+ private snapshot;
107
136
  /**
108
137
  * Put a file on a row, as the three-call handshake leaves the site.
109
138
  *
package/dist/mock.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { B as operatorsFor, E as toFilterArray, F as isFilterable, I as isLinkType, L as isNumericType, M as TIME_UNITS, _ as pluralPath, d as fieldSchemaOverride, i as eventLogFilters, k as NEGATING_OPERATORS, o as normalizeEventLogEntry, r as SgApiError, t as EVENT_LOG_FIELDS, u as displayNameOf } from "./client-DwN_-UuF.mjs";
1
+ import { A as NEGATING_OPERATORS, D as toFilterArray, I as isFilterable, L as isLinkType, N as TIME_UNITS, R as isNumericType, V as operatorsFor, a as eventLogFilters, d as displayNameOf, f as fieldSchemaOverride, r as SgApiError, s as normalizeEventLogEntry, t as EVENT_LOG_FIELDS, v as pluralPath } from "./client-BdePDloQ.mjs";
2
2
  //#region src/mock.ts
3
3
  /**
4
4
  * Status vocabularies. `valid_values` is the site's whole vocabulary and is
@@ -2007,6 +2007,11 @@ var MockClient = class {
2007
2007
  latencyMs;
2008
2008
  clock;
2009
2009
  pendingFailure;
2010
+ /** Rows a delete retired, by `Type:id`. Reads skip them and a revive puts them back. */
2011
+ retired = /* @__PURE__ */ new Map();
2012
+ /** Rows a batch created with no `project`: no read reaches them, a delete does (report 001). */
2013
+ unreadable = /* @__PURE__ */ new Map();
2014
+ deletes = 0;
2010
2015
  constructor(options = {}) {
2011
2016
  this.fixtures = buildFixtures(options.seed ?? 1, options.counts ?? {});
2012
2017
  this.latencyMs = options.latencyMs ?? 0;
@@ -2128,6 +2133,9 @@ var MockClient = class {
2128
2133
  }
2129
2134
  async update(entityType, id, patch) {
2130
2135
  await this.gate();
2136
+ return this.updateRow(entityType, id, patch);
2137
+ }
2138
+ updateRow(entityType, id, patch) {
2131
2139
  const spec = this.schemaOf(entityType);
2132
2140
  const row = this.fixtures.index.get(`${entityType}:${id}`);
2133
2141
  if (!row) throw new SgApiError(404, null, `Entity of type [${entityType}] with id=${id} does not exist.`);
@@ -2476,11 +2484,21 @@ var MockClient = class {
2476
2484
  */
2477
2485
  async create(entityType, body) {
2478
2486
  await this.gate();
2487
+ return this.createRow(entityType, body, false);
2488
+ }
2489
+ /**
2490
+ * A create, alone or inside a batch. The batch skips the `project` check and stores
2491
+ * the row where no read reaches it (report 001), and it spells an unknown field its
2492
+ * own way (recipes/002).
2493
+ */
2494
+ createRow(entityType, body, inBatch) {
2479
2495
  const spec = this.schemaOf(entityType);
2480
- if (spec["project"] && body["project"] === void 0) throw new SgApiError(400, null, `API create() missing 'project' attribute: ${JSON.stringify(body)}`);
2496
+ const orphan = spec["project"] !== void 0 && body["project"] === void 0;
2497
+ if (orphan && !inBatch) throw new SgApiError(400, null, `API create() missing 'project' attribute: ${JSON.stringify(body)}`);
2481
2498
  const identity = IDENTITY_FIELD[entityType];
2482
2499
  for (const [name, value] of Object.entries(body)) {
2483
2500
  const field = spec[name];
2501
+ if (!field && inBatch) throw new SgApiError(400, null, `Invalid field value, update failed [2 - Invalid field name: field [${entityType}.${name}] does not exist or user does not have access permission.]`);
2484
2502
  if (!field) throw new SgApiError(400, null, `API create() ${entityType}.${name} doesn't exist.`);
2485
2503
  if (field.editable === false && !field.createOnly) throw new SgApiError(400, null, `API create() ${entityType}.${name} is read only.`);
2486
2504
  this.checkLink("create", entityType, name, field, value);
@@ -2509,6 +2527,10 @@ var MockClient = class {
2509
2527
  id,
2510
2528
  values
2511
2529
  };
2530
+ if (orphan) {
2531
+ this.unreadable.set(`${entityType}:${id}`, row);
2532
+ return this.project(row, spec);
2533
+ }
2512
2534
  const rows = this.fixtures.rows.get(entityType);
2513
2535
  if (rows) rows.push(row);
2514
2536
  else this.fixtures.rows.set(entityType, [row]);
@@ -2517,6 +2539,113 @@ var MockClient = class {
2517
2539
  return this.project(row, spec);
2518
2540
  }
2519
2541
  /**
2542
+ * Retire one row: reads skip it and a second delete is 404 (delete_entity_type_id).
2543
+ * The mock retires only the row; what a delete does to rows that link to it
2544
+ * (probe 060, 089_task_delete_side_effects) is not modelled.
2545
+ */
2546
+ async delete(entityType, id) {
2547
+ await this.gate();
2548
+ this.deleteRow(entityType, id);
2549
+ }
2550
+ deleteRow(entityType, id) {
2551
+ this.schemaOf(entityType);
2552
+ const key = `${entityType}:${id}`;
2553
+ const row = this.fixtures.index.get(key) ?? this.unreadable.get(key);
2554
+ if (!row) throw new SgApiError(404, null, `Entity of type [${entityType}] with id=${id} does not exist.`);
2555
+ this.unreadable.delete(key);
2556
+ this.fixtures.index.delete(key);
2557
+ const rows = this.fixtures.rows.get(entityType) ?? [];
2558
+ const at = rows.indexOf(row);
2559
+ if (at >= 0) rows.splice(at, 1);
2560
+ this.retired.set(key, row);
2561
+ }
2562
+ /** Bring a retired row back with its values; `false` for a row that is already live (post_entity_type_id). */
2563
+ async revive(entityType, id) {
2564
+ await this.gate();
2565
+ this.schemaOf(entityType);
2566
+ const key = `${entityType}:${id}`;
2567
+ if (this.fixtures.index.has(key)) return false;
2568
+ const row = this.retired.get(key);
2569
+ if (!row) throw new SgApiError(404, null, `Entity of type [${entityType}] with id=${id} does not exist.`);
2570
+ this.retired.delete(key);
2571
+ const rows = this.fixtures.rows.get(entityType) ?? [];
2572
+ const at = rows.findIndex((r) => r.id > id);
2573
+ rows.splice(at < 0 ? rows.length : at, 0, row);
2574
+ this.fixtures.rows.set(entityType, rows);
2575
+ this.fixtures.index.set(key, row);
2576
+ return true;
2577
+ }
2578
+ /**
2579
+ * Apply the requests in order, and undo all of them when one fails (recipes/002).
2580
+ * A create or update row carries the record under `data`; a delete row is flat.
2581
+ */
2582
+ async batch(requests) {
2583
+ await this.gate();
2584
+ const restore = this.snapshot();
2585
+ try {
2586
+ return requests.map((request, i) => this.batchOne(request, i));
2587
+ } catch (error) {
2588
+ restore();
2589
+ throw error;
2590
+ }
2591
+ }
2592
+ batchOne(request, i) {
2593
+ const invalid = (source) => new SgApiError(400, { errors: [{
2594
+ status: 400,
2595
+ code: 103,
2596
+ title: "Request Parameters invalid.",
2597
+ source
2598
+ }] }, "Request Parameters invalid.");
2599
+ const kind = request.request_type;
2600
+ if (kind !== "create" && kind !== "update" && kind !== "delete") throw invalid({ requests: { [i]: { request_type: ["request_type must be one of: create, update, delete"] } } });
2601
+ if (!request.entity) throw invalid({ requests: { [i]: { entity: ["entity is missing"] } } });
2602
+ if (!SPECS[request.entity]) throw new SgApiError(400, null, "Invalid entity type: entity type [] does not exist.");
2603
+ const id = "record_id" in request ? request.record_id ?? 0 : 0;
2604
+ if (request.request_type === "delete") {
2605
+ this.deleteRow(request.entity, id);
2606
+ this.deletes += 1;
2607
+ const uuid = `00000000-0000-4000-8000-${this.deletes.toString(16).padStart(12, "0")}`;
2608
+ return {
2609
+ request_type: "delete",
2610
+ type: request.entity,
2611
+ id,
2612
+ uuid,
2613
+ did_delete: true
2614
+ };
2615
+ }
2616
+ const data = request.data;
2617
+ if (data === null || typeof data !== "object") throw invalid({ data: ["data hash containing field/value pairs is required for the given request"] });
2618
+ if (request.request_type === "create") return {
2619
+ request_type: "create",
2620
+ data: this.createRow(request.entity, data, true)
2621
+ };
2622
+ return {
2623
+ request_type: "update",
2624
+ data: this.updateRow(request.entity, id, data)
2625
+ };
2626
+ }
2627
+ /** Everything a batch can change, and a function that puts it back. */
2628
+ snapshot() {
2629
+ const rows = new Map([...this.fixtures.rows].map(([type, list]) => [type, [...list]]));
2630
+ const index = new Map(this.fixtures.index);
2631
+ const values = new Map([...index.values()].map((row) => [row, structuredClone(row.values)]));
2632
+ const retired = new Map(this.retired);
2633
+ const unreadable = new Map(this.unreadable);
2634
+ const deletes = this.deletes;
2635
+ const refill = (target, from) => {
2636
+ target.clear();
2637
+ for (const [k, v] of from) target.set(k, v);
2638
+ };
2639
+ return () => {
2640
+ refill(this.fixtures.rows, rows);
2641
+ refill(this.fixtures.index, index);
2642
+ for (const [row, saved] of values) row.values = saved;
2643
+ refill(this.retired, retired);
2644
+ refill(this.unreadable, unreadable);
2645
+ this.deletes = deletes;
2646
+ };
2647
+ }
2648
+ /**
2520
2649
  * Put a file on a row, as the three-call handshake leaves the site.
2521
2650
  *
2522
2651
  * The field in the path picks the kind: `image` a Thumbnail, another field an
@@ -2603,7 +2732,9 @@ var MockClient = class {
2603
2732
  }
2604
2733
  /** The next free id of a type, which is what a create takes. */
2605
2734
  nextId(entityType) {
2606
- return (this.fixtures.rows.get(entityType) ?? []).reduce((max, row) => Math.max(max, row.id), 0) + 1;
2735
+ let taken = (this.fixtures.rows.get(entityType) ?? []).reduce((max, row) => Math.max(max, row.id), 0);
2736
+ for (const row of [...this.retired.values(), ...this.unreadable.values()]) if (row.type === entityType) taken = Math.max(taken, row.id);
2737
+ return taken + 1;
2607
2738
  }
2608
2739
  /** The reverse view of a link the server fills in: a Reply lands in `Note.replies`. */
2609
2740
  linkBack(row) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sg-widgets-core",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Headless, framework-agnostic model of Flow Production Tracking (ShotGrid) fields, filters, statuses and a client adapter, for building widgets.",
5
5
  "keywords": [
6
6
  "shotgrid",