Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 131 additions & 0 deletions marklogic.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,29 @@ declare module 'marklogic' {
systemTime?: string;
}

/**
* A timestamp object representing a point in time on the server.
* Used for point-in-time queries and operations.
* @since 2.1.1
*/
export interface Timestamp {
/** The timestamp value as a string */
value: string | null;
}

/**
* Result value from eval, xqueryEval, or invoke operations.
* Each returned value includes format, datatype, and the actual value.
*/
export interface EvalResult {
/** Format of the value: 'json', 'xml', 'text', or 'binary' */
format: 'json' | 'xml' | 'text' | 'binary';
/** Datatype of the value (e.g., 'node()', 'string', 'boolean', 'integer') */
datatype: string;
/** The actual value (type depends on format and datatype) */
value: any;
}

/**
* Result from a removeAll operation.
*/
Expand Down Expand Up @@ -390,6 +413,114 @@ declare module 'marklogic' {
*/
checkConnection(): ResultProvider<ConnectionCheckResult>;

/**
* Creates one or more JSON documents for a collection.
* The server assigns URI identifiers to the documents.
* This is a simplified convenience method - use documents.write() for more control.
* @since 1.0
* @param collection - The collection name for the documents
* @param content - The JSON content object(s) for the documents
* @returns A result provider that resolves to an array of assigned URIs
*/
createCollection(collection: string, ...content: any[]): ResultProvider<string[]>;

/**
* Probes whether a document exists.
* This is a simplified convenience method - use documents.probe() for more information.
* @since 1.0
* @param uri - The URI of the document to check
* @returns A result provider that resolves to a boolean
*/
probe(uri: string): ResultProvider<boolean>;

/**
* Queries documents in a collection.
* This is a simplified convenience method - use documents.query() for more control.
* @since 1.0
* @param collection - The collection name
* @param query - Optional query built by queryBuilder
* @returns A result provider that resolves to an array of document content
*/
queryCollection(collection: string, query?: any): ResultProvider<DocumentContent[]>;

/**
* Reads one or more documents, returning just the content.
* This is a simplified convenience method - use documents.read() for metadata too.
* @since 1.0
* @param uris - One or more document URIs
* @returns A result provider that resolves to an array of document content
*/
read(...uris: string[]): ResultProvider<DocumentContent[]>;

/**
* Removes one or more documents.
* This is a simplified convenience method - use documents.remove() for more control.
* @since 1.0
* @param uris - One or more document URIs to remove
* @returns A result provider that resolves to an array of removed URIs
*/
remove(...uris: string[]): ResultProvider<string[]>;

/**
* Removes all documents in a collection.
* This is a simplified convenience method - use documents.removeAll() for more options.
* @since 1.0
* @param collection - The collection whose documents should be deleted
* @returns A result provider that resolves to the collection name
*/
removeCollection(collection: string): ResultProvider<string>;

/**
* Writes documents to a collection using a URI-to-content mapping.
* This is a simplified convenience method - use documents.write() for more control.
* @since 1.0
* @param collection - The collection name for the documents
* @param documents - An object mapping URIs to document content
* @returns A result provider that resolves to an array of written URIs
*/
writeCollection(collection: string, documents: Record<string, DocumentContent>): ResultProvider<string[]>;

/**
* Creates a timestamp object for point-in-time operations.
* @since 2.1.1
* @param value - Optional timestamp value as a string
* @returns A Timestamp object
*/
createTimestamp(value?: string): Timestamp;

/**
* Evaluates JavaScript code on the server.
* The user must have permission to evaluate code and execute the actions performed.
* @since 1.0
* @param source - The JavaScript source code to evaluate
* @param variables - Optional object with variable name-value pairs
* @param txid - Optional transaction ID or Transaction object
* @returns A result provider that resolves to an array of EvalResult objects
*/
eval(source: string, variables?: Record<string, any>, txid?: string | object): ResultProvider<EvalResult[]>;

/**
* Evaluates XQuery code on the server.
* The user must have permission to evaluate code and execute the actions performed.
* @since 1.0
* @param source - The XQuery source code to evaluate
* @param variables - Optional object with variable name-value pairs (keys may use Clark notation)
* @param txid - Optional transaction ID or Transaction object
* @returns A result provider that resolves to an array of EvalResult objects
*/
xqueryEval(source: string, variables?: Record<string, any>, txid?: string | object): ResultProvider<EvalResult[]>;

/**
* Invokes a JavaScript or XQuery module on the server.
* The module must have been installed previously (typically with config.extlibs.write()).
* @since 1.0
* @param path - The path of the module in the modules database
* @param variables - Optional object with variable name-value pairs
* @param txid - Optional transaction ID or Transaction object
* @returns A result provider that resolves to an array of EvalResult objects
*/
invoke(path: string, variables?: Record<string, any>, txid?: string | object): ResultProvider<EvalResult[]>;

/**
* Releases the client and destroys the agent.
* Call this method when you're done with the client to free up resources.
Expand Down
1 change: 1 addition & 0 deletions test-app/src/main/ml-modules/root/hello.xqy
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<hello>world</hello>
161 changes: 161 additions & 0 deletions test-typescript/dbclient-convenience-runtime.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/*
* Copyright (c) 2015-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
*/

/// <reference path="../marklogic.d.ts" />

/**
* Runtime validation tests for DatabaseClient convenience methods.
*
* These tests verify that the simplified convenience methods on DatabaseClient
* have correct TypeScript types and work as expected.
*
* Run with: npm run test:compile && npx mocha test-typescript/*.js
*/

const should = require('should');
import type { DatabaseClient } from 'marklogic';

const testConfig = require('../etc/test-config.js');
const marklogic = require('../lib/marklogic.js');

describe('DatabaseClient convenience methods runtime validation', function() {
let client: DatabaseClient;
const testUri = '/test-typescript/convenience-test.json';
const testContent = { message: 'Convenience method test', timestamp: Date.now() };

before(function() {
client = marklogic.createDatabaseClient(testConfig.restWriterConnection);
});

after(function() {
client.release();
});

it('should create collection with createCollection()', async function() {
const uris = await client.createCollection('typescript-convenience-test', testContent).result();

uris.should.be.an.Array();
uris.length.should.equal(1);
uris[0].should.be.a.String();
});

it('should probe document with probe()', async function() {
// Write a test document first
await client.documents.write({
uri: testUri,
content: testContent,
collections: ['typescript-convenience-test']
}).result();

const exists = await client.probe(testUri).result();

exists.should.be.a.Boolean();
exists.should.equal(true);
});

it('should read document with read()', async function() {
const contents = await client.read(testUri).result();

contents.should.be.an.Array();
contents.length.should.equal(1);
contents[0].should.have.property('message');
});

it('should query collection with queryCollection()', async function() {
const results = await client.queryCollection('typescript-convenience-test').result();

results.should.be.an.Array();
results.length.should.be.greaterThan(0);
});

it('should write collection with writeCollection()', async function() {
const uriMap = {
'/test-typescript/conv-1.json': { id: 1, name: 'Test 1' },
'/test-typescript/conv-2.json': { id: 2, name: 'Test 2' }
};

const uris = await client.writeCollection('typescript-convenience-test', uriMap).result();

uris.should.be.an.Array();
uris.length.should.equal(2);
});

it('should remove document with remove()', async function() {
const uris = await client.remove('/test-typescript/conv-1.json', '/test-typescript/conv-2.json').result();

uris.should.be.an.Array();
uris.length.should.equal(2);
});

it('should remove collection with removeCollection()', async function() {
const result = await client.removeCollection('typescript-convenience-test').result();

result.should.be.a.String();
result.should.equal('typescript-convenience-test');
});

it('should create timestamp with createTimestamp()', function() {
const ts1 = client.createTimestamp();
ts1.should.have.property('value');

const ts2 = client.createTimestamp('2025-11-25T12:00:00Z');
ts2.should.have.property('value');
if (ts2.value !== null) {
ts2.value.should.equal('2025-11-25T12:00:00Z');
}
});

it('should evaluate JavaScript with eval()', async function() {
const results = await client.eval('xdmp.toJSON({message: "Hello from eval"})').result();

results.should.be.an.Array();
results.length.should.equal(1);
results[0].should.have.property('format');
results[0].should.have.property('datatype');
results[0].should.have.property('value');
results[0].format.should.equal('json');
results[0].value.should.have.property('message');
results[0].value.message.should.equal('Hello from eval');
});

it('should evaluate JavaScript with variables', async function() {
const results = await client.eval(
'var x; xdmp.toJSON({result: x * 2})',
{ x: 21 }
).result();

results.should.be.an.Array();
results[0].value.result.should.equal(42);
});

it('should evaluate XQuery with xqueryEval()', async function() {
const results = await client.xqueryEval('"Hello from XQuery"').result();

results.should.be.an.Array();
results.length.should.equal(1);
results[0].format.should.equal('text');
results[0].datatype.should.equal('string');
results[0].value.should.equal('Hello from XQuery');
});

it('should evaluate XQuery with variables', async function() {
const results = await client.xqueryEval(
'declare variable $x as xs:integer external; $x * 3',
{ x: 14 }
).result();

results.should.be.an.Array();
results[0].value.should.equal(42);
});

it('should invoke a module with invoke()', async function() {
const results = await client.invoke('/hello.xqy').result();

results.should.be.an.Array();
results.length.should.equal(1);
results[0].should.have.property('format');
results[0].should.have.property('value');
results[0].value.should.be.a.String();
});
});
11 changes: 11 additions & 0 deletions typescript-test-project/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,17 @@ async function run() {

const wipeResult = await client.documents.wipe({uri: temporalUri, temporalCollection: temporalCollection}).result();
console.log('wipe', wipeResult);

// Try out eval / invoke
const evalResult = await client.eval('fn.currentDateTime()').result();
console.log('eval', evalResult);

const xqueryEvalResult = await client.xqueryEval('fn:current-dateTime()').result();
console.log('xqueryEval', xqueryEvalResult);

const invokeResult = await client.invoke('/hello.xqy').result();
console.log('invoke', invokeResult);

} else {
console.error(`❌ Connection failed: ${result.httpStatusCode} - ${result.httpStatusMessage}`);
process.exit(1);
Expand Down
Loading