@seaverse/data-service-sdk 0.5.2 → 0.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.
package/dist/browser.js CHANGED
@@ -1,3 +1,5 @@
1
+ export { addDoc, arrayRemove, arrayUnion, collection, collectionGroup, deleteDoc, deleteField, doc, endAt, endBefore, getDoc, getDocs, getFirestore, increment, limit, limitToLast, onSnapshot, orderBy, query, runTransaction, serverTimestamp, setDoc, startAfter, startAt, updateDoc, where, writeBatch } from 'firebase/firestore';
2
+
1
3
  /**
2
4
  * Create a bound version of a function with a specified `this` context
3
5
  *
@@ -4173,6 +4175,628 @@ class DataServiceClient {
4173
4175
  }
4174
4176
  }
4175
4177
 
4178
+ /**
4179
+ * Firestore Path Helper Functions
4180
+ *
4181
+ * These helper functions generate correct Firestore paths that match
4182
+ * the security rules. Use these instead of manually constructing paths
4183
+ * to avoid permission-denied errors.
4184
+ *
4185
+ * 🚨 IMPORTANT: These paths are designed to work with SeaVerse Firestore Rules
4186
+ *
4187
+ * Permission Levels:
4188
+ * - publicRead: Read-only for all users, write for admins only
4189
+ * - publicData: Read/write for all authenticated users
4190
+ * - userData: Read/write only for the data owner
4191
+ */
4192
+ /**
4193
+ * Generate path for publicRead data (read-only for users, write for admins)
4194
+ *
4195
+ * @param appId - Your application ID
4196
+ * @param collection - Collection name (e.g., 'announcements', 'config')
4197
+ * @returns Firestore path string
4198
+ *
4199
+ * @example
4200
+ * ```typescript
4201
+ * // Read system announcements
4202
+ * const path = getPublicReadPath('my-app', 'announcements');
4203
+ * // Returns: 'appData/my-app/publicRead/announcements'
4204
+ *
4205
+ * const snapshot = await getDocs(collection(db, path));
4206
+ * ```
4207
+ */
4208
+ function getPublicReadPath(appId, collectionName) {
4209
+ validateSegment('appId', appId);
4210
+ validateSegment('collectionName', collectionName);
4211
+ return `appData/${appId}/publicRead/${collectionName}`;
4212
+ }
4213
+ /**
4214
+ * Generate path for publicData (read/write for all authenticated users)
4215
+ *
4216
+ * @param appId - Your application ID
4217
+ * @param collection - Collection name (e.g., 'posts', 'comments')
4218
+ * @returns Firestore path string
4219
+ *
4220
+ * @example
4221
+ * ```typescript
4222
+ * // Write a public post
4223
+ * const path = getPublicDataPath('my-app', 'posts');
4224
+ * // Returns: 'appData/my-app/publicData/posts'
4225
+ *
4226
+ * await addDoc(collection(db, path), {
4227
+ * _appId: appId,
4228
+ * _createdAt: serverTimestamp(),
4229
+ * _createdBy: userId,
4230
+ * title: 'My Post'
4231
+ * });
4232
+ * ```
4233
+ */
4234
+ function getPublicDataPath(appId, collectionName) {
4235
+ validateSegment('appId', appId);
4236
+ validateSegment('collectionName', collectionName);
4237
+ return `appData/${appId}/publicData/${collectionName}`;
4238
+ }
4239
+ /**
4240
+ * Generate path for userData (private, read/write only by owner)
4241
+ *
4242
+ * @param appId - Your application ID
4243
+ * @param userId - User ID who owns this data
4244
+ * @param collection - Collection name (e.g., 'notes', 'settings')
4245
+ * @returns Firestore path string
4246
+ *
4247
+ * @example
4248
+ * ```typescript
4249
+ * // Write private user notes
4250
+ * const path = getUserDataPath('my-app', 'user-123', 'notes');
4251
+ * // Returns: 'appData/my-app/userData/user-123/notes'
4252
+ *
4253
+ * await addDoc(collection(db, path), {
4254
+ * _appId: appId,
4255
+ * _createdAt: serverTimestamp(),
4256
+ * _createdBy: userId,
4257
+ * content: 'Private note'
4258
+ * });
4259
+ * ```
4260
+ */
4261
+ function getUserDataPath(appId, userId, collectionName) {
4262
+ validateSegment('appId', appId);
4263
+ validateSegment('userId', userId);
4264
+ validateSegment('collectionName', collectionName);
4265
+ return `appData/${appId}/userData/${userId}/${collectionName}`;
4266
+ }
4267
+ /**
4268
+ * Generate path for a specific document in publicRead
4269
+ *
4270
+ * @param appId - Your application ID
4271
+ * @param collection - Collection name
4272
+ * @param docId - Document ID
4273
+ * @returns Firestore document path string
4274
+ *
4275
+ * @example
4276
+ * ```typescript
4277
+ * const path = getPublicReadDocPath('my-app', 'announcements', 'announcement-1');
4278
+ * // Returns: 'appData/my-app/publicRead/announcements/announcement-1'
4279
+ *
4280
+ * const docSnap = await getDoc(doc(db, path));
4281
+ * ```
4282
+ */
4283
+ function getPublicReadDocPath(appId, collectionName, docId) {
4284
+ validateSegment('appId', appId);
4285
+ validateSegment('collectionName', collectionName);
4286
+ validateSegment('docId', docId);
4287
+ return `appData/${appId}/publicRead/${collectionName}/${docId}`;
4288
+ }
4289
+ /**
4290
+ * Generate path for a specific document in publicData
4291
+ *
4292
+ * @param appId - Your application ID
4293
+ * @param collection - Collection name
4294
+ * @param docId - Document ID
4295
+ * @returns Firestore document path string
4296
+ *
4297
+ * @example
4298
+ * ```typescript
4299
+ * const path = getPublicDataDocPath('my-app', 'posts', 'post-123');
4300
+ * // Returns: 'appData/my-app/publicData/posts/post-123'
4301
+ *
4302
+ * const docSnap = await getDoc(doc(db, path));
4303
+ * ```
4304
+ */
4305
+ function getPublicDataDocPath(appId, collectionName, docId) {
4306
+ validateSegment('appId', appId);
4307
+ validateSegment('collectionName', collectionName);
4308
+ validateSegment('docId', docId);
4309
+ return `appData/${appId}/publicData/${collectionName}/${docId}`;
4310
+ }
4311
+ /**
4312
+ * Generate path for a specific document in userData
4313
+ *
4314
+ * @param appId - Your application ID
4315
+ * @param userId - User ID who owns this data
4316
+ * @param collection - Collection name
4317
+ * @param docId - Document ID
4318
+ * @returns Firestore document path string
4319
+ *
4320
+ * @example
4321
+ * ```typescript
4322
+ * const path = getUserDataDocPath('my-app', 'user-123', 'notes', 'note-456');
4323
+ * // Returns: 'appData/my-app/userData/user-123/notes/note-456'
4324
+ *
4325
+ * const docSnap = await getDoc(doc(db, path));
4326
+ * ```
4327
+ */
4328
+ function getUserDataDocPath(appId, userId, collectionName, docId) {
4329
+ validateSegment('appId', appId);
4330
+ validateSegment('userId', userId);
4331
+ validateSegment('collectionName', collectionName);
4332
+ validateSegment('docId', docId);
4333
+ return `appData/${appId}/userData/${userId}/${collectionName}/${docId}`;
4334
+ }
4335
+ /**
4336
+ * Validate a path segment to ensure it doesn't contain invalid characters
4337
+ *
4338
+ * @param name - Parameter name for error messages
4339
+ * @param value - The segment value to validate
4340
+ * @throws Error if the segment is invalid
4341
+ */
4342
+ function validateSegment(name, value) {
4343
+ if (!value || typeof value !== 'string') {
4344
+ throw new Error(`${name} must be a non-empty string`);
4345
+ }
4346
+ if (value.includes('/')) {
4347
+ throw new Error(`${name} cannot contain forward slashes (/). Got: "${value}"`);
4348
+ }
4349
+ if (value.trim() !== value) {
4350
+ throw new Error(`${name} cannot start or end with whitespace. Got: "${value}"`);
4351
+ }
4352
+ }
4353
+ /**
4354
+ * Path builder for advanced use cases
4355
+ * Provides a fluent interface for building Firestore paths
4356
+ *
4357
+ * @example
4358
+ * ```typescript
4359
+ * // Build a path step by step
4360
+ * const builder = new PathBuilder('my-app');
4361
+ * const path = builder.publicData('posts').build();
4362
+ * // Returns: 'appData/my-app/publicData/posts'
4363
+ *
4364
+ * // With document ID
4365
+ * const docPath = builder.publicData('posts').doc('post-123').build();
4366
+ * // Returns: 'appData/my-app/publicData/posts/post-123'
4367
+ * ```
4368
+ */
4369
+ class PathBuilder {
4370
+ constructor(appId) {
4371
+ this.appId = appId;
4372
+ this.segments = ['appData'];
4373
+ validateSegment('appId', appId);
4374
+ this.segments.push(appId);
4375
+ }
4376
+ /**
4377
+ * Add publicRead collection to path
4378
+ */
4379
+ publicRead(collectionName) {
4380
+ validateSegment('collectionName', collectionName);
4381
+ this.segments.push('publicRead', collectionName);
4382
+ return this;
4383
+ }
4384
+ /**
4385
+ * Add publicData collection to path
4386
+ */
4387
+ publicData(collectionName) {
4388
+ validateSegment('collectionName', collectionName);
4389
+ this.segments.push('publicData', collectionName);
4390
+ return this;
4391
+ }
4392
+ /**
4393
+ * Add userData collection to path
4394
+ */
4395
+ userData(userId, collectionName) {
4396
+ validateSegment('userId', userId);
4397
+ validateSegment('collectionName', collectionName);
4398
+ this.segments.push('userData', userId, collectionName);
4399
+ return this;
4400
+ }
4401
+ /**
4402
+ * Add document ID to path
4403
+ */
4404
+ doc(docId) {
4405
+ validateSegment('docId', docId);
4406
+ this.segments.push(docId);
4407
+ return this;
4408
+ }
4409
+ /**
4410
+ * Build the final path string
4411
+ */
4412
+ build() {
4413
+ return this.segments.join('/');
4414
+ }
4415
+ }
4416
+ /**
4417
+ * Common path patterns as constants for frequently used paths
4418
+ */
4419
+ const PATH_PATTERNS = {
4420
+ /**
4421
+ * Base pattern for all SeaVerse data
4422
+ */
4423
+ APP_DATA: 'appData',
4424
+ /**
4425
+ * Permission layers
4426
+ */
4427
+ PUBLIC_READ: 'publicRead',
4428
+ PUBLIC_DATA: 'publicData',
4429
+ USER_DATA: 'userData',
4430
+ };
4431
+
4432
+ /**
4433
+ * Firestore Helper - LLM-Friendly Firestore Operations
4434
+ *
4435
+ * This helper class automatically handles required fields (_appId, _createdAt, _createdBy)
4436
+ * so LLM doesn't need to remember them. It provides a simple, high-level API.
4437
+ *
4438
+ * 🎯 LLM-FIRST DESIGN:
4439
+ * - No need to remember required fields
4440
+ * - No need to construct paths manually
4441
+ * - No need to import serverTimestamp
4442
+ * - One method does everything
4443
+ */
4444
+ /**
4445
+ * Firestore operations helper with automatic metadata injection
4446
+ *
4447
+ * This class wraps Firestore operations and automatically adds required fields:
4448
+ * - _appId: Application ID (for data isolation)
4449
+ * - _createdAt: Server timestamp (for creation time)
4450
+ * - _createdBy: User ID (for ownership tracking)
4451
+ *
4452
+ * @example
4453
+ * ```typescript
4454
+ * const helper = new FirestoreHelper(db, appId, userId);
4455
+ *
4456
+ * // ✅ LLM-friendly: Just pass your data, required fields auto-injected
4457
+ * await helper.addToPublicData('posts', {
4458
+ * title: 'My Post',
4459
+ * content: 'Hello world'
4460
+ * });
4461
+ *
4462
+ * // ❌ OLD WAY: LLM needs to remember 3 required fields
4463
+ * await addDoc(collection(db, path), {
4464
+ * _appId: appId,
4465
+ * _createdAt: serverTimestamp(),
4466
+ * _createdBy: userId,
4467
+ * title: 'My Post',
4468
+ * content: 'Hello world'
4469
+ * });
4470
+ * ```
4471
+ */
4472
+ class FirestoreHelper {
4473
+ constructor(db, appId, userId) {
4474
+ this.db = db;
4475
+ this.appId = appId;
4476
+ this.userId = userId;
4477
+ }
4478
+ /**
4479
+ * Add document to publicData (shared, read/write for all users)
4480
+ *
4481
+ * Automatically injects: _appId, _createdAt, _createdBy
4482
+ *
4483
+ * @param collectionName - Collection name (e.g., 'posts', 'comments')
4484
+ * @param data - Your business data
4485
+ * @returns Document reference
4486
+ *
4487
+ * @example
4488
+ * ```typescript
4489
+ * // ✅ LLM-friendly: Simple and clean
4490
+ * const docRef = await helper.addToPublicData('posts', {
4491
+ * title: 'My First Post',
4492
+ * content: 'Hello world'
4493
+ * });
4494
+ * console.log('Created post:', docRef.id);
4495
+ * ```
4496
+ */
4497
+ async addToPublicData(collectionName, data) {
4498
+ const path = getPublicDataPath(this.appId, collectionName);
4499
+ return this.addDocWithMeta(path, data);
4500
+ }
4501
+ /**
4502
+ * Add document to userData (private, only owner can read/write)
4503
+ *
4504
+ * Automatically injects: _appId, _createdAt, _createdBy
4505
+ *
4506
+ * @param collectionName - Collection name (e.g., 'notes', 'settings')
4507
+ * @param data - Your business data
4508
+ * @returns Document reference
4509
+ *
4510
+ * @example
4511
+ * ```typescript
4512
+ * // ✅ LLM-friendly: Private data, auto-isolated
4513
+ * await helper.addToUserData('notes', {
4514
+ * title: 'Private Note',
4515
+ * content: 'Only I can see this'
4516
+ * });
4517
+ * ```
4518
+ */
4519
+ async addToUserData(collectionName, data) {
4520
+ const path = getUserDataPath(this.appId, this.userId, collectionName);
4521
+ return this.addDocWithMeta(path, data);
4522
+ }
4523
+ /**
4524
+ * Get all documents from publicData collection
4525
+ *
4526
+ * @param collectionName - Collection name
4527
+ * @returns QuerySnapshot with documents
4528
+ *
4529
+ * @example
4530
+ * ```typescript
4531
+ * const snapshot = await helper.getPublicData('posts');
4532
+ * snapshot.forEach(doc => {
4533
+ * console.log(doc.id, doc.data());
4534
+ * });
4535
+ * ```
4536
+ */
4537
+ async getPublicData(collectionName) {
4538
+ const path = getPublicDataPath(this.appId, collectionName);
4539
+ return this.getDocs(path);
4540
+ }
4541
+ /**
4542
+ * Get all documents from userData collection (user's private data)
4543
+ *
4544
+ * @param collectionName - Collection name
4545
+ * @returns QuerySnapshot with documents
4546
+ *
4547
+ * @example
4548
+ * ```typescript
4549
+ * const snapshot = await helper.getUserData('notes');
4550
+ * snapshot.forEach(doc => {
4551
+ * console.log('My note:', doc.data());
4552
+ * });
4553
+ * ```
4554
+ */
4555
+ async getUserData(collectionName) {
4556
+ const path = getUserDataPath(this.appId, this.userId, collectionName);
4557
+ return this.getDocs(path);
4558
+ }
4559
+ /**
4560
+ * Get all documents from publicRead collection (read-only for users)
4561
+ *
4562
+ * @param collectionName - Collection name
4563
+ * @returns QuerySnapshot with documents
4564
+ *
4565
+ * @example
4566
+ * ```typescript
4567
+ * const configs = await helper.getPublicRead('config');
4568
+ * ```
4569
+ */
4570
+ async getPublicRead(collectionName) {
4571
+ const path = getPublicReadPath(this.appId, collectionName);
4572
+ return this.getDocs(path);
4573
+ }
4574
+ /**
4575
+ * Get collection reference for publicData
4576
+ * Use this for advanced queries with where(), orderBy(), limit()
4577
+ *
4578
+ * @param collectionName - Collection name
4579
+ * @returns Collection reference
4580
+ *
4581
+ * @example
4582
+ * ```typescript
4583
+ * import { query, where, orderBy } from 'firebase/firestore';
4584
+ *
4585
+ * const postsRef = helper.publicDataCollection('posts');
4586
+ * const q = query(
4587
+ * postsRef,
4588
+ * where('_createdBy', '==', userId),
4589
+ * orderBy('_createdAt', 'desc')
4590
+ * );
4591
+ * const snapshot = await getDocs(q);
4592
+ * ```
4593
+ */
4594
+ publicDataCollection(collectionName) {
4595
+ const path = getPublicDataPath(this.appId, collectionName);
4596
+ return this.getCollection(path);
4597
+ }
4598
+ /**
4599
+ * Get collection reference for userData
4600
+ *
4601
+ * @param collectionName - Collection name
4602
+ * @returns Collection reference
4603
+ */
4604
+ userDataCollection(collectionName) {
4605
+ const path = getUserDataPath(this.appId, this.userId, collectionName);
4606
+ return this.getCollection(path);
4607
+ }
4608
+ /**
4609
+ * Get collection reference for publicRead
4610
+ *
4611
+ * @param collectionName - Collection name
4612
+ * @returns Collection reference
4613
+ */
4614
+ publicReadCollection(collectionName) {
4615
+ const path = getPublicReadPath(this.appId, collectionName);
4616
+ return this.getCollection(path);
4617
+ }
4618
+ /**
4619
+ * Update document with automatic metadata update
4620
+ *
4621
+ * Automatically sets: _updatedAt, _updatedBy
4622
+ *
4623
+ * @param collectionPath - Full collection path
4624
+ * @param docId - Document ID
4625
+ * @param data - Data to update
4626
+ *
4627
+ * @example
4628
+ * ```typescript
4629
+ * await helper.updateDoc(
4630
+ * getPublicDataPath(appId, 'posts'),
4631
+ * 'post-123',
4632
+ * { title: 'Updated Title' }
4633
+ * );
4634
+ * ```
4635
+ */
4636
+ async updateDoc(collectionPath, docId, data) {
4637
+ const { updateDoc, doc, serverTimestamp } = await this.loadFirestore();
4638
+ const docRef = doc(this.db, collectionPath, docId);
4639
+ return updateDoc(docRef, {
4640
+ ...data,
4641
+ _updatedAt: serverTimestamp(),
4642
+ _updatedBy: this.userId
4643
+ });
4644
+ }
4645
+ /**
4646
+ * Delete document
4647
+ *
4648
+ * @param collectionPath - Full collection path
4649
+ * @param docId - Document ID
4650
+ */
4651
+ async deleteDoc(collectionPath, docId) {
4652
+ const { deleteDoc, doc } = await this.loadFirestore();
4653
+ const docRef = doc(this.db, collectionPath, docId);
4654
+ return deleteDoc(docRef);
4655
+ }
4656
+ /**
4657
+ * Get single document by ID
4658
+ *
4659
+ * @param collectionPath - Full collection path
4660
+ * @param docId - Document ID
4661
+ *
4662
+ * @example
4663
+ * ```typescript
4664
+ * const docSnap = await helper.getDoc(
4665
+ * getPublicDataPath(appId, 'posts'),
4666
+ * 'post-123'
4667
+ * );
4668
+ * if (docSnap.exists()) {
4669
+ * console.log(docSnap.data());
4670
+ * }
4671
+ * ```
4672
+ */
4673
+ async getDoc(collectionPath, docId) {
4674
+ const { getDoc, doc } = await this.loadFirestore();
4675
+ const docRef = doc(this.db, collectionPath, docId);
4676
+ return getDoc(docRef);
4677
+ }
4678
+ // ============================================================================
4679
+ // Private Helper Methods
4680
+ // ============================================================================
4681
+ /**
4682
+ * Internal: Add document with metadata injection
4683
+ */
4684
+ async addDocWithMeta(collectionPath, data) {
4685
+ const { addDoc, collection, serverTimestamp } = await this.loadFirestore();
4686
+ const colRef = collection(this.db, collectionPath);
4687
+ return addDoc(colRef, {
4688
+ _appId: this.appId,
4689
+ _createdAt: serverTimestamp(),
4690
+ _createdBy: this.userId,
4691
+ ...data
4692
+ });
4693
+ }
4694
+ /**
4695
+ * Internal: Get all documents from collection
4696
+ */
4697
+ async getDocs(collectionPath) {
4698
+ const { getDocs, collection } = await this.loadFirestore();
4699
+ const colRef = collection(this.db, collectionPath);
4700
+ return getDocs(colRef);
4701
+ }
4702
+ /**
4703
+ * Internal: Get collection reference
4704
+ */
4705
+ getCollection(collectionPath) {
4706
+ // Note: This is sync, so we can't use dynamic import
4707
+ // We assume firebase/firestore is already loaded by initializeWithToken
4708
+ const { collection } = require('firebase/firestore');
4709
+ return collection(this.db, collectionPath);
4710
+ }
4711
+ /**
4712
+ * Internal: Lazy load Firebase Firestore functions
4713
+ */
4714
+ async loadFirestore() {
4715
+ const firestore = await import('firebase/firestore');
4716
+ return {
4717
+ collection: firestore.collection,
4718
+ doc: firestore.doc,
4719
+ addDoc: firestore.addDoc,
4720
+ setDoc: firestore.setDoc,
4721
+ getDoc: firestore.getDoc,
4722
+ getDocs: firestore.getDocs,
4723
+ updateDoc: firestore.updateDoc,
4724
+ deleteDoc: firestore.deleteDoc,
4725
+ serverTimestamp: firestore.serverTimestamp,
4726
+ query: firestore.query,
4727
+ where: firestore.where,
4728
+ orderBy: firestore.orderBy,
4729
+ limit: firestore.limit
4730
+ };
4731
+ }
4732
+ }
4733
+ /**
4734
+ * Standalone helper function: Add document with automatic metadata injection
4735
+ *
4736
+ * Use this if you don't want to create a FirestoreHelper instance.
4737
+ *
4738
+ * @param db - Firestore instance
4739
+ * @param collectionPath - Full collection path
4740
+ * @param appId - Application ID
4741
+ * @param userId - User ID
4742
+ * @param data - Your business data
4743
+ * @returns Document reference
4744
+ *
4745
+ * @example
4746
+ * ```typescript
4747
+ * import { addDocWithMeta, getPublicDataPath } from '@seaverse/data-service-sdk';
4748
+ *
4749
+ * const docRef = await addDocWithMeta(
4750
+ * db,
4751
+ * getPublicDataPath(appId, 'posts'),
4752
+ * appId,
4753
+ * userId,
4754
+ * { title: 'Post', content: 'Hello' }
4755
+ * );
4756
+ * ```
4757
+ */
4758
+ async function addDocWithMeta(db, collectionPath, appId, userId, data) {
4759
+ const { addDoc, collection, serverTimestamp } = await import('firebase/firestore');
4760
+ const colRef = collection(db, collectionPath);
4761
+ return addDoc(colRef, {
4762
+ _appId: appId,
4763
+ _createdAt: serverTimestamp(),
4764
+ _createdBy: userId,
4765
+ ...data
4766
+ });
4767
+ }
4768
+ /**
4769
+ * Standalone helper function: Update document with automatic metadata
4770
+ *
4771
+ * @param db - Firestore instance
4772
+ * @param collectionPath - Full collection path
4773
+ * @param docId - Document ID
4774
+ * @param userId - User ID (for _updatedBy field)
4775
+ * @param data - Data to update
4776
+ *
4777
+ * @example
4778
+ * ```typescript
4779
+ * import { updateDocWithMeta, getPublicDataPath } from '@seaverse/data-service-sdk';
4780
+ *
4781
+ * await updateDocWithMeta(
4782
+ * db,
4783
+ * getPublicDataPath(appId, 'posts'),
4784
+ * 'post-123',
4785
+ * userId,
4786
+ * { title: 'Updated Title' }
4787
+ * );
4788
+ * ```
4789
+ */
4790
+ async function updateDocWithMeta(db, collectionPath, docId, userId, data) {
4791
+ const { updateDoc, doc, serverTimestamp } = await import('firebase/firestore');
4792
+ const docRef = doc(db, collectionPath, docId);
4793
+ return updateDoc(docRef, {
4794
+ ...data,
4795
+ _updatedAt: serverTimestamp(),
4796
+ _updatedBy: userId
4797
+ });
4798
+ }
4799
+
4176
4800
  /**
4177
4801
  * Create Firebase configuration from Firestore token response
4178
4802
  *
@@ -4206,23 +4830,32 @@ function getFirebaseConfig(tokenResponse) {
4206
4830
  * 1. Creates Firebase config from token response
4207
4831
  * 2. Initializes Firebase app
4208
4832
  * 3. Signs in with the custom token
4209
- * 4. Returns authenticated Firebase instances
4833
+ * 4. Creates FirestoreHelper for LLM-friendly operations
4834
+ * 5. Returns authenticated Firebase instances
4210
4835
  *
4211
4836
  * IMPORTANT: This function requires Firebase SDK to be installed separately:
4212
4837
  * npm install firebase
4213
4838
  *
4839
+ * 🎯 LLM RECOMMENDED: Use the `helper` object for simplified operations
4840
+ *
4214
4841
  * @param tokenResponse - The Firestore token response from SDK
4215
- * @returns Object containing initialized Firebase app, auth, and db instances
4842
+ * @returns Object containing initialized Firebase instances and LLM-friendly helper
4216
4843
  *
4217
4844
  * @example
4218
4845
  * ```typescript
4219
4846
  * import { initializeWithToken } from '@seaverse/data-service-sdk';
4220
4847
  *
4221
4848
  * const tokenResponse = await client.generateGuestFirestoreToken({ app_id: 'my-app' });
4222
- * const { app, auth, db, userId, appId } = await initializeWithToken(tokenResponse);
4849
+ * const { db, userId, appId, helper } = await initializeWithToken(tokenResponse);
4223
4850
  *
4224
- * // Ready to use Firestore!
4225
- * const snapshot = await getDocs(collection(db, `appData/${appId}/publicData/posts`));
4851
+ * // LLM-FRIENDLY: Use helper (automatic required fields)
4852
+ * await helper.addToPublicData('posts', {
4853
+ * title: 'My Post',
4854
+ * content: 'Hello'
4855
+ * });
4856
+ *
4857
+ * // ❌ OLD WAY: Manual (need to remember required fields)
4858
+ * // await addDoc(collection(db, path), { _appId, _createdAt, _createdBy, ...data });
4226
4859
  * ```
4227
4860
  */
