@object-ui/data-objectstack 17.5.0 → 17.7.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.
Files changed (53) hide show
  1. package/CHANGELOG.md +2311 -0
  2. package/README.md +557 -11
  3. package/dist/index.cjs +1663 -463
  4. package/dist/index.d.cts +1780 -518
  5. package/dist/index.d.ts +1780 -518
  6. package/dist/index.js +1648 -457
  7. package/package.json +9 -9
  8. package/dist/tsconfig.tsbuildinfo +0 -1
  9. package/src/aggregate-capability.test.ts +0 -177
  10. package/src/apiAccessDenied.test.ts +0 -233
  11. package/src/appAccessProbe.test.ts +0 -147
  12. package/src/bulkDelete.test.ts +0 -75
  13. package/src/bulkUpdate.test.ts +0 -93
  14. package/src/cache/MetadataCache.test.ts +0 -477
  15. package/src/cache/MetadataCache.ts +0 -289
  16. package/src/cloud-surface-retired-4152.pin.test.ts +0 -171
  17. package/src/connection.test.ts +0 -188
  18. package/src/contracts.ts +0 -115
  19. package/src/deleteView.homes.test.ts +0 -389
  20. package/src/deleteViewContract.types.test.ts +0 -246
  21. package/src/errors.test.ts +0 -426
  22. package/src/errors.ts +0 -293
  23. package/src/exportDownload.test.ts +0 -141
  24. package/src/filter-entry-translation.test.ts +0 -360
  25. package/src/filter-operator-ast-parity.test.ts +0 -135
  26. package/src/getObjectSchema.test.ts +0 -145
  27. package/src/httpFailureLogging.test.ts +0 -109
  28. package/src/importJob.test.ts +0 -71
  29. package/src/index.ts +0 -4442
  30. package/src/integration.ts +0 -200
  31. package/src/listViewOverrides.test.ts +0 -241
  32. package/src/listViews.test.ts +0 -175
  33. package/src/metadata-client.get-envelope.test.ts +0 -171
  34. package/src/metadata-client.saveAdvisories.test.ts +0 -226
  35. package/src/metadata-client.test.ts +0 -303
  36. package/src/metadata-client.ts +0 -1046
  37. package/src/occ.test.ts +0 -101
  38. package/src/onMutation.test.ts +0 -589
  39. package/src/onSaveAdvisory.test.ts +0 -311
  40. package/src/onWriteWarning.test.ts +0 -273
  41. package/src/orderby-serialization.test.ts +0 -115
  42. package/src/queryDataset.test.ts +0 -543
  43. package/src/searchAll.test.ts +0 -120
  44. package/src/security.ts +0 -243
  45. package/src/spec-symbol-batch6.test.ts +0 -326
  46. package/src/studio.ts +0 -152
  47. package/src/updateView.draft.test.ts +0 -242
  48. package/src/userState.test.ts +0 -315
  49. package/src/userState.ts +0 -238
  50. package/src/v3-compat.test.ts +0 -229
  51. package/src/validation-error.test.ts +0 -94
  52. package/src/viewCacheInvalidation.pin.test.ts +0 -260
  53. package/src/viewInvalidationSeam.pin.test.ts +0 -162
@@ -1,109 +0,0 @@
1
- /**
2
- * objectui#4042 half 2 — a request failure must SAY which request failed.
3
- *
4
- * `@objectstack/client` reports every non-2xx as
5
- * `logger.error("HTTP request failed", undefined, { method, url, status, error })`.
6
- * The console's logger used to forward that verbatim, so the identifying
7
- * fields lived only in the third argument: anything that flattens a console
8
- * record to text rendered them `[object Object]` / `Object`, and a screenful of
9
- * failures told you nothing about which URL or which status. These tests pin
10
- * that the message string itself now carries method + url + status (+ code).
11
- */
12
-
13
- import { describe, it, expect, vi, afterEach } from 'vitest';
14
- import { formatHttpFailureMessage, createQuietHttpLogger } from './index';
15
-
16
- const META_401 = {
17
- method: 'GET',
18
- url: '/api/v1/meta/object',
19
- status: 401,
20
- error: { code: 'UNAUTHORIZED', message: 'Unauthorized' },
21
- };
22
-
23
- describe('formatHttpFailureMessage', () => {
24
- it('folds method, url and status into the message', () => {
25
- expect(formatHttpFailureMessage('HTTP request failed', META_401)).toBe(
26
- 'HTTP request failed: GET /api/v1/meta/object -> 401 [UNAUTHORIZED]',
27
- );
28
- });
29
-
30
- it('omits the code when the body carries none', () => {
31
- expect(
32
- formatHttpFailureMessage('HTTP request failed', {
33
- method: 'POST',
34
- url: '/api/v1/data/task',
35
- status: 500,
36
- }),
37
- ).toBe('HTTP request failed: POST /api/v1/data/task -> 500');
38
- });
39
-
40
- it('reads the `statusCode` spelling too', () => {
41
- expect(
42
- formatHttpFailureMessage('HTTP request failed', { url: '/api/v1/meta/app', statusCode: 403 }),
43
- ).toBe('HTTP request failed: GET /api/v1/meta/app -> 403');
44
- });
45
-
46
- it('returns null — not a husk — when meta identifies nothing', () => {
47
- expect(formatHttpFailureMessage('HTTP request failed', undefined)).toBeNull();
48
- expect(formatHttpFailureMessage('HTTP request failed', {})).toBeNull();
49
- expect(formatHttpFailureMessage('HTTP request failed', { error: { message: 'boom' } })).toBeNull();
50
- });
51
-
52
- it('never yields the un-diagnosable string the card reported', () => {
53
- const line = formatHttpFailureMessage('HTTP request failed', META_401)!;
54
- expect(line).not.toContain('[object Object]');
55
- expect(line).toContain('/api/v1/meta/object');
56
- expect(line).toContain('401');
57
- });
58
- });
59
-
60
- describe('createQuietHttpLogger', () => {
61
- afterEach(() => vi.restoreAllMocks());
62
-
63
- it('logs a 401 as an error whose FIRST argument identifies the request', () => {
64
- const spy = vi.spyOn(console, 'error').mockImplementation(() => {});
65
- createQuietHttpLogger().error('HTTP request failed', undefined, META_401);
66
-
67
- expect(spy).toHaveBeenCalledTimes(1);
68
- const [message, , meta] = spy.mock.calls[0];
69
- expect(message).toBe('HTTP request failed: GET /api/v1/meta/object -> 401 [UNAUTHORIZED]');
70
- // The structured bag is still handed over for DevTools to expand — the
71
- // string is added alongside it, not instead of it.
72
- expect(meta).toEqual(META_401);
73
- });
74
-
75
- it('keeps demoting an expected 404, and identifies it in the demoted line too', () => {
76
- const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
77
- const debugSpy = vi.spyOn(console, 'debug').mockImplementation(() => {});
78
-
79
- createQuietHttpLogger().error('HTTP request failed', undefined, {
80
- method: 'GET',
81
- url: '/api/v1/data/sys_presence',
82
- status: 404,
83
- error: { code: 'OBJECT_NOT_FOUND' },
84
- });
85
-
86
- expect(errorSpy).not.toHaveBeenCalled();
87
- expect(debugSpy).toHaveBeenCalledTimes(1);
88
- expect(debugSpy.mock.calls[0][0]).toContain('/api/v1/data/sys_presence -> 404');
89
- });
90
-
91
- it('does NOT silence a 401 — only 404-on-an-optional-collection is demoted', () => {
92
- const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
93
- const debugSpy = vi.spyOn(console, 'debug').mockImplementation(() => {});
94
-
95
- // Session expiry mid-use: the console gates doomed requests, it does not
96
- // hide the ones that still fail.
97
- createQuietHttpLogger().error('HTTP request failed', undefined, META_401);
98
-
99
- expect(errorSpy).toHaveBeenCalledTimes(1);
100
- expect(debugSpy).not.toHaveBeenCalled();
101
- });
102
-
103
- it('falls back to the bare message when there is nothing to identify', () => {
104
- const spy = vi.spyOn(console, 'error').mockImplementation(() => {});
105
- createQuietHttpLogger().error('Something broke', undefined, undefined);
106
-
107
- expect(spy.mock.calls[0][0]).toBe('Something broke');
108
- });
109
- });
@@ -1,71 +0,0 @@
1
- /**
2
- * ObjectUI
3
- * Copyright (c) 2024-present ObjectStack Inc.
4
- *
5
- * This source code is licensed under the MIT license found in the
6
- * LICENSE file in the root directory of this source tree.
7
- */
8
-
9
- import { describe, it, expect, vi } from 'vitest';
10
- import { ObjectStackAdapter } from './index';
11
-
12
- /**
13
- * Adapter over a mock client `data` namespace. The async import-job methods are
14
- * thin pass-throughs to the client SDK, so we assert delegation + argument
15
- * shaping, plus graceful degradation when the client lacks the job API.
16
- */
17
- function makeDS(stub: Record<string, any>) {
18
- const ds: any = new ObjectStackAdapter({
19
- baseUrl: 'http://test.local',
20
- fetch: vi.fn(async () => new Response(JSON.stringify({ success: true, data: { capabilities: {}, routes: {} } }), {
21
- status: 200,
22
- headers: { 'Content-Type': 'application/json' },
23
- })),
24
- });
25
- ds.connected = true;
26
- ds.connectionState = 'connected';
27
- ds.client = { data: stub };
28
- return ds;
29
- }
30
-
31
- describe('ObjectStackAdapter async import jobs', () => {
32
- it('createImportJob delegates to client.data.createImportJob', async () => {
33
- const createImportJob = vi.fn().mockResolvedValue({ jobId: 'imp_1', object: 'task', status: 'pending', total: 2 });
34
- const ds = makeDS({ createImportJob });
35
- const req = { format: 'json' as const, rows: [{ a: 1 }, { a: 2 }], writeMode: 'upsert' as const, matchFields: ['a'] };
36
- const res = await ds.createImportJob('task', req);
37
- expect(createImportJob).toHaveBeenCalledTimes(1);
38
- expect(createImportJob.mock.calls[0]).toEqual(['task', req]);
39
- expect(res).toMatchObject({ jobId: 'imp_1', status: 'pending', total: 2 });
40
- });
41
-
42
- it('getImportJobProgress / getImportJobResults / cancelImportJob delegate by jobId', async () => {
43
- const getImportJobProgress = vi.fn().mockResolvedValue({ jobId: 'imp_1', status: 'running', percentComplete: 50 });
44
- const getImportJobResults = vi.fn().mockResolvedValue({ jobId: 'imp_1', status: 'succeeded', results: [], resultsTruncated: false });
45
- const cancelImportJob = vi.fn().mockResolvedValue({ success: true });
46
- const ds = makeDS({ createImportJob: vi.fn(), getImportJobProgress, getImportJobResults, cancelImportJob });
47
-
48
- expect((await ds.getImportJobProgress('imp_1')).percentComplete).toBe(50);
49
- expect(getImportJobProgress).toHaveBeenCalledWith('imp_1');
50
-
51
- expect((await ds.getImportJobResults('imp_1')).resultsTruncated).toBe(false);
52
- expect(getImportJobResults).toHaveBeenCalledWith('imp_1');
53
-
54
- await ds.cancelImportJob('imp_1');
55
- expect(cancelImportJob).toHaveBeenCalledWith('imp_1');
56
- });
57
-
58
- it('listImportJobs forwards filters and returns the jobs array', async () => {
59
- const listImportJobs = vi.fn().mockResolvedValue([{ jobId: 'imp_1', object: 'task', status: 'succeeded' }]);
60
- const ds = makeDS({ createImportJob: vi.fn(), listImportJobs });
61
- const jobs = await ds.listImportJobs({ object: 'task', status: 'succeeded', limit: 10 });
62
- expect(listImportJobs).toHaveBeenCalledWith({ object: 'task', status: 'succeeded', limit: 10 });
63
- expect(jobs).toHaveLength(1);
64
- });
65
-
66
- it('throws UNSUPPORTED_OPERATION when the client lacks the job API', async () => {
67
- const ds = makeDS({ import: vi.fn() }); // sync import only, no createImportJob
68
- await expect(ds.createImportJob('task', { format: 'json', rows: [] })).rejects.toMatchObject({ code: 'UNSUPPORTED_OPERATION' });
69
- await expect(ds.getImportJobProgress('imp_1')).rejects.toMatchObject({ code: 'UNSUPPORTED_OPERATION' });
70
- });
71
- });