4228
4861
  async function initializeWithToken(tokenResponse) {
@@ -4254,14 +4887,19 @@ async function initializeWithToken(tokenResponse) {
4254
4887
  // Get Firestore instance with correct database ID
4255
4888
  // IMPORTANT: Must specify database_id, not just use default!
4256
4889
  const db = getFirestore(app, tokenResponse.database_id);
4890
+ const userId = tokenResponse.user_id;
4891
+ const appId = tokenResponse.app_id || '';
4892
+ // Create LLM-friendly helper
4893
+ const helper = new FirestoreHelper(db, appId, userId);
4257
4894
  return {
4258
4895
  app,
4259
4896
  auth,
4260
4897
  db,
4261
- userId: tokenResponse.user_id,
4262
- appId: tokenResponse.app_id || '',
4898
+ userId,
4899
+ appId,
4900
+ helper,
4263
4901
  };
4264
4902
  }
4265
4903
 
4266
- export { DEFAULT_BASE_URL, DEFAULT_TIMEOUT, DataServiceClient, ENDPOINTS, getFirebaseConfig, initializeWithToken };
4904
+ export { DEFAULT_BASE_URL, DEFAULT_TIMEOUT, DataServiceClient, ENDPOINTS, FirestoreHelper, PATH_PATTERNS, PathBuilder, addDocWithMeta, getFirebaseConfig, getPublicDataDocPath, getPublicDataPath, getPublicReadDocPath, getPublicReadPath, getUserDataDocPath, getUserDataPath, initializeWithToken, updateDocWithMeta };
4267
4905
  //# sourceMappingURL=browser.js.